Files
alkgit/docs/research/alk-stack.md
T
glm-5.3-flash 665f701133 docs(research): POC-1 complete — pkt-line over alkcall BiStream verified
Full V2 fetch handshake (ls-refs + fetch with done + sideband pack) runs
between real git 2.43 and a Rust listener over the alkcall
Connection -> BiStream -> tokio-util compat -> gix-packetline path.

Verification: git ls-remote, git clone (fsck --strict clean), incremental
git fetch, annotated-tag checkout, in-process duplex selftest. All over
alkcall 0.8 core types; the flagged futures-io risk resolved with a
two-line tokio-util compat adapter.

Findings: docs/research/poc-1-findings.md (deliverable; POC source stays
disposable at /workspace/alkgit-poc1 per the standalone POC mode).

Also records in the research docs:
- git-protocol.md: observed done-path shape (no acknowledgments section),
  git:// first-request framing, advertise-once rule
- gitoxide.md: gix-packetline async stop-delimiter + reset() contract
- alk-stack.md: POC-1 verification item marked resolved
- pocs.md: POC-1 status -> proceed
2026-09-20 12:18:04 +00:00

6.2 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.
  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).
  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.