Resolves review 001 finding A-1 (critical): ADR-012 §3's "scope
git:admin OR ownership" gate is not expressible in alkcall's
AccessControl (AND-composition). Resolution is the review's option (a)
shape with the OR-term generalized: the per-repo grant action set gains
manage, authorize(record, identity, read|write|manage) becomes the
single policy function for git access and repo administration, and the
delete/update/get gate is admin scope OR manage grant (handler-side,
generic FORBIDDEN, unknown-repo = unauthorized per ADR-008). Repo
create seeds the creator's {read, write, manage} grants —
administration is grantable, so collaborators/bots/app-compiled roles
work without global scopes. Ownership stays as alkcall spawn-tracking
(mint at create unchanged); "ownership never implies git access" is
superseded.
- ADR-015 (new): manage grant tier, op gate, flat-grants-as-replication-
substrate, opaque grant-key rule
- ADR-011: action set + policy domain amended, references updated
- ADR-012 §3: gate table replaced, two-tier paragraph superseded
- backend.md/doors.md/overview.md: gate + grant restatements, ADR tables
- OQ-16 (new, deferred(scope)): grant-key identity namespace —
globally-comparable ids for cross-assembly/replicator grant state;
tracker task tasks/architecture/oq-16-grant-identity-namespace.md
- review 001: A-1 marked resolved (ADR-015)
- vision.md: supersession notes (Internal-ops framing, v1 grant set)
Verification: cargo test, clippy -D warnings, fmt --check, doc --no-deps
all clean.
115 lines
6.7 KiB
Markdown
115 lines
6.7 KiB
Markdown
---
|
|
status: draft
|
|
last_updated: 2026-09-25
|
|
---
|
|
|
|
# Overview: alkgit
|
|
|
|
## Purpose
|
|
|
|
alkgit is the git payload service of the alk family: a pure protocol crate
|
|
(per ADR-010, following the alktty/alktunnels template) implementing the
|
|
git smart protocol over alkcall channels — the `alk/git` ALPN. It provides
|
|
repository storage as backend traits (with a feature-gated gitoxide
|
|
implementation), the git smart protocol as producer/consumer halves, and
|
|
nothing else: no binary, no front doors. The original framing of this repo
|
|
(a monorepo with an `alkgitd` binary and its own http/ssh crates) was an
|
|
init-agent artifact corrected by OQ-09/ADR-010; `docs/research/vision.md`
|
|
is amended accordingly.
|
|
|
|
## The one-line architecture
|
|
|
|
**One protocol crate: producer + consumer + backend traits, gix behind a
|
|
feature.** Doors (alkhttp, alkssh, alknet) expose it; assembly belongs to
|
|
downstream consumers.
|
|
|
|
## Crate map
|
|
|
|
Single crate `alkgit`:
|
|
|
|
| Half | Contents | POC evidence |
|
|
|---|---|---|
|
|
| Producer | `GitAdapter` (`alk/git` ALPN via alkcall `ProtocolHandler`), channels `register_openable` (repo id in open-op params — the negotiation + ACL point) | POC-1 verbatim |
|
|
| Consumer | `GitSession` typed client (`connect_direct`, `open_via_channels`) — the replication/mirroring primitive for alknet | new, small (TtySession analog) |
|
|
| Substrate | duplex session + stateless request/response layer (ADR-005); wire framing, V2 state machines (ADR-003) | POC-1, POC-3 |
|
|
| Backends | `GitRegistry` (+ write supertrait), `GitRefs`, `GitPackGen`, `GitPackIngest` traits; impls behind the default-on `gix` (engine) and `registry-file` (records) features | POC-2 (gix impl) |
|
|
| Management ops | `git/repo/*` call ops over `GitRegistryStore` (ADR-012 §3) — the JSON half alongside the `alk/git` open op (first dual-kind payload; ADR-012 §5) | thin over the store trait |
|
|
|
|
Feature model: `gix` (engine impls) and `registry-file` (record store +
|
|
ops) are independent default-on seams (ADR-012 §4);
|
|
`default-features = false` gives the wire/protocol layer without either
|
|
(wasm-clean as a side effect, not a goal); the `sha256`
|
|
passthrough and (eventually, in alkhttp) the `git` door feature ride the
|
|
same pattern. Doors live in the door crates — see [doors.md](doors.md).
|
|
|
|
## Security invariants (spec-level, carried from vision/principles)
|
|
|
|
1. **Authenticated by default** — anonymous fetch only on explicitly-
|
|
public repos; push always authenticated.
|
|
2. **Visible-surface = authorized-surface** — alkcall ACL end-to-end;
|
|
internal ops never wire-callable.
|
|
3. **ACL before advertisement** — nothing is emitted before the check
|
|
(ADR-007); the channels open-op carrying the repo id is the natural
|
|
enforcement point on the native path.
|
|
4. **Registry-resolved repo identity** — wire names are ids, never paths
|
|
(ADR-008).
|
|
5. **No secret material on the wire or at rest outside alkvault** —
|
|
metadata holds vault references; no env-var credential reads. (In v1
|
|
alkgit's metadata holds no credential-shaped material at all, so
|
|
nothing is vault-placed — ADR-011 §5.)
|
|
6. **No shelling out to `git`** — pure Rust on gix primitives.
|
|
7. **Bounded resources** — every session carries `Limits` (ADR-009).
|
|
8. **Honest capability advertisement** — advertise exactly what we serve
|
|
(ADR-003).
|
|
|
|
## What is already validated (POC-backed)
|
|
|
|
- pkt-line over alkcall `BiStream` end-to-end (POC-1 → producer half).
|
|
- Pack generation pipeline streaming with O(counts) memory (POC-2 → gix
|
|
backend impl).
|
|
- Smart-http streaming both ways (POC-3 → the stateless substrate that
|
|
alkhttp's future `git` feature maps onto; `docs/research/poc3-findings.md`
|
|
§alkhttp fit is the mounting reference).
|
|
The full V2 fetch path against real git 2.43 is proven; receive-pack and
|
|
multi-round negotiation are design-complete against real-client captures
|
|
(ADR-013, ADR-014 — `push-captures.md`, `negotiation-captures.md`).
|
|
|
|
## Design Decisions
|
|
|
|
| ADR | Decision | Summary |
|
|
|---|---|---|
|
|
| [001](decisions/001-crate-decomposition.md) | Crate decomposition | **superseded by ADR-010** |
|
|
| [002](decisions/002-front-door-blind-core.md) | Session boundary | (identity, repo, stream, limits) — unchanged, load-bearing |
|
|
| [003](decisions/003-protocol-v2-first.md) | V2-first protocol | V2-only fetch both doors; push is V0-framed by upstream design (ADR-013); honest advertisement |
|
|
| [004](decisions/004-pack-pipeline.md) | Pack pipeline | `data::output` gen / `data::input` ingestion |
|
|
| [005](decisions/005-session-substrate-types.md) | Substrate types | duplex + stateless APIs over one state machine |
|
|
| [006](decisions/006-http-adapter-composition.md) | HTTP adapter composition | **superseded by ADR-010** (mounting → alkhttp feature) |
|
|
| [007](decisions/007-acl-before-advertisement.md) | ACL first | no ref/capability line before ACL passes |
|
|
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry IDs |
|
|
| [009](decisions/009-bounded-resources-budget.md) | Budgets | every session carries limits |
|
|
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | single crate, producer/consumer halves, no doors/binary |
|
|
| [011](decisions/011-per-repo-authorization.md) | Per-repo authorization | grants in repo records, policy in core, vault-nil (amended: ADR-015) |
|
|
| [012](decisions/012-registry-backing-and-ops.md) | Registry + ops | read/write split, file default, CRUD ops, feature split |
|
|
| [013](decisions/013-receive-pack-state-machine.md) | receive-pack | V0-framed push machine, thin-pack acceptance, unpack-first CAS |
|
|
| [014](decisions/014-v2-negotiation-ack-loop.md) | Negotiation | ack loop, `common_haves` seam, no `ready` |
|
|
| [015](decisions/015-manage-grant-and-op-gate.md) | Manage grant + op gate | `manage` tier, admin-OR-manage gate, create seeds manage |
|
|
| [015](decisions/015-manage-grant-and-op-gate.md) | Manage grant + op gate | `manage` tier, admin-OR-manage gate, create seeds manage |
|
|
|
|
## Open Questions
|
|
|
|
Key questions tracked in [open-questions.md](open-questions.md):
|
|
|
|
- **OQ-03**: publish-time API freeze inventory (the `git/repo/*` op set
|
|
and the trait family enter it; ADR-012, ADR-013/014's trait additions;
|
|
the ADR-015 three-action grant shape must land in it).
|
|
- **OQ-05**: sha256 policy (deferred(scope), low).
|
|
- **OQ-16**: grant-key identity namespace (deferred(scope); blocks
|
|
nothing in v1 — ADR-015 §7).
|
|
|
|
Resolved this cycle: OQ-04 (ADR-013 — receive-pack state machine,
|
|
capture-grounded), OQ-02 (ADR-014 — negotiation ack loop). Earlier:
|
|
OQ-08 (ADR-011 — per-repo authorization, grants in records, vault-nil),
|
|
OQ-06 (ADR-012 — `registry-file` default, persistence adapters
|
|
additive), OQ-07 (ADR-012 — CRUD ops shipped External with scope+grant
|
|
ACL, gate amended by ADR-015), OQ-09/OQ-01 (ADR-010 — pure protocol
|
|
crate). |