Update four existing specs (overview, server, napi-and-pubsub, call-protocol) to reflect Phase 0 decisions: three-layer model, IdentityProvider, ForwardingPolicy, OperationEnv, static/dynamic config split. Review all 9 Phase 0a ADRs (026-034) for consistency. Fix 4 critical issues from architecture review: missing OQ-SVC-05 in open-questions.md, deprecated hub terminology, undefined AuthService and noq terms. Replace inline OQ text with cross-references per format rules. Add ConfigServiceImpl definition to configuration.md. Port absolute workspace paths to project-relative links by copying referenced docs (feasibility, certbot, fail2ban, event_source_types) into docs/research/.
2.2 KiB
ADR-004: SSH Runs Over Transport, Not Alongside
Status
Accepted
Context
There are two ways to structure the relationship between SSH and the transport layer:
-
SSH over transport: The transport produces one duplex stream. The entire SSH session (handshake, key exchange, channel multiplexing) runs over that single stream via
connect_stream()/run_stream(). SSH has no direct network access. -
Transport alongside SSH: SSH manages its own TCP connections via
connect()/run(). The transport layer is an additional feature that wraps outgoing connections. SSH knows about the network.
Decision
SSH runs over the transport (Option 1). The SSH layer never opens its own sockets or knows what transport it's on.
This is directly enabled by russh's connect_stream() and run_stream() APIs, which accept any AsyncRead+AsyncWrite+Unpin+Send. SSH's entire interaction with the network goes through the single stream produced by the transport.
Consequences
- Positive: Adding a new transport requires implementing the
Transporttrait, not modifying SSH code. - Positive: Testing is straightforward — mock transports produce in-memory streams.
- Positive: Security audit is clean — the SSH implementation has no network-facing code.
- Positive: The transport can be layered. Iroh connecting through a SOCKS5 proxy (which itself tunnels through alknet) is just a transport that calls out to a SOCKS5 library before establishing the QUIC connection.
- Negative: SSH keepalive and reconnection must be handled at the transport level. If the transport stream dies, the SSH session dies. Reconnection means establishing a new transport + new SSH session. There's no "SSH reconnects over the same transport" — you get a new session.
- Negative: Multiple SSH sessions over the same iroh connection require the iroh
Endpoint(not stream) to be shared between sessions. The transport trait produces one stream perconnect()call. The irohEndpointmust be created externally and shared. (TheIrohTransportstruct holds anArc<Endpoint>.)