alkgit 0.1.0

Git smart protocol for the alk family: pkt-line substrates, protocol V2 serving (upload-pack/receive-pack), producer/consumer halves on alkcall channels, and the backend seam traits — the shipped alkstore+alkblobs composition is the alkgit-substrate crate (ADR-010/025)
Documentation
---
status: reviewed
last_updated: 2026-10-20
---

# Doors: how alkgit is exposed

## What this is

alkgit (per ADR-010) owns no front doors. This document records the two
door mappings that exist or are planned in the family, and the requirements
alkgit places on each. The protocol crate itself is door-blind (ADR-002):
every door converges on the same two substrates.

## The door pattern

Doors are family infrastructure: alkhttp (exists), alkssh (planned, after
alksocks), the alknet rewrite (coming). A door wraps alkcall's
producer/consumer in its wire protocol; services like git, tty, tunnels,
and socks5 are payloads doors optionally expose. Downstream consumers
(our platform deployment, a future gitea-like app) assemble the doors they
want with the payloads they want.

Auth semantics are the door's auth (http: the door's token mechanism; ssh:
the door's key-based identity) resolving an alkcall `Identity` (the
identity-extractor seam that ADR-006 needed exists only in the door, where
it belongs). alkgit consumes the resolved identity and the registry
record: the per-repo check is alkgit-core's `authorize` policy function
(ADR-011, ADR-015 — public+read anonymous-first-class, write always
authenticated+granted, `manage` for repo administration), run at
ADR-007's step-3 position by every door and by the channels open-op
gate. There is no door-specific auth surface
in alkgit and no scope constant — per-repo grants replaced the
`tty:open`-style gate (the single-scope shape cannot express
anonymous-public fetch).

## alkhttp `git` feature (http mounting)

**Scope**: an alkhttp feature that maps two route shapes onto alkgit's
stateless substrate (ADR-005):

| Route | Service |
|---|---|
| `GET /{repo}/info/refs?service=git-{upload,receive}-pack` | advertisement (smart prefix + capability dump + flush) |
| `POST /{repo}/git-upload-pack` | V2 fetch commands (one command per POST) |
| `POST /{repo}/git-receive-pack` | push (streaming ingestion, budgeted body) |
| `GET /{repo}/info/refs?service=git-upload-archive` | honest refusal (not served) |

`{repo}` is the registry id (ADR-008); resolution + ACL run before any
response byte (ADR-007).

Two-segment repo ids (`owner/name`, backend.md §repo-id grammar) need
a two-placeholder route (`/{owner}/{repo}/info/refs`, single-segment
ids enumerated too) — and alkhttp door-surface route tables are static
shapes (no route fallback; the POC-L/alkhttp constraint), so the
patterns are enumerated, not fallback-chained.

**Sequencing**: the feature requires `alkgit` on crates.io (optional
dependencies must resolve), so it lands in alkhttp after alkgit's
first publish — the next unpublished minor after 0.8 (0.8 carried the
door-auth/ownership wire-throughs; the `git` feature itself is the
first alkgit-consuming surface and sets the pattern the `lfs` feature
follows). Until then, git-over-http is served by any downstream that
mounts the stateless substrate directly — POC-3's `httpservice.rs` is the
reference implementation of exactly that mapping.

**Framing facts the feature must honor** (all POC-3-validated, encoded in
the substrate, not re-decided): responses end at flush (never `0002`);
top-level flush is one-shot per response (the receive-pack sideband
report's inner flush lives inside band-1 and is part of the report
framing — ADR-013 §8; the rule governs protocol-level sections only);
flush-only POSTs are probes answered
200-empty; request bodies stream (no accumulation); response bodies
stream under back pressure (bounded mpsc → `Body::from_stream`); request
bodies carry a budget (ADR-009 — alkhttp custom routes are unbounded by
default). Push framing per ADR-013 §12 (probe/Content-Length/chunked,
request/result content types).

## alkhttp `lfs` feature (LFS door, per [lfs.md]lfs.md / ADR-022)

**Scope**: an alkhttp feature mapping the LFS batch/transfer protocol
onto alkblobs' `ops` surface — batch/checking through the door's own
`OperationContext` (identity + ownership provider *wired* — never the
static fallback; alkcall ADR-048's loud default is the guard the POC's
seam evidence became the regression record for), transfer through the
ops byte-path (pre-keyed `blobs/put` / ranged `blobs/fetch`, the
identity-family keying making uploads wire-native again — ADR-022 §2).
git-lfs speaks HTTP only, so this door has no native-op sibling.

| Route | Purpose |
|---|---|
| `POST /{repo}/objects/batch` (and the `/{owner}/{name}` shape) | batch/checking → `blobs/stat` |
| `PUT /{repo}/lfs/objects/{oid}` | basic transfer upload → `blobs/put` (pre-keyed claim) |
| `GET /{repo}/lfs/objects/{oid}` | basic transfer download → `blobs/fetch` (ranged; per-window digests) |

