WebSocket (Authenticated)

Topology Updates

GET /api/v1/ws/topology

Upgrades to WebSocket. The osprey_access cookie is validated during the upgrade. Real-time topology diffs are pushed to connected clients when devices or links change. Connection limits: 100 total concurrent connections, 10 per user. Excess connections are rejected with HTTP 503.

Protocol: Binary or text frames containing JSON topology diff messages.

SSH/Telnet Terminal Proxy

GET /api/v1/ws/ssh?host={router_id}

Upgrades to WebSocket and proxies an SSH or telnet session to the specified device.

Roles: admin or engineer. Operators are refused with 403; interactive device access is outside the read-only role boundary.

Query parameters:

Authentication flow:

  1. Client sends a JSON text message with the username only (the password is typed interactively, not sent here):
    {"type": "auth", "username": "admin"}
    
  2. Server dials SSH (port 22) and relays the device's banner and password prompt to the terminal as binary frames; the user types the password, which is read from the client's binary input frames (SSH keyboard-interactive, password-callback fallback). On a wrong password the device re-prompts (bounded retries). On a connection-level failure, if the ssh.allow_telnet_fallback system setting is true, falls back to telnet (port 23) — disabled by default (cleartext credentials).
  3. Server sends connection status and device output as binary frames.
  4. After login, binary frames carry terminal I/O in both directions.
  5. Client sends resize events as JSON text messages:
    {"type": "resize", "cols": 80, "rows": 24}
    

SSRF Protection:

Error responses:

WebSocket close codes:

Host key verification (TOFU): The proxy pins each device's SSH host key on first connect (ssh_known_host table). On a later mismatch the connection is refused (close 1008, reason host key mismatch). Before closing, the server emits one text control frame (terminal output is sent as binary frames, so a text frame is an unambiguous control message) describing the change:

{"type":"hostkey_mismatch","host":"198.51.100.13","port":22,
 "stored_fingerprint":"SHA256:…","stored_key_type":"ssh-ed25519",
 "offered_fingerprint":"SHA256:…","offered_key_type":"ssh-ed25519"}

An admin can then clear the pin (see Clear SSH Host Key) and reconnect, which re-runs TOFU and pins the new key.

Clear SSH Host Key (TOFU)

DELETE /api/v1/admin/ssh/known-hosts?host={host}&port={port}&offered_fp={fingerprint}

Admin only. Removes the pinned TOFU host key for host:port so a legitimately re-keyed or replaced device can be reconnected to — the next SSH connection re-runs Trust-On-First-Use and pins the new key. Recorded in the audit log (action=clear, entity_type=ssh_known_host) with the cleared fingerprint and the client-reported offered fingerprint.

Query parameters:

Response: 200 OK

{"cleared": true, "host": "198.51.100.13", "port": 22, "fingerprint": "SHA256:…", "key_type": "ssh-ed25519"}

Error responses: