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
---

# LFS: the large-media surface

## What this is

Git LFS support for alkgit assemblies: the pointer-file flow, the
media storage placement, the door mapping, and the GC liveness wiring.
LFS is v1 scope (ADR-022) because the substrate makes it nearly free:
alkblobs' two-tier shape is exactly "small objects in kv, big media in
large tier" (local files on sqlite, pg-lo on postgres), and the media
protocol is a small batch/transfer adapter. Everything here rides the
substrate stack in [substrate.md](substrate.md); the *decision* rationale
is ADR-022.

Evidence: POC-L (`pocl-findings.md`) — real `git-lfs` 3.7.0
push/pull/fresh-consumer round trips, both door bindings, mem/sqlite/
postgres engines, GC sweep behavior. Requirements reference:
`rudolfs` (`/workspace/rudolfs`) — batch/transfer protocol behavior and
Range serving (facts only, license-clean; its no-auth posture is the
opposite of ours).

## The model in one paragraph

A pushed `git lfs` push commits **pointer-file blobs** — ordinary git
blobs under the git-object family, handled by the normal git path with
no LFS awareness. The *media* lives in alkblobs, keyed under
`Algorithm::GitObjectSha256` with the raw content as the stored
preimage: **key = `[0x04] + sha256(content)`, byte-exact the LFS oid**
(alkblobs ADR-018's preimage-identity derivation) — so the key is
formable in advance from the pointer's metadata, oid addressing needs
no translation table, and verification-on-get re-derives under the
same rule every read. Which repos reference which media is *not* a
store fact (the CAS pool is global — their ADR-017) — that reference
map is alkgit's co-tenant liveness row table, wired into the sweep
chain by default.

## The three surfaces

### 1. The batch/checking adapter (the JSON protocol)

The batch API (`POST <url>/objects/batch`) is an alkhttp-route surface
— the door maps it onto alkblobs' ops through its **own**
`OperationContext` (identity + ownership provider wired — alkcall
ADR-048's loud default is the guard against the static-fallback leak).

- Existence check: `blobs/stat` with the formable key (tag + oid).
  No `have` use — that is the replication diff verb; `stat` answers
  the batch's `objects[].{oid,size,authenticated}` shape.
- Authorization: the door's per-repo grant governs batch access
  (ADR-011 read/write actions map to batch upload/download);
  anonymous-public repos ride the door-synthesized `anon` identity
  composed onto the ops ACL (POC-L's validated composition). Push is
  always authenticated, as everywhere.
- `hash_algo` answers `sha256` only (409 otherwise — the batch is
  pinned to the media algorithm alkgit keys on); `verify` is not
  served; hrefs are door-minted absolute URLs carrying per-action
  `Authorization` tokens — the LFS hand-off, client-verified.
- Batch auth accepts Basic userinfo on this surface (alkhttp ADR-073's
  door tolerance) — real LFS clients authenticate the batch with
  Basic userinfo; transfer actions carry the minted Bearer tokens.

### 2. Transfer (the byte protocol)

- **Upload** (`PUT <href>`): mapped onto the wire ops `blobs/put`
  with the **mandatory pre-keyed claim** — key formed from the
  pointer's oid, `len` from the batch's size (size always present; a
  size-less offer is refused at the door). Restored to the wire by
  ADR-022 §2 — the pre-keyed form is satisfiable for LFS, which is
  exactly what identity-family keying buys. A mislabeled body is
  refused at the stream's derivation check: no stored content, no
  held pin.
- **Download** (`GET <href>`, `Range`-bearing): mapped onto the wire
  ops `blobs/fetch`, ranged arm — **per-window slice digests verified
  mid-flight** (the ranged arm's answer; the door owns window
  splitting; measured: 45 MiB as two 32/13 MiB opens, byte-exact).
  The whole arm is unbounded at establish, so the door budgets it
  (per-assembly object-size ceiling); the ranged total is bounded by
  `fetch_max_total_bytes` (an `OpsConfig` knob, default 64 MiB —
  raise per assembly to trade opens for windows).
- **Bindings**: for the embedded door, **DirectOps** — the door binds
  the ops machinery *in-process* (it holds the `OpsContext` and invokes
  the op registry directly, no loopback), the recommended form (POC-L's
  coined term, `pocl-findings.md` §"where do transfer/token hand-offs
  bind best"; measured ~25 ms
  per-request vs ~80 ms through the per-request loopback bridge);
  the loopback `alk/channels` bridge stays the shape for a door that
  must exercise the real ops byte-path (fleet/remote consumers) —
  with the pre-keyed upload restored it now works in *both*
  directions (the read-only limitation was blob-family keying's
  artifact, superseded by ADR-022 §2); an in-process embedder that
  binds the store directly uses the facade seam
  `put_as(GitObjectSha256, len, stream)` / `read_range` with no ops
  plane at all.
- Token layers compose as measured: door-minted href tokens
  (per-action capabilities — `lfs:<hex>` Bearer) over alkblobs
  PinTokens (store custody, `mint`/`confirm`). The hostile
  upload-loop threat shape is met by the sibling's mint-boundary
  admission cap (`RegistryFull` → `MINT_REFUSED` — alkblobs
  ADR-019); fleet forms give pin-row TTLs by engine configuration.

### 3. The pointer lifecycle / liveness table (alkgit-owned)

One co-tenant row per reference: `(repo_id, oid, size)`.

- **Written** when a reference is asserted — at batch check/upload
  time (the moment the server learns the pair) — into the same
  relational store as refs/registry (substrate.md's composition; its
  own transaction). No stream event: the reactive halves' publish leg
  is record-write-scoped (ADR-019 — refs and liveness rows ride their
  own durable surfaces; consumers of repo-record changes need neither).
- **Consumed** by alkgit's shipped LFS `LivenessSource`, wired into
  the sweep chain by default (ADR-019 §6; `lfs` feature default-on —
  fail-safe: an unregistered source is the dangerous default for a
  mixed deployment).
- **Cross-reference sharing**: the same content in two repos is one
  key with two rows; the source must re-protect the key while *any*
  row references it (POC-L's refcount requirement — a crude second
  scan in the POC, replaced by the shipped design; the refcount column
  ships in the initial v1 schema, ADR-024 §5 — no migration owed).
- **Orphans** (rows gone from refs, content unreferenced) are
  sweep-eligible: the store's sweep-window stages then deletes them
  (POC-L: orphaned entry deleted, referenced survive).
- **Repo deletion**: the rows go with the repo (same removal as
  refs/registry); media becomes unreferenced and sweeps — nothing
  LFS-specific exists here.
- `PinConverter` stays alkblobs' sync-only/failure-free/`Key`-only
  contract — rich reference metadata lives in these rows, never in
  the converter (POC-L's structural fact).

## Constraints and posture

- **Engines**: LFS ⇒ a dual-tier constructor (`dual_tier_sqlite_local`
  on sqlite; `dual_tier_postgres_pglo`/`+local` on postgres;
  `dual_tier_mem_local` for completeness). kv-only shapes refuse
  over-threshold puts — the deployment fails loudly at the first big
  upload, which is the right failure (ADR-022 §6).
- **Isolation**: the CAS global-pool caveat applies unchanged — media
  is cross-reachable by digest knowledge; per-repo isolation means
  per-trust-domain store instances (the accepted posture, ADR-022 §7).
- **No native LFS op set of alkgit's own**: git-lfs speaks HTTP only;
  the door is the alkhttp `lfs` feature (family infrastructure,
  after alkgit's publish — doors.md's sequencing rule). The native
  plane LFS rides is alkblobs' own `ops`.
- **Route shapes** (empirically pinned by POC-L, and the static tables
  the door mounts — no route fallback — so these are the feature's
  routes): batch `POST /{repo}/objects/batch` (the `/info/lfs` prefix
  is a gitea URL-embedding convention, not a client rule — with
  `lfs.url = http://host/<repo>` the client POSTs at `{url}/objects/
  batch`); transfer `PUT`/`GET /{repo}/lfs/objects/{oid}` — hrefs are
  door-minted absolute URLs pointing at these static transfer routes;
  two-segment repo ids enumerate patterns (same caveat as the `git`
  feature, backend.md §repo-id grammar).
- **Encryption**: non-goal (storage of pointer references and media is
  custody-neutral; any at-rest encryption story rides alkvault and the
  substrate's own posture, not this surface).
- **Lock server** (`lfs.locks` API): not served v1 — `lfs.locksverify
  false` is the client-verified workaround; a future additive surface.

## Design Decisions

| ADR | Decision | Summary |
|---|---|---|
| [022]decisions/022-lfs.md | LFS keying + door + liveness | the decisions this doc details |
| [019]decisions/019-substrate-baseline.md | GC wiring | the sweep chain the liveness rows feed |
| [021]decisions/021-feature-graph-and-dependency-posture.md | Feature graph | the default-on `lfs` feature (placement re-homed: `alkgit-substrate` — ADR-025, which supersedes this ADR's graph) |

## Open Questions

- **OQ-20**: per-oid pruning of stale reference rows (beyond the
  repo-lifetime scoping this spec pins — the pointer-lifecycle ops'
  future shape).
- **OQ-21**: the LFS lock server (`lfs.locks` API) — surface build
  deferred on a client requirement (`lfs.locksverify false` is the
  verified workaround); its composition-validation POC is owed before
  the v0.1 publish (the register entry carries the split).
- **OQ-17** (resolved — ADR-024): the liveness refcount ships in the
  initial schema; the co-tenant tables' migration story is
  construction-run/version-stamped/additive-first.

## References

- [ADR-022]decisions/022-lfs.md (the decision record — read this
  first), [substrate.md]substrate.md (the composition), [backend.md]backend.md
  (seam + repo grammar), [doors.md]doors.md (door sequencing)
- alkblobs ADR-018 §1/§3 (preimage-identity derivation; the stat
  semantic), ADR-017 there (global-pool caveat), ADR-019 there (mint
  admission cap), ADR-020 there (pre-keyed wire put)
- `docs/research/pocl-findings.md` (the gate evidence, route/auth
  facts, custody postures, mid-flight verification measurements)
- `/workspace/rudolfs` — the requirements reference (protocol facts
  only; license-clean — see `docs/research/reference-policy.md`)