Door facts (all POC-L-validated): hrefs door-minted absolute with
per-action `Authorization`; anonymous-public rides the synthesized
`anon` identity; batch auth tolerates Basic userinfo (landed: alkhttp
0.8 ADR-073); `hash_algo` = sha256 only; `verify` not served; route
tables are static shapes — two-segment repo ids enumerate patterns (no
route fallback exists on alkhttp door surfaces). Same publish
sequencing as the `git` feature (after alkgit's first publish); until
then a downstream mounts the surfaces directly — POC-L's door is the
reference implementation.

## alkssh (git-over-ssh, future)

**alkgit's requirement on alkssh** (to record in alkssh's spec when it
exists): parse the exec-request string with a fixed grammar —
`git-upload-pack '<repo>'` / `git-receive-pack '<repo>'` — never shell-
interpret it (ADR-008's never-execute rule), map the door's key-based
identity to the alkcall identity space, resolve the repo id against the
registry, run ACL, and hand the ADR-002 duplex tuple — `(identity, repo,
service, authorized-repo marker, post-auth stream, limits)` — to
alkgit's duplex session; the exec command is the service selector
(ADR-016's per-path service-establishment rule). `git-upload-archive`
gets a fixed refusal. V2 is expected (ADR-003); `GIT_PROTOCOL=version=2`
rides the ssh env mechanism.

**Interim**: no git-over-ssh path ships with alkgit. A downstream that
needs it before alkssh lands can terminate wire-ssh itself (russh or
otherwise) and consume the duplex session — that is exactly the embedder
path ADR-002 defines, and it works today against the POC-1 shape. It is
an embedder assembly concern, not alkgit scope.

## The alkcall-native path (no adapter at all)

The `alk/git` ALPN producer and the channels open-op (`channels/git/sub`,
params `{repo, service}` — ADR-016: the params are the negotiation, the
service selector, and the open-time ACL point; the establisher resolves
the repo and runs `authorize` with the action the service selects) need
zero door code — POC-1 is that shape verbatim. On the direct-ALPN path
`GitAdapter` parses the git-daemon request line
(`git-upload-pack <repo>\0host=…\0\0version=2\0` /
`git-receive-pack <repo>\0host=…\0`) as the in-band preamble (ADR-016).
Any alkcall-speaking client (including alkgit's own consumer half,
`GitSession` — ADR-017's typed fetch/push client) can use either path.
This is the baseline path; the http/ssh doors are conveniences layered
on top for stock git clients, and the replicator path of the p2p
deployment (direct push/pull between replicators) rides it directly.

## Assembly (downstream responsibility)

There is no alkgit binary (ADR-010). A deployment assembles: alkcall
connection sources (alkhttp/alktls for http, alkssh later, raw ALPN for
the native path) + `GitAdapter` + a backend construction (the
`alkgit-substrate` composition's —
[substrate.md](substrate.md) §assembly shapes — or its
own trait impls against the protocol core) + `Limits` from config +
vault for any credential
material. Reference sequence for our platform deployment lives in that
deployment's docs, not here.

## Design Decisions

| ADR | Decision | Summary |
|---|---|---|
| [002]decisions/002-front-door-blind-core.md | Session boundary | every door consumes the same tuple |
| [003]decisions/003-protocol-v2-first.md | V2-first | honest advertisement per door |
| [007]decisions/007-acl-before-advertisement.md | ACL first | before any protocol byte |
| [008]decisions/008-registry-resolved-repo-identity.md | Repo identity | wire names are registry ids |
| [009]decisions/009-bounded-resources-budget.md | Budgets | limits on every session |
| [010]decisions/010-pure-protocol-crate.md | Pure protocol crate | doors are family infrastructure |
| [011]decisions/011-per-repo-authorization.md | Per-repo authorization | `authorize` policy, grants in records (amended: ADR-015) |
| [012]decisions/012-registry-backing-and-ops.md | Registry + ops | op registration surface, two op kinds (gate amended: ADR-015) |
| [013]decisions/013-receive-pack-state-machine.md | receive-pack | V0-framed push advertisement per door, report framing |
| [014]decisions/014-v2-negotiation-ack-loop.md | Negotiation | one round per POST; ack section is per-round, stateless |
| [015]decisions/015-manage-grant-and-op-gate.md | Manage grant + op gate | `manage` tier, admin-OR-manage op gate |
| [016]decisions/016-native-session-preamble.md | Native session preamble | `{repo, service}` open-op params, request-line preamble, service in the tuple |
| [017]decisions/017-consumer-half-git-session.md | Consumer half | `GitSession` typed client — the direct-connection push/pull primitive |
| [018]decisions/018-backend-trait-signatures-and-storage-error-model.md | Trait signatures + storage errors | backend-seam shapes pinned (the door-blind object-storage family) |
| [019]decisions/019-substrate-baseline.md | Substrate re-baseline | the backend construction doors assemble (substrate.md §assembly shapes) |
| [025]decisions/025-two-crate-workspace.md | Two-crate workspace | door crates depend on the protocol core; the substrate can never ride along by default |
| [022]decisions/022-lfs.md | LFS | the alkhttp `lfs` door (door facts + sequencing) |
| [023]decisions/023-replication-v1.md | Replication v1 | `GitSession` — the native path's consumer client |

## Open Questions

- OQ-19 lives in [open-questions.md]open-questions.md
  (implementation-phase wire validations); OQ-17 (registry migrations)
  resolved as ADR-024 — neither is door-scope. Door-specific facts are
  pinned in the feature sections above and [lfs.md]lfs.md; OQ-16
  (grant-key identity namespace) lives at the identity-provider seam,
  not the door.

## References

- `docs/research/poc-1-findings.md` (duplex shape), `docs/research/poc3-findings.md`
  (http mounting evidence, framing facts)
- alktty/alktunnels architecture docs (the template this follows)
- ADR-010 (supersedes ADR-001/006; this file replaces http.md, ssh.md,
  alkgitd.md)