Renames the written-usage rule into practice across the contract surface: docs/ (architecture, research, plans, sdd), tasks/ (all 43 task files, taskgraph validate passes), AGENTS.md, README.md, CHANGELOG.md, and the .opencode/agents/ prompts. Every replacement is a sense-preserving rewrite (colons, semicolons, commas, parentheses, sentence splits, varied), never a mechanical swap; no spaced hyphens were substituted. Enum fields, frontmatter, tables, wire data, and cited identifiers survive verbatim; en dashes inside numeric ranges keep their meaning. Verification: taskgraph validate (43 tasks), repo grep clean (single exempt hit: the verification command's own search pattern in AGENTS.md), cargo test, clippy --all-targets -D warnings, fmt --check, doc all green.
131 lines
6.8 KiB
Markdown
131 lines
6.8 KiB
Markdown
# Vision
|
|
|
|
Status: Draft (phase 0 research).
|
|
|
|
## What this is
|
|
|
|
alkssh is the alk family's SSH adapter: a thin wrapper around russh
|
|
terminating the vanilla-ssh wire and bridging to/from alkcall channels
|
|
(family ADR-003). It is the alkhttp-style adapter for the SSH protocol, the
|
|
vanilla-ssh front door for vanilla clients, and the family's
|
|
vanilla-ssh client where a family app dials an upstream SSH server.
|
|
|
|
The substance almost entirely exists in the family. What alkssh adds is
|
|
the glue + the mapping:
|
|
|
|
- vanilla ssh (ssh(1), git(1), sftp clients) connect to an alkssh
|
|
endpoint as they would to any sshd;
|
|
- each SSH channel type is mapped onto a family protocol half
|
|
(see `mapping-table.md`);
|
|
- identity arrives the same way it does everywhere else in the family:
|
|
a verified SSH public key's fingerprint resolved through alkcall's
|
|
`IdentityProvider` (see `auth-bridge.md`).
|
|
|
|
The alkforge-relevant property is **exclusion**: an alkforge ssh
|
|
endpoint built with only the `git` feature is structurally incapable of
|
|
serving a shell, tunnels, or socks. Every mapping is a cargo feature;
|
|
unfeatured channel opens decline fail-closed (family ADR-003 decision
|
|
3). This is why alkssh leads alkforge's phase 0: the adapter is glue,
|
|
not protocol invention.
|
|
|
|
## Guiding principles
|
|
|
|
1. **russh owns the SSH wire.** No SSH framing is authored in-family
|
|
(family ADR-003 decision 1). KEX, cipher negotiation, channel
|
|
multiplexing, and userauth state machines come from russh; third-
|
|
party-upstream posture applies (file asks upstream; never silently
|
|
absorb a workaround).
|
|
2. **A door in both directions, vanilla on the outside.** alkssh is a
|
|
door for vanilla clients (server direction: vanilla in, family
|
|
out, each channel served by a family seam entry; primary current
|
|
purpose: git) and a door to vanilla servers (client direction:
|
|
family in, vanilla out; the reverse-runner shape: a family hub
|
|
dials vanilla sshd and the ops map family-side onto the same
|
|
protocols, **alktty** for sessions, **alktunnels** for
|
|
tunnels/-D legs, **alkgit** for upstream git, alkfs client later
|
|
for sftp; the runner stays vanilla by design). Type symmetry at
|
|
the seam: the family-facing surface speaks family-protocol types
|
|
in BOTH directions; the vanilla side speaks russh. The asymmetry
|
|
is honest and recorded: the server direction *serves* family seam
|
|
entries; the client direction *consumes* family types. It carries
|
|
no deployment policy: a downstream app (alkforge) embeds it,
|
|
enables features, and brings identity/ACL configuration.
|
|
3. **Fail-closed everything.** The feature graph, the auth-method
|
|
disposition, and the channel-request disposition all default to a
|
|
clean decline. Nothing is advertised that isn't served; nothing is
|
|
half-served. Every decline path is contract territory and is
|
|
tested.
|
|
4. **One seam per mapping.** Each family protocol already exposes a
|
|
generic duplex-stream or `Connection` serving entry
|
|
(`mapping-table.md`); a russh channel's read/write halves plug in at
|
|
exactly one point per mapping. No channel-type knowledge flows
|
|
upstream into the protocol crates.
|
|
5. **Identity is the family's, not SSH's.** SSH user names, `~/.ssh/`
|
|
conventions, and password auth are not the alk family's identity
|
|
model. A verified key's fingerprint → `Identity` via alkcall's
|
|
`IdentityProvider` is the only resolution path; secrets an alkssh
|
|
*client* holds come from alkvault-backed sources: vault-encrypted
|
|
at rest, treated as bearer-token-class secret material, never env
|
|
vars (family ADR-002 decision 5). The vault is a secret store on
|
|
this path, not a key generator or key custodian.
|
|
|
|
## Non-goals
|
|
|
|
- **Authoring SSH protocol machinery**: no in-family framing, cipher,
|
|
or KEX code, ever (russh upstream decides what SSH is).
|
|
- **Being an sshd.** No local user accounts, no `authorized_keys` file
|
|
semantics of the OpenSSH variety, no PAM-shaped anything. Deployment
|
|
posture belongs to the embedding app.
|
|
- **Serving sftp**: deferred to alkfs (family ADR-002); the subsystem
|
|
decline is the placeholder contract.
|
|
- **A binary / CLI.** Downstream apps embed the crate; there is no
|
|
`alkssh` executable in this crate.
|
|
- **Password / keyboard-interactive auth for clients.** The family
|
|
identity model has no password concept; these methods decline
|
|
server-side (see `auth-bridge.md`) and exist client-side only as
|
|
non-goals until a consuming use case arrives.
|
|
|
|
## Scope questions phase 0 must answer
|
|
|
|
These frame the research docs; each is a candidate open question for
|
|
phase 1's `open-questions.md`:
|
|
|
|
1. **The `alk/ssh` ALPN's topology role**: alkcall ADR-004 reserves
|
|
`alk/ssh` → `SshAdapter`, ADR-034's channels table gives it
|
|
stream_types `[0, 1]` ("SSH multiplexes internally, including its
|
|
own control"), and ADR-009 sketches a browser-WASM route where the
|
|
ALPN transports an SSH stream to an `SshAdapter`. But family ADR-003
|
|
describes alkssh terminating vanilla SSH at an edge. It remains
|
|
unresolved whether `alk/ssh` carries raw vanilla-SSH bytes between
|
|
alkssh instances (a relay leg), serves the browser route, or both,
|
|
and how that interacts with ADR-042's "the hub never runs a handler
|
|
for `alk/ssh`" relay ruling (see `mapping-table.md` §topology).
|
|
2. **russh pin**: version, crypto-backend feature, and the
|
|
RC-pinned transitive dependency situation (see `russh.md`).
|
|
3. **Feature names + graph**: the `session`/`tunnel`/`dynamic-forward`/`git`/`sftp` naming and their independence
|
|
(no feature implies another; flat until architecture says otherwise).
|
|
4. **Auth dispositions**: which russh auth methods the server
|
|
implements vs declines; the exact `auth_publickey` → fingerprint →
|
|
identity contract; client-side vault-backed at-rest key handling
|
|
(see `auth-bridge.md`).
|
|
5. **The mapping table details**: per channel type and per request,
|
|
the family seam, the parameter mapping, and the decline contract
|
|
(see `mapping-table.md`); POC validation per `pocs.md`.
|
|
|
|
Resolution status (post-POC): question 4 is answered by
|
|
POC-1/POC-2 evidence, question 5 by POC-3 (three mapping-row
|
|
corrections recorded), and question 2's *facts* are verified (the pin
|
|
decision itself is phase 1 ADR territory). Questions 1 (the `alk/ssh`
|
|
topology) and 3 (feature naming) are phase 1 decisions, carried as
|
|
seeded OQs / ADR territory per `pocs.md` §Sequencing and gates. A
|
|
client-direction consumer pattern (hub-dials-runner) is extracted in
|
|
`reverse-runner.md` and shapes the phase 1 client surface.
|
|
|
|
## References
|
|
|
|
- family ADR-003 (`/workspace/@alkdev/alkfamily/docs/architecture/decisions/003-alkssh-russh-adapter.md`)
|
|
- family ADR-002 (alkforge; why the exclusion property matters)
|
|
- family open-questions OQ-08 (partially resolved; remaining scope is
|
|
this repo's phase 0)
|
|
- `russh.md`, `auth-bridge.md`, `mapping-table.md`, `pocs.md` in this
|
|
directory |