Traversio

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 releases

These 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, and ecdh-sha2-nistp521 key 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-ctr packet 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_INFO and SSH_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_hosts import for exact, wildcard, negated, hashed, CIDR, @revoked, and @cert-authority entries 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 SFTPClient can overlap safely
  • high-level bulk helpers cover bounded whole-file reads/writes, local file URL transfers, 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.

On this page