Files
alkssh/docs/architecture/open-questions.md
T
glm-5.3-flash 1a40bfb498 docs(architecture): OQ-10 resolved as a decided mapping contract; tracker statements made accurate
- OQ-10 resolved: alkssh derives SSH exit-signal from the alktty exit
  chunk's negative code (−N = signal N, pipe path) server-side; −1
  (alktty's could-not-determine sentinel) → exit-status(255), never a
  signal claim; core_dumped never asserted in v1; no alktty wire change
- new verified backend finding recorded: portable-pty 0.9 reduces
  PTY-mode signal termination to a plain exit code 1 (signal number
  unrecoverable through its trait surface) — faithful exit-signal on
  the PTY row is an alktty/portable-pty upstream ask, not an alkssh one
- research docs (poc-3 §D.2, pocs.md, mapping-table rows) restated
  accurately instead of carrying inline correction scaffolding

Verification: cargo doc --no-deps earlier this session (docs-only change)
2026-10-27 14:19:02 +00:00

13 KiB
Raw Blame History

status, last_updated
status last_updated
draft 2026-10-27

Open questions

Centralized tracker for alkssh. Seeded from the phase 0 POC findings (docs/research/poc-{1,2,3}-*.md), the research docs' OQ seeds, and the reverse-runner extraction. Numbers are repo-local (family and alkcall trackers have their own OQ spaces).

Theme A — wire & family topology

OQ-01: Does an alk/ssh relay/browser-leg consumer emerge?

  • Origin: mapping-table.md §topology, vision.md scope Q1
  • Status: deferred(scope)
  • Door type: two-way (the pin is one-way; consumer existence is not)
  • Priority: medium
  • Impacts: none for the phase 1 scope — ADR-001 pins the contract so relay/browser legs don't foreclose it; the phase 1 docs serve edge termination only. A future relay leg would exercise docs/architecture/client.md's accept surface in a new role.
  • Blocked on: a concrete consumer — either the browser-WASM route (alkcall ADR-009 sketch) materializing in an assembly, or a hub deployment needing SSH-in-relay. No such use case exists yet.
  • Resolution: Not yet decidable — no consumer use case exists. ADR-001 decides everything decidable now: the ALPN pin's raw-stream contract, and its transport-blindness.
  • Cross-references: ADR-001, OQ-13

Theme B — session-channel dispatch & mapping details

OQ-02: What is the session-channel dispatch shape (SOCKS-detect vs parse, and who bounds the window)? — resolved

  • Origin: poc-3-mapping-bakeoff.md §Stage B.3, mapping-table.md §notes
  • Status: resolved. Decided by ADR-006 point 3, then simplified by the ADR-003 §1a amendment: the dynamic-forward feature retired (no consumer is ever the family-native SOCKS-in-channel client; vanilla -D is client-side SOCKS + plain direct-tcpip; the family -D door is alksocks composed locally at the assembly), so the detect lost its owner. Session dispatch is parse-only; request-less session channels decline. POC-3 stage B survives as the feasibility record for re-adding a remote SOCKS door via explicit ADR.
  • Cross-references: ADR-006, ADR-003 §1a

OQ-03: Username disposition — resolved

  • Origin: poc-1-auth-bridge.md §2, auth-bridge.md §The mapping
  • Status: resolved
  • Resolution: usernames are never the identity model and carry no routing semantics — the fingerprint is the sole resolution path (POC-1: ssh anything@host works with the same identity). Whether a username string appears in logs is deployment policy, outside the crate. Rides as the recorded contract in the auth.md spec / ADR-004.

OQ-04: OpenSSH certificate auth — is there ever a CA-trust use case?

  • Origin: mapping-table.md (auth rows), poc-1-auth-bridge.md OQ seed 4
  • Status: deferred(scope)
  • Door type: two-way
  • Priority: low
  • Impacts: none — auth_openssh_certificate is declined (the ADR-004 disposition), and POC-1 established the decline exists. A revisit only matters if alkforge ever wants CA-issued certificates.
  • Blocked on: a concrete OpenSSH-CA use case arriving in an embedding assembly.
  • Resolution: Not yet decidable — decline initial; no CA-trust story in the family identity model today.

Theme C — auth, identity & key custody

OQ-05: What is the client-side host-key pin store? — resolved (policy-seam shape)

  • Origin: auth-bridge.md §Host keys, reverse-runner.md §5

  • Status: resolved — the seam is decided; where learned pins persist is assembly detail (the crate takes a policy, not a pin store).

  • Resolution: two basic client cases, one seam:

    1. Known beforehand — trust anchors exist. Family-built runners mint their host key at assembly time, so the expected fingerprint is known before first contact: assembly-supplied pins, hard decline on mismatch (no soft failure).
    2. Unknown beforehand — vanilla sshd on container fleets the assembly didn't provision; no anchor exists until first contact. The crate surfaces the presented host key (host, port, fingerprint) to an assembly-provided HostKeyPolicy that decides: prompt-and-record, provider-metadata match, learn-and-record, or accept-and-continue (the ephemeral-fleet shape — see below). The OpenSSH "add to known_hosts?" moment becomes an assembly policy rather than crate behavior.

    Crate rules that make this family-shaped: the crate's default policy is fail-closed (reject unknown, hard-decline mismatch — russh's own reject-all default, kept); the crate never decides trust itself and never hard-codes a trust model — accept-all is not a built-in crate option, but a permissive case-2 policy is the assembly's to choose where its fleet model calls for it (alkminer's accept-all for rental fleets is that shape, made deliberately: fresh container per rent → fresh host key, so no pin can exist beforehand and the security anchor is hub-held per-instance dial keys plus what rides the tunnels, not host pins). The contract lands in the auth.md spec (host-key policy section) and client.md's dial surface.

  • Cross-references: OQ-07, reverse-runner.md, poc-2-vault-keys.md §5 (host keys ride the vault model)

