Architecture
How Traversio is layered, which boundaries are stable, and where the implementation still needs refinement.
What This Page Is For
This page is about how Traversio is built.
If you are trying to use the public API, start with Mental Model first. That page already covers:
- the public object graph
- the difference between connection, channel, session, and SFTP client
- which top-level API to reach for first
This page starts one layer lower:
- how the package is split conceptually
- where protocol state lives
- which implementation constraints shape the package
- which implementation areas still need simplification
Layer Diagram
Your code
|
v
Public API wrappers
SSHClient / SSHConnection / SSHSession / SFTPClient / forwarding wrappers
|
v
Connection and forwarding orchestration
|
v
SSHTransportProtocolClient actor
- version exchange
- key exchange
- encrypted transport
- authentication sequencing
- channel lifecycle and flow control
|
+--> Authentication helpers
+--> Connection message coders
+--> SFTP protocol helpers
|
v
Wire and algorithm layers
- SSH binary framing
- typed message codecs
- key derivation
- cipher/MAC handling
|
v
Transport abstraction
SSHByteStreamTransport
|
v
Selected TCP backend
- Apple 26+ modern Network.framework backend
- compatibility backend on older supported releasesThese are logical layers first. The public library still ships mainly as one Swift target while those boundaries settle, with small helper targets where platform or validation work requires them.
Working Source Layout
The source tree currently reflects those boundaries under Sources/Traversio/:
Transport/Wire/Algorithms/Authentication/Connection/Forwarding/SFTP/TransportProtocol/Client/
Main Constraints
- Apple platforms first
- the public API now lives at the package floor declared in
Package.swift - Apple 26+ systems still prefer the newer transport backend automatically
- Swift Concurrency is the default model
- the SSH core must stay testable without a live network
- transport details stay isolated
- the current release line keeps exec, shell, SFTP, SCP, forwarding, proxy routing, and diagnostics in the public library surface
Transport Layer
Purpose
SSH is defined over a reliable byte stream. Traversio therefore keeps a byte-stream abstraction so the protocol layers stay portable while the first concrete backend is Apple-specific.
Direction
Traversio keeps one transport abstraction and selects the concrete backend at runtime. Apple 26+ systems use the newer Network framework API family by default, and older supported releases use the compatibility backend. The transport layer provides:
- async send
- async receive
- orderly connection lifetime around a single byte stream
This keeps the SSH core separate from Apple transport types while still using the preferred modern APIs first.
Wire Layer
The wire layer owns deterministic byte-level SSH behavior:
- primitive encoding and decoding
- packet framing
- identification parsing
- binary readers and writers
- typed transport and connection messages
Design rules:
- the wire layer should be testable without a live connection
- the wire layer stays focused on byte-level SSH behavior
Algorithms Layer
Implemented interoperability path:
curve25519-sha256,[email protected],ecdh-sha2-nistp256,ecdh-sha2-nistp384, andecdh-sha2-nistp521key exchange- Ed25519, RSA SHA-2, and ECDSA host-key verification
- password auth with password-change callbacks
- keyboard-interactive auth
- Ed25519, RSA/SHA-2, and ECDSA public-key auth
aes128-ctr/aes256-ctrpacket protection with HMAC-SHA-2 and OpenSSH UMAC MACs, including classic and ETM forms- OpenSSH
[email protected]/[email protected] - OpenSSH
[email protected] - OpenSSH
[email protected]strict-kex marker handling on the initial exchange
The project prefers Apple system frameworks where practical, and the current algorithm coverage focuses on the documented client profile.
Transport Protocol Engine
This layer owns the SSH transport state machine:
- version exchange
SSH_MSG_KEXINIT- key exchange
- transition into encrypted packet flow
- service requests
- inbound and outbound sequencing
- selected post-auth transport messages such as
SSH_MSG_EXT_INFOandSSH_MSG_DEBUG
Mutable protocol state is actor-owned so sequencing stays explicit.
Authentication Layer
Public auth methods:
- password
- keyboard-interactive callbacks
- Ed25519 and ECDSA public key from raw private-key bytes
- RSA public key from PKCS#1 DER private-key bytes
- Ed25519, RSA, and ECDSA public key from OpenSSH private keys, OpenSSL-style PEM containers, metadata inspection, OpenSSH bcrypt + AES encrypted-key loading, and encrypted traditional RSA PEM loading
- OpenSSH key-pair generation for Ed25519, ECDSA P-256/P-384/P-521, and RSA, including matching authorized-key output
- SSH agent-backed public-key signing through
SSHAgentClient
Trust posture:
- the caller must choose a host-key policy
- exact pinning plus
known_hostsimport for exact, wildcard, negated, hashed, CIDR,@revoked, and@cert-authorityentries are available - trust-on-first-use, changed-key helper, and async trust callbacks are public
- trust-any is explicit and not implicit fallback
Follow-up work:
- broader host-key trust behavior
- keychain-backed credential loading and product-owned credential-store policy
Connection And Channels
Above the transport actor, the connection layer is responsible for:
- connect lifecycle orchestration
- authentication sequencing
- channel allocation
- session-channel exec and shell flows
- transcript collection
- channel window tracking for send and receive paths
The public connection wrapper exists in two forms:
- explicit long-lived ownership through
SSHClient.connect(configuration:) - closure-scoped convenience through
SSHClient.withConnection(configuration:_:)
The current direction is:
- one SSH connection supports multiple concurrent shell, exec, SFTP, and forwarding channels
- reconnect remains an upper-layer concern
- shell, streamed exec sessions, and raw forwarding channels expose event-based stream surfaces, while richer shell control and broader forwarding interoperability are not currently covered
SFTP
The current SFTP design is actor-based and routes replies by SFTP request ID inside one subsystem session:
- requests go through one actor-owned channel/session pair
- replies are matched back to the correct waiter by request ID
- path operations and handle operations on one
SFTPClientcan overlap safely - high-level bulk helpers cover bounded whole-file reads/writes, local file
URLtransfers, recursive directory transfers, continuation callbacks, and resumable whole-file transfers - local file-transfer helpers pipeline bounded SFTP read/write requests by default
That keeps protocol coordination localized without forcing callers to open a second SFTP subsystem just to overlap simple metadata or handle work.
Traversio already supports overlapping in-session requests plus a practical transfer-helper layer.
The next SFTP work should come from real deployment evidence such as:
- automatic transfer-window tuning
- segmented multi-session transfer for very large files on per-connection-capped routes
- metadata-preserving recursive-copy policy
- throughput isolation between independent long-running transfers
- broader extension behavior across more server families
Forwarding
Forwarding is part of the implemented surface. Traversio includes raw direct-tcpip, [email protected], forwarded-tcpip, and [email protected] public wrappers, local-forwarding and dynamic-SOCKS helpers built on shared listener factories, raw remote TCP and streamlocal listeners, and a remote-forwarding helper that bridges remote TCP listeners back to one local TCP endpoint. The main remaining work is:
- broader lifecycle guidance for unusual peer shutdown and cancellation timing
- broader interoperability evidence outside the currently documented server and proxy families
- continued simplification of post-auth channel coordination
The release scope keeps two points explicit:
- local, remote, and dynamic forwarding are all required and already exist as public APIs
- forwarding failures should default to the forwarding scope unless the caller chooses a stronger policy
Main Structural Risk Today
The largest structural risk is the size and responsibility load of SSHTransportProtocolClient.
That actor carries too much of:
- post-auth sequencing
- channel routing
- forwarding coordination
- SFTP/session plumbing
It remains workable, and future protocol work should keep reducing that concentration of responsibilities.
Read Next
- Current Status for the current validation and readiness picture
- Release Scope for the current release line and unsupported areas