Files
alkgit/docs/research/alk-stack.md
T
glm-5.3-flash 201c7a1fce docs: D-1 remainder — stale Internal-ops framing and OQ list
Completes review 001 D-1 (vision.md's half landed with ADR-015):

- alk-stack.md gitea-lesson item 2: supersession note pointing at
  ADR-012 §3 / ADR-015 (repo ops are External, scope + manage grant —
  right mechanism, wrong axis).
- AGENTS.md lifecycle status: active OQ list updated (OQ-03
  partially resolved, OQ-05 deferred, OQ-16 deferred; OQ-04/06/08
  resolved via ADR-013/012/011).
- review 001: D-1 marked resolved.

All three stale statements were pre-decomposition landmines: research
docs are declared 'current source of truth' by AGENTS.md, so the
superseded admin-API design needed marking.
2026-09-29 08:30:45 +00:00

6.9 KiB

Research: alk stack fit for a git server

Status: initial pass complete Date: 2026-09-19 Sources: local sibling crates at /workspace/@alkdev/{alkcall,alkhttp,alktls,alkvault} (all MIT OR Apache-2.0; alkcall/alkhttp/alktls published on crates.io, alkvault published at 0.1.0).

TL;DR

The alk stack covers the parts of a self-hosted git server that were historically the attack surface: the HTTP and SSH front doors, the wire framing, TLS/ACME, and secret storage. alkgit contributes the git brain (storage + smart protocol + repo/permission model) and the server binary that assembles everything. The architectural match with alkcall is unusually good: alkcall's BiStream is exactly the byte-stream abstraction git's packetline-based protocol wants underneath it — the smart protocol is a pair of unidirectional streams with request/response framing, which maps cleanly onto BiStream::write_half / read_half.

How the pieces map

alkgit need alk crate mechanism
Authenticated transport with per-peer identity alkcall Connection, Identity, IdentityProvider
Internal/external operation split alkcall Visibility::Internal ops are never wire-callable (ADR-017/024)
Per-repo/per-op authorization alkcall AccessControl::check(peer_identity) + required_scopes
HTTP front door for git smart-http + admin API alkhttp HTTP/1.1 + HTTP/2 serving, adapters for call protocol
TLS + ACME for the HTTP endpoint alktls rustls server configs, cert resolvers, ACME state machine
Encrypted storage of tokens/keys at rest alkvault AES-256-GCM vault
Byte streams under pkt-line alkcall BiStream handed to handlers via ProtocolHandler::connected

The gitea lesson, restated as alkgit design constraints

The pwned-gitea incident (CVE-2026-59774) was reachable because an internal API was exposed over the internet regardless of auth — the openapi-spec pattern of "describe everything, enforce somewhere" is the structural antipattern. alkcall already fixes the API-exposure half: only ops the caller has privileges for are even visible, and internal ops are structurally unreachable from the wire. alkgit must extend the same philosophy:

  1. No unauthenticated surface by default. Even info/refs advertisement requires an authenticated session unless a repo is explicitly public. Anonymous-clone is a per-repo opt-in, not a global default.
  2. Admin API = internal ops. Repo/user/permission management rides the call protocol as Visibility::Internal ops over the admin interface (or via alkhttp with auth); there is no admin endpoint that exists on the same unauthenticated surface as git traffic. (Superseded by ADR-012 §3 / ADR-015 for alkgit's repo ops: the ops are Visibility::External, gated by scope + the per-repo manage grant — the gitea lesson is served by the ACL (visible-surface = authorized-surface), not by hiding the ops. "Right mechanism, wrong axis.")
  3. No plaintext secrets in the DB. Credentials/tokens go through alkvault; the metadata store holds references, not keys.
  4. Blast-radius thinking carries into the design: single binary, no plugin execution, no markdown renderer by default (a web UI, if it ever exists, is a separate phase and separate threat model).

Interfaces in alkcall terms

Git transport framing per interface:

  • SSH: the alkcall channels protocol carries a channel whose stream is the git pkt-line conversation. The client (git clone ssh://...) expects an exec of git-upload-pack '<repo>'; with alkcall the "SSH channel-like" layer provides the framing and identity, and we dispatch on the requested command string. alkcall's channel model (channel 0 = call registry, data channels = BiStream) maps to: one data channel per git session.
  • HTTP: alkhttp hosts the smart-http endpoints (GET /<repo>/info/refs?service=git-upload-pack, POST /<repo>/git-upload-pack, POST /<repo>/git-receive-pack). Smart-http is stateless per request (V0/V1) or one-shot (V2), which fits the http adapter model; the response body is a pkt-line stream we generate.

Both interfaces converge on the same core: a BiStream-like duplex session + repo identity + caller identity → alkgit-transport runs the smart protocol and produces/consumes packs via alkgit-core. The interfaces are thin adapters; all policy (auth, ACL, limits) lives before the transport layer gets the stream. This is the same "ALPN as a service" shape as alktty/alktunnels/alksocks: the git member of the family, one core, many front doors, and nothing below the transport boundary knows or cares which door produced the stream.

What to verify before architecture commits (POC candidates)

  1. BiStream ↔ pkt-line fit: run a real git clone handshake over an alkcall Connection using gix-packetline async codec end-to-end. Verified (POC-1, 2026-09-20): real git 2.43 clones/fetches fsck-clean over Connection → BiStream → tokio-util compat → gix-packetline. See poc-1-findings.md.
  2. HTTP smart protocol shape: confirm alkhttp's streaming response body can carry a pack (chunked) and that request bodies stream in without buffering the whole POST in memory (receive-pack can be gigabytes). Verified (POC-3, 2026-09-21): real git 2.43 clones/fetches fsck-clean over Connection → HttpAdapter → axum custom routes; request bodies stream (BodyDataStream, one hyper-read chunk per poll_next — measured with a dribble probe and git's forced-chunked POST path); pack responses stream under back pressure (bounded mpsc → Body::from_stream, O(counts) RSS on a 60k-object clone). See poc3-findings.md.
  3. alkcall backpressure/limits vs git sessions: large clones are long-lived, high-throughput, single-direction streams; confirm channels buffer limits (ADR-040) don't fight pack streaming, or route git sessions as raw duplex streams rather than chunk-framed channels if needed.
  4. alktls ACME: config shape for the http endpoint; likely trivial.

Composability boundary (what "ALPN as a service" implies here)

Same family pattern as alktty/alktunnels/alksocks (alknet decomposition): one core that is front-door-blind, adapters that each speak one door. For alkgit the boundary is concrete:

  • alkgit-core + alkgit-transport depend on alkcall types only (BiStream, identity/ACL types); they never depend on alkhttp or alkgit-ssh, and carry no http/channel-specific types below the stream.
  • The http and ssh crates are replaceable: a downstream app (gitea-like) embeds the core + transport and brings its own front doors.
  • Admin API = alkcall ops, usable as-is or replaced by a downstream app.

Version pins in workspace manifests

  • alkcall 0.8 (published), alkhttp 0.5 (published), alktls 0.1 (published), alkvault 0.1 (published). The workspace root already pins these; bump via workspace.dependencies when siblings release.