OQ-06: One vault, many roles? — resolved

  • Origin: poc-2-vault-keys.md OQ seed 3
  • Status: resolved
  • Resolution: not an alkssh constraint. The crate takes vault handles the assembly injects; whether the assembly runs separate vault instances per role (client keys vs host keys) is assembly policy. ADR-005 records the boundary: vault handle in, key material out, version semantics owned by the vault.

OQ-07: Is the alkvault decrypt plain-String zeroization gap closed at the vault or at the adapter? — resolved (at this level)

  • Origin: poc-2-vault-keys.md §2 + OQ seed 1
  • Status: resolved — the alkssh-level answer is the adapter-side wrap; the vault-side improvement belongs upstream, not here.
  • Resolution: ADR-005 decision 3 is the contract on this path: the adapter owns a Zeroizing<String> wrap at the seam (POC-2-proven; bounds the plaintext from adapter-takeover onward; ssh-key zeroizes the decoded key bytes on drop). The remaining unfenced pre-wrap window is alkvault's API shape to close — the ask is filed in alkvault's own tracker (downstream/family-space concern; alkforge and every secrets-bearing crate benefits, not just alkssh). alkssh does not carry it, does not gate on it, and ADR-005 records only the adapter obligation. This tracker entry stays closed; when alkvault ships a Zeroizing-out API, ADR-005's reference here updates as a one-line doc touch, nothing else.
  • Cross-references: ADR-005 §3, OQ-05

Theme D — session-request mapping details

OQ-08: Shell command convention for shell_request

  • Origin: mapping-table.md (shell row), poc-3-mapping-bakeoff.md OQ seed 3
  • Status: open
  • Priority: medium
  • Impacts: blocks the mapping.md session-row detail (what alktty drives when a vanilla ssh host lands). The POC's stand-in (bash/sh -c) was explicit assembly policy, not wire-derived.
  • Resolution: pending. Direction known (assembly policy, nothing wire-supplied); the decision is the concrete convention surface: a config field on the assembly vs a crate-level default that the assembly may override. Decide when writing mapping.md.

OQ-09: env_request policy — is an allowlist ever wanted?

  • Origin: mapping-table.md (env_request row)
  • Status: deferred(scope)
  • Door type: two-way
  • Priority: low
  • Impacts: none — decline-by-default is the fixed posture (wire env is not family config). An allowlist (LANG, …) only matters if a consuming assembly has a concrete need.
  • Blocked on: a concrete consumer use case requiring wire-supplied environment variables.
  • Resolution: Not yet decidable — no use case; decline stands.

