The substrate release gate (substrate/review-wave-10) verdict, landed: - OQ-21 outcome confirmed (posture evidenced; build deferral stands verbatim; OQ-03's substrate publish-side blocker stays dropped) - the substrate-side OQ-03 freeze inventory settled and reconciled against the crate root's actual re-export list (recorded for w10-publish to freeze) - the release-readiness surface verified (docs, matrix incl. the three off-matrix clippies, CHANGELOG/README, dry-run re-run; F-w10-B4/B5 + N-3 dispositions confirmed) - all flagged recorded interpretations confirmed (O-g future work, the through-store probe stamp, mem_registry removal, testutil home, LIVE_GATE stay) - the test evidence read green; review 006 R-1..R-7 verified absorbed Inline findings fixed in-session: - D-1: the architecture README's status-index cells were stale since the wave-5 flip; made truthful with the substrate.md/lfs.md `stable` flips - D-2/D-3: two task-record slips (the test-evidence Summary's O-b/O-c cross-reference; the placement task's unticked ACs) - D-4: DEFAULT_SCHEMA + LOCKED_OUT_RETRY_SECS promoted to the crate-root re-export list (both module-path-public, never root re-exported — the freeze authority byte-exact; additive pre-freeze) Verification: cargo test (376/186), --all-features 379, the live-pg harness run 212 passed 0 skips, the fuzz corpus gate 13 targets, fmt + five clippy passes (-D warnings), cargo doc 0 warnings, publish dry-run green, taskgraph validate (90 tasks).
12 KiB
status, last_updated
| status | last_updated |
|---|---|
| stable | 2026-10-24 |
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; 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 every read verifies under the same rule's
discipline: the kv tier's materialized reads re-derive at the store,
while the large tier (where the media lives) carries the per-window
slice digests mid-flight — the end-to-end oid conclusion is the batch
adapter's whole-span oid check (the wave-9 gate's F-S3 correction to
"verification-on-get re-derives every read"; ADR-022's Status carries
the amendment). 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/statwith the formable key (tag + oid). Nohaveuse — that is the replication diff verb;statanswers the batch'sobjects[].{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
anonidentity composed onto the ops ACL (POC-L's validated composition). Push is always authenticated, as everywhere. hash_algoanswerssha256only (409 otherwise — the batch is pinned to the media algorithm alkgit keys on);verifyis not served; the batch request'stransfersnegotiation is the door's to read — refuse a request whose list excludesbasicbefore the batch reaches the adapter (the adapter answersbasicand carries no negotiation input); hrefs are door-minted absolute URLs carrying per-actionAuthorizationtokens — 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 opsblobs/putwith the mandatory pre-keyed claim — key formed from the pointer's oid,lenfrom 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 opsblobs/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 byfetch_max_total_bytes(anOpsConfigknob, 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
OpsContextand 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 loopbackalk/channelsbridge 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 seamput_as(GitObjectSha256, len, stream)/read_rangewith 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;lfsfeature 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.
PinConverterstays 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_localon sqlite;dual_tier_postgres_pglo/+localon postgres;dual_tier_mem_localfor 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
lfsfeature (family infrastructure, after alkgit's publish — doors.md's sequencing rule). The native plane LFS rides is alkblobs' ownops. - 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/lfsprefix is a gitea URL-embedding convention, not a client rule — withlfs.url = http://host/<repo>the client POSTs at{url}/objects/ batch); transferPUT/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 thegitfeature, 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.locksAPI): not served v1 —lfs.locksverify falseis the client-verified workaround; a future additive surface. Its composition is now evidenced (OQ-21's validation POC, 2026-10-24 — branch mode,poc/w10-oq21-lock, main's tree untouched; the record lives in open-questions.md OQ-21's Investigation): the honest mapping is plain co-tenant rows — repo-scoped(repo_id, id, path, owner, locked_at)with the INSERT-if-absent primary key as the exclusion, one BEGIN IMMEDIATE transaction per verb — not alkstore's named-TTL lock machinery (whose silent exclusion lapse at TTL expiry is the two-writer data-loss window, whose custody is process-local, and whose trait surface carries no enumeration for the API's list/verify verbs). Real-client evidence: the four verbs (create/list/verify/unlock) over the batch door's auth posture (Basic tolerance; anonymous rides nothing — locking is a writer's op); a locked-by-another push refused before the batch (no transfer, no residue); the unlock- then-push riding the pre-keyed custody unchanged; sqlite contention measured (push median ~1.3–1.6x under concurrent lock traffic, zero storage failures — the busy windows absorb).
Design Decisions
| ADR | Decision | Summary |
|---|---|---|
| 022 | LFS keying + door + liveness | the decisions this doc details |
| 019 | GC wiring | the sweep chain the liveness rows feed |
| 021 | 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.locksAPI) — surface build deferred on a client requirement (lfs.locksverify falseis the verified workaround). The composition-validation half resolved 2026-10-24: the POC evidenced the plain-row mapping and the verify-then-transfer composition (see the Lock server constraint note); 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 (the decision record — read this first), substrate.md (the composition), backend.md (seam + repo grammar), 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 — seedocs/research/reference-policy.md)