# 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. 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 ''`; 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 //info/refs?service=git-upload-pack`, `POST //git-upload-pack`, `POST //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.