OQ-10: SSH exit-signal fidelity over the alktty exit-code convention — resolved

  • Origin: mapping-table.md (exit_signal row), poc-3-mapping-bakeoff.md §D.2

  • Status: resolved — the alkssh mapping contract is decided (below, and in mapping.md's exit rows). The PTY-path fidelity limit recorded in point 4 is an alktty-backend/portable-pty matter and belongs in their trackers; alkssh adapts automatically with no contract change if the convention ever holds on that path.

  • Resolution: alkssh adds nothing to the alktty wire — the exit chunk is alktty's contract ({"type":"exit","code":N} on ctrl-out, exit-chunk-is-last — alktty ADR-004, ported from alknet ADR-055) — and derives all SSH fidelity from the code the chunk already carries:

    1. The negative code is the signal encoding on the pipe path. alktty's pipe backend maps Unix ExitStatus signal termination to −N (pipe.rs::exit_code_from, ExitStatusExt::signal() → -sig; the alknet ADR-055 convention). POC-3 stage D observed it live on the wire: signal TERM → −15 → SSH exit-signal("TERM") via the cloned Session::handle().
    2. Name derivation is alkssh server-side work, not a wire change: −N → the SSH exit-signal name from a Unix signal-number→name table (signal semantics belong to the serving platform, where the backend reaps). core_dumped is always false in v1 — alktty discards ExitStatus::core_dumped(), so nothing better is knowable from the chunk; no error message rides.
    3. −1 is never a signal claim. Per alktty ADR-004 §4, −1 is the backend could-not-determine sentinel — it collides with a real SIGHUP termination (signal 1). Alkssh renders −1 as exit-status(255) (OpenSSH's conventional failure value): fabricating exit-signal("HUP") for a wait failure would invent a signal that never happened. The SIGHUP information loss inside the sentinel is alktty's documented edge — alktty's additive exit_error control type is its own extension seam if it ever becomes load-bearing.
    4. PTY path — the real fidelity limit, backend-side. portable-pty 0.9 converts a signal-terminated child's wait status with code = status.code().unwrap_or(1) → 1, keeping the signal only in a separate humanized (strsignal) string field that alktty's PTY waiter discards (pty.rs reads exit_code() only; the number is unrecoverable through portable-pty 0.9's trait surface). So a signal-killed PTY session arrives as a plain exit 1 → alkssh maps exit-status(1); faithful exit-signal for the session/PTY row needs the PTY backend to conform to alktty's own negative-code convention — an alktty/portable-pty ask with tty/pty-grounded justification, not an alkssh convenience ask.
    5. Non-Unix: no signal termination; −1 is the only negative that can appear → rule 3.
    6. Pipe-mode pid-only vs PTY process-group signal scope is backend semantics, not a mapping change — recorded as a fidelity note on mapping.md's signal row.
  • Cross-references: OQ-08, mapping.md (exit_signal, exit_status, signal rows), alktty ADR-004 / alknet ADR-055

OQ-11: direct-streamlocal platform surface — compile-time or runtime decline shape?

  • Origin: mapping-table.md (streamlocal row)
  • Status: open
  • Priority: low
  • Impacts: blocks the tunnel feature's platform matrix detail (unix-only compile-out vs universal-compile runtime decline), a small contract test question.
  • Resolution: pending — decide during mapping.md; default direction is runtime decline (matches fail-closed and windows-on-ssh portability) unless the unix-only compile split proves simpler.

OQ-12: Client reconnect: does the crate own forward/session restoration? — resolved

  • Origin: reverse-runner.md §4
  • Status: resolved
  • Resolution: the crate owns lifecycle primitives only — bound-port handles (forward listeners return the actually-bound port, per russh's tcpip_forward -> u32), explicit cancellable forward/session handles, and clean disconnects. Reconnect-and-restore policy (rebinding persisted tunnel ports per alkminer's flow) is assembly-owned. Recorded as a constraint in client.md; no reconnect policy is baked into the crate.

Theme E — deferred features

OQ-13: The sftp client+server halves when alkfs lands

  • Origin: mapping-table.md (sftp rows), reverse-runner.md, POC-2 scope notes
  • Status: deferred(scope)
  • Door type: two-way
  • Priority: low (until alkforge phase 0 resumes)
  • Impacts: the sftp feature ships declining from the first build (honest advertisement, decline contract tested on both sides); the alkforge CI deploy path (binary/artifact push, alkminer-precedent evidence) and the alkfs server seam are the eventual consumers.
  • Blocked on: alkfs — family ADR-002, paused so alkforge can land; alkssh's sftp implementation follows alkfs' maturity.
  • Resolution: Not yet decidable — the target protocol is pending. The placeholder contract (decline, tested) is in scope for the first architecture pass.
  • Cross-references: OQ-01

Deferred / Blocked (at a glance)

OQ Blocked on Priority
OQ-01 concrete relay/browser-leg consumer medium
OQ-04 concrete OpenSSH-CA use case low
OQ-09 concrete wire-env use case low
OQ-13 alkfs landing (family ADR-002) low (for now)