- OQ-10 is not resolved: the mapping-contract derivation details (name
table, core-dumped disposition, −1 disposition) are working
dispositions on mapping.md's exit rows until formalized there / as an
ADR-006 amendment. The 1a40bfb commit prematurely closed them and left
ADR-006's reference (OQ-10 open) stale.
- Premise correction integrated into the seed, not argued after the
fact; the portable-pty 0.9.0 source now cited as verified
(From<std::process::ExitStatus> maps signal-terminated → code 1 +
locale-dependent strsignal string; the number is unrecoverable).
- mapping.md: exit_signal row and the Open Questions entry updated to
open OQ-10 with the working-draft framing.
Verification: cargo test (2/2 targets ok), cargo clippy --all-targets -- -D warnings, cargo fmt --check (docs-only change)
10 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-10-27 |
Mapping — server direction: SSH channels × family seams (the spine)
The door framing
The server direction is a door for vanilla clients: vanilla in,
family out. Vanilla ssh/git connect as they would to any sshd;
every accepted channel/request maps to exactly one family protocol
seam and exactly one cargo feature (ADR-003). Rows below are
POC-3-validated against published sibling crates with real vanilla
tooling (OpenSSH 9.6, git 2.43, curl 8.5 — poc-3-mapping-bakeoff.md).
The seams
| Family crate | Serve-over-a-stream entry | Signature shape |
|---|---|---|
| alktty | drive_session[_pre_negotiated] |
(impl AsyncWrite, impl AsyncRead, backends, identity) |
| alkgit | serve_duplex_resolved |
(repo_id, identity, service, duplex, limits, backend traits) |
| alktunnels | — no serve entry (absence note resolved: route 1 never needed one, ADR-006) | consumer/open-op registrations |
russh channels convert to duplex via into_stream/split — the
bridge primitives every mapping uses (POC-1 §7).
Channel-type rows
| SSH channel type | Vanilla use | Feature | Family seam | Contract notes |
|---|---|---|---|---|
session → pty_request + shell_request |
ssh host interactive shell |
session |
alktty: NegotiateRequest constructed locally → drive_session_pre_negotiated |
pty_request args → TerminalParamsWire (term/cols/rows/pixels; SSH modes → the reserved modes JSON); shell convention per OQ-08; the data plane is a translation bridge, not a byte pump — alktty is chunk-framed both directions (stdin/stdout/stderr/CTRL chunks): SSH data→stdin chunks; stdout chunks→SSH data; stderr chunks→extended_data(1); exit chunk→exit-status/exit-signal + close (POC-3 §D.1) |
session → exec_request (non-git) |
ssh host "cmd" |
session |
alktty pipe mode (terminal: None) — separate stderr |
stderr rides extended_data(1) — only valid without PTY; PTY mode merges (kernel PTY, POC-3 §D.1 proven) |
session → exec_request git |
git clone/push ssh:// |
git |
alkgit: parse command → Service + repo → serve_duplex_resolved(repo_id, identity, service, duplex, …) |
exec-only (ADR-006 decision 2): the repo rides inside the exec command string (git-upload-pack '/repo'); vanilla git never speaks subsystem for git; process-mode dialect (server-speaks-first advertisement); exit-status proven both directions |
session → subsystem_request |
sftp clients; manual ssh -s |
sftp (placeholder) / git rows |
— | sftp name: declines until alkfs lands (OQ-13) — the feature exists, the route doesn't. git service names: decline with reason — a subsystem request carries only a name, never a repo id (POC-3 §A.3); never half-serves |
direct-tcpip |
ssh -L; all ssh -D traffic |
tunnel |
alktunnels route 1: adapter-side bridge — TunnelSession::open_direct on a held ChannelClient, pump take_halves() vs the channel |
target = SubstrateAddr::HostPort/Path; Substrate = Tcp/Unix; accept only after the fabric open succeeds — failure maps to an explicit ConnectFailed reject (POC-3 §C.2); vanilla -D's dynamic targets dispatch here (POC-3 correction 2) |
forwarded-tcpip |
ssh -R data channels |
tunnel |
the register_tunnel_forwarded_openable's ForwardedHandler shape — hand the channel duplex to the handler |
listen flow = the global-request rows below; the data leg is untested in POC-3 beyond refusal (see the POC-3 note below) |
direct-streamlocal |
unix-socket forward | tunnel |
SubstrateAddr::Path treatment |
unix-only; decline shape per OQ-11 |
x11 |
X forwarding | — (never) | — | always declines; not feature-gated (ADR-003 §5) |
auth-agent |
agent forwarding | — (never) | — | always declines; the anti-pattern of the alkvault custody posture (ADR-003 §5) |
session → SOCKS5-in-channel |
(no vanilla use) | — (retired, ADR-003 §1a) | — | vanilla -D is client-side SOCKS + direct-tcpip; the family local door is alksocks composed at the assembly — see client.md |
Session-channel dispatch (ADR-006, as amended)
Parse-only. A session channel that receives a request takes the
parse path: exec_request parses first (a git-parseable command
routes to git; anything else to session's pipe path), and
pty_request/shell_request route to session. Request-less session
channels decline as unfeatured. Parsing happens before dispatch and
before any resource allocation — a leaked channel alloc is a
capability leak, so decline first, alloc never (POC-3 stage A's
decline matrix is the contract: unknown repo → collapsed generic
denial before any protocol byte — no existence oracle; non-git
command → parse-then-fail).
Global-request rows (the -R listen flow)
Handler::tcpip_forward(address, &mut port) -> bool +
Handler::streamlocal_forward — gate on tunnel (decline both the
listen request and the data channels when absent). The bound-port
out-param (port = 0 → server picks) is a spec detail in
client.md's lifecycle primitives; disposition follows tunnel.
POC-3 note: only the refusal shape was exercised (egress-only build);
the served listen flow is implementation-phase contract-test territory.
Request-level rows on a session channel
| SSH request | Family mapping |
|---|---|
pty_request |
NegotiateRequest.tty = TerminalParamsWire{…} (POC-3 §D.1: live stty size honored) |
shell_request |
NegotiateRequest.cmd per OQ-08 (assembly convention, never wire-supplied) |
exec_request |
parse → git dispatch (git) or alktty pipe cmd (session); unknown → channel_failure, no resource alloc |
subsystem_request |
decline (per the rows table; honest, with reason) |
env_request |
decline by default (OQ-09 deferred); wire env is not family config |
window_change_request |
resize control (POC-3 §D.1: live resize proven) |
signal |
signal control (alktty names verbatim); PTY mode reaches the process group, pipe mode is pid-only (backend semantics, recorded in OQ-10) |
data / extended_data(1) |
the translation bridge (rows table) |
exit_status |
from the alktty exit chunk — sent before close, from the spawned task via Session::handle() (POC-3 §D: proven both legs). −1 (alktty's could-not-determine sentinel — alktty ADR-004 §4) → exit-status(255), never a signal claim |
exit_signal |
signal-terminated exits on the pipe path: the exit chunk's negative code is the signal — −N → name via a Unix signal-number→name table (OQ-10 open — direction fixed, derivation details pending; POC-3 §D.2 observed −15 → exit-signal("TERM") live). core_dumped never asserted (working disposition, v1 — alktty discards ExitStatus::core_dumped()). PTY path: signal-terminated children arrive as a plain code 1 (portable-pty's exit surface; the signal number is unrecoverable there today) → exit-status(1) — faithful exit-signal on the PTY row is an alktty-backend/portable-pty ask, upstream |
The structural constraint: spawn-and-return
russh (this pin: ADR-002) runs server handler callbacks inline
inside the session's packet loop — a blocking handler suspends all
packet routing for the connection (channel data is routed by that
same loop; an inline serve deadlocks against the peer's immediate
follow-up bytes — POC-3 proved by construction with receive-pack).
Therefore: every serving path is spawned; handlers return fast;
exit/status reporting from spawned tasks rides the cloned
Session::handle(). This is a load-bearing correctness constraint of
the whole spine, not an implementation choice.
Data-plane / limits
- russh knobs (
window_size,maximum_packet_size,channel_buffer_size,Limits,inactivity_timeout,keepalive_*) + in-crate max-concurrent-channels cap + per-length exec-parse limits — per the bounded-resources policy inoverview.md. - alkcall bridge leg (when the family half is remote): channels-layer
reassembly cap 1 MiB/channel; ADR-034 grants
alk/sshstream_types[0, 1]because SSH multiplexes internally. - Honest advertisement:
Config::methodsmirrors implemented auth (ADR-004); an unfeatured channel type declines at open, never half-opens; vanilla clients observe clean diagnostics (POC-1 §5: channel-scoped decline, auth intact, no hangs).
Decline matrix (per feature build, contract-tested)
| Build | Serves | Declines |
|---|---|---|
| none (default) | — | everything |
git |
git-parseable exec | pty/shell, plain exec, all other channel types, all session requests |
session |
pty/shell/pipe-exec | git-parseable exec (the git parse is its gate), other channel types |
tunnel |
direct/forwarded-tcpip, streamlocal (unix), -R listens |
session channels (auto-reject), all others |
any + sftp slot |
— | sftp subsystems decline-with-reason until alkfs |
Every row is tested in every build it can appear in — the decline
paths are contract territory (AGENTS.md conventions 5–6), including
under --all-features.
Open Questions
- OQ-08 (shell convention — status: open): blocks this doc's
shell_request/NegotiateRequest.cmdrow detail. - OQ-10 (exit-signal derivation — open: the negative code is the signal encoding on the pipe path [premise corrected]; the
−1→ 255 and core-dumped-never-asserted dispositions on theexit_status/exit_signalrows here are the working draft, formalized at OQ-10's close; the PTY path's signal-identity loss is recorded upstream, alktty/portable-pty-side). - OQ-11 (streamlocal platform decline shape — status: open).
OQ-02(dispatch composition — resolved by ADR-006, as amended).
References
- ADR-003 (features), ADR-006 (route 1, exec-only git, dispatch), ADR-001 (the ALPN/edge context), ADR-004 (auth)
docs/research/mapping-table.md(the validated research ancestor + annotations),poc-3-mapping-bakeoff.md(stages A–D),poc-1-auth-bridge.md§5/§7 (decline observability, plumbing)- Family ADR-003; alkcall ADR-034 (
alk/sshstream_types) client.md(the same door, client-side)