docs(architecture): phase 1 bootstrap — specs, 9 ADRs, OQ tracker
Architecture documentation structure per sdd_process phase 1: - README index (doc table, ADR table, lifecycle), overview with crate map, dependency rules, and security invariants - Component specs: storage, transport, http, ssh, alkgitd (all draft) - ADRs 001-009: crate decomposition, front-door-blind core, V2-first protocol, pack pipeline (data::output generation / data::input ingestion), session substrate types, http adapter composition (proposed, OQ-01), ACL-before-advertisement, registry-resolved repo identity, bounded-resources budgets - open-questions.md: OQ-01..08 with two deferred(scope), one deferred(unclear), door-type definitions, blocker tracker tasks in tasks/architecture/ - v1 ssh-door decision recorded: russh terminates wire SSH in alkgitd; alkcall channels stay the internal substrate (OQ-03 partially resolved) Two review rounds (fresh-context subagent): 4 critical + 17 warnings fixed in round one; zero critical + 4 warnings + 5 suggestions fixed in round two. All ADR/OQ cross-references verified resolving.
This commit is contained in:
1 parent
70815e22a0
commit
8f73da5d12
21 files changed
+1669
No files matched your search
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
status: draft
|
||||||
|
last_updated: 2026-09-21
|
||||||
|
---
|
||||||
|
|
||||||
|
# alkgit Architecture
|
||||||
|
|
||||||
|
Phase 1 (SDD) output for alkgit — the self-hosted, single-binary git server
|
||||||
|
built on the alk stack (alkcall, alkhttp, alktls, alkvault) and gitoxide.
|
||||||
|
Phase 0 research lives in [docs/research/](../research/README.md); every
|
||||||
|
design claim here traces to a POC finding or research doc, or is flagged as
|
||||||
|
an open question.
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
Phase 1 is starting. All architecture documents below are `draft`
|
||||||
|
(ADR-006 additionally carries a Proposed ADR status pending OQ-01).
|
||||||
|
POC-1/2/3 validated the git protocol half end-to-end against real git 2.43;
|
||||||
|
the remaining design work is shape work (adapter composability, metadata/
|
||||||
|
registry backing, admin surface, receive-pack).
|
||||||
|
|
||||||
|
## Architecture Documents
|
||||||
|
|
||||||
|
| Doc | Area | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| [overview.md](overview.md) | Cross-cutting: crate map, dependency rules, security invariants | draft |
|
||||||
|
| [storage.md](storage.md) | `alkgit-core`: registry, refs, odb, pack generate/ingest, ACL types | draft |
|
||||||
|
| [transport.md](transport.md) | `alkgit-transport`: pkt-line sessions, V2 state machine, upload/receive-pack | draft |
|
||||||
|
| [http.md](http.md) | `alkgit-http`: smart-http adapter over alkhttp | draft |
|
||||||
|
| [ssh.md](ssh.md) | `alkgit-ssh`: git-command dispatch (wire SSH terminated by russh in alkgitd) | draft |
|
||||||
|
| [alkgitd.md](alkgitd.md) | `alkgitd`: binary assembly, config, TLS/ACME, serving loops | draft |
|
||||||
|
| [open-questions.md](open-questions.md) | Centralized OQ tracker | — |
|
||||||
|
|
||||||
|
## ADRs
|
||||||
|
|
||||||
|
| ADR | Decision | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| [001](decisions/001-crate-decomposition.md) | Workspace crate decomposition (5 crates) | Accepted |
|
||||||
|
| [002](decisions/002-front-door-blind-core.md) | Front-door-blind core: session boundary (identity, repo, stream, limits) | Accepted |
|
||||||
|
| [003](decisions/003-protocol-v2-first.md) | Protocol V2-first with honest capability advertisement | Accepted |
|
||||||
|
| [004](decisions/004-pack-pipeline.md) | Pack generation/ingestion pipeline (gitoxide `data::output`) | Accepted |
|
||||||
|
| [005](decisions/005-session-substrate-types.md) | Session substrate types (duplex + stateless APIs, request reader) | Accepted |
|
||||||
|
| [006](decisions/006-http-adapter-composition.md) | HTTP adapter composition (alkgit-owned router factory) | Proposed |
|
||||||
|
| [007](decisions/007-acl-before-advertisement.md) | ACL runs before any advertisement/ref line | Accepted |
|
||||||
|
| [008](decisions/008-registry-resolved-repo-identity.md) | Wire repo names are registry IDs, never paths | Accepted |
|
||||||
|
| [009](decisions/009-bounded-resources-budget.md) | Bounded-resources budget model (limits on every session) | Accepted |
|
||||||
|
|
||||||
|
Note: ADR-001's crate table and ssh.md record the v1 ssh-door decision
|
||||||
|
(russh terminates wire SSH in alkgitd; OQ-03 covers embedder variants).
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
All unresolved questions are tracked in [open-questions.md](open-questions.md)
|
||||||
|
with stable OQ-IDs, priorities, and cross-references. Highest-priority opens:
|
||||||
|
OQ-01 (http adapter home / composability, blocks ADR-006 finalization),
|
||||||
|
OQ-06 (metadata store backing), OQ-08 (identity sources per front door).
|
||||||
|
|
||||||
|
## Document Lifecycle
|
||||||
|
|
||||||
|
| Status | Meaning | Transitions |
|
||||||
|
|---|---|---|
|
||||||
|
| `draft` | Under active development; may change significantly | → `reviewed` when its OQs are resolved |
|
||||||
|
| `reviewed` | Architecture final; implementation may begin; changes need review | → `stable` when implementation verified |
|
||||||
|
| `stable` | Locked; changes require review, may warrant an ADR | → `deprecated` when superseded |
|
||||||
|
| `deprecated` | Superseded; kept for reference | Removed when no longer referenced |
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
status: draft
|
||||||
|
last_updated: 2026-09-21
|
||||||
|
---
|
||||||
|
|
||||||
|
# alkgitd: Server Binary
|
||||||
|
|
||||||
|
## What it is
|
||||||
|
|
||||||
|
The assembly point: config parsing, listener setup (TLS/ACME), vault
|
||||||
|
wiring, the admin surface, and the glue that turns config into running
|
||||||
|
adapters. The only crate that knows the whole graph (ADR-001). No business
|
||||||
|
logic lives here.
|
||||||
|
|
||||||
|
## Assembly shape
|
||||||
|
|
||||||
|
For each listener (http and/or ssh), the binary:
|
||||||
|
|
||||||
|
1. Builds the identity provider (per-door; **OQ-08** decides mechanics).
|
||||||
|
2. Builds the registry (core trait + chosen backing; **OQ-06** decides
|
||||||
|
backing) and the storage roots from config.
|
||||||
|
3. Builds the transport hooks + `Limits` from config (ADR-009 defaults +
|
||||||
|
overrides).
|
||||||
|
4. http door: constructs the alkgit-http router factory (ADR-006) and
|
||||||
|
merges via `HttpAdapter::with_extra_routes`; wraps in alktls per
|
||||||
|
config; `Arc<HttpAdapter>` per accept task (POC-3 usage note).
|
||||||
|
5. ssh door: russh listener terminates SSH; alkgit-ssh dispatches the
|
||||||
|
exec requests (ssh.md §client compatibility).
|
||||||
|
6. Vault (alkvault) wired for any credential material; metadata/config
|
||||||
|
hold vault references only (convention 4/5).
|
||||||
|
|
||||||
|
## Config schema (v1 shape)
|
||||||
|
|
||||||
|
Server-facing values (registry backing location, storage roots, per-door
|
||||||
|
listeners/ports, TLS/ACME, limits overrides, vault path). The schema's
|
||||||
|
final shape depends on **OQ-06** (backing) and **OQ-08** (identity
|
||||||
|
config); the skeleton above is the stable frame.
|
||||||
|
|
||||||
|
Feature flags (convention 10): `sha256` (passthrough), `acme` (alktls
|
||||||
|
wiring). Base binary compiles lean; both feature sets verified in CI
|
||||||
|
(AGENTS.md verification commands).
|
||||||
|
|
||||||
|
## Admin API (scope: OQ-07)
|
||||||
|
|
||||||
|
- alkcall ops with `Visibility::Internal`, served over an admin-only
|
||||||
|
listener — never on the git traffic surface (alk-stack.md §gitea
|
||||||
|
lesson; vision threat-model notes).
|
||||||
|
- Candidate v1 op set (repo create/delete, visibility, ACL grant/revoke)
|
||||||
|
and its exact shapes: **OQ-07** — a dedicated session once the identity
|
||||||
|
model (OQ-08) exists, since op shapes depend on what identities mean.
|
||||||
|
- A downstream app may replace this surface entirely (vision
|
||||||
|
composability): the ops are alkcall ops, not embedded web endpoints.
|
||||||
|
|
||||||
|
## TLS/ACME
|
||||||
|
|
||||||
|
- alktls composition: `Connection::from_bidi(TlsStream, alpn)` — the
|
||||||
|
POC-1/3-validated alkcall entry shape. The http door rides TLS with
|
||||||
|
ALPN `http/1.1`/`h2`. The ssh door is a plain TCP listener
|
||||||
|
(stock `git clone ssh://` clients do not do TLS) — russh terminates SSH
|
||||||
|
directly on it (ssh.md); alkcall `Connection::from_stream` wraps the
|
||||||
|
post-auth stream when the adapter needs alkcall types.
|
||||||
|
- `acme` feature: alktls ACME state machine wired to config for the http
|
||||||
|
listener (alk-stack.md lists this as "likely trivial"; risk note: if the
|
||||||
|
wiring surprises, that becomes a research task before alkgitd
|
||||||
|
implementation — not silently absorbed).
|
||||||
|
|
||||||
|
## Serving loops
|
||||||
|
|
||||||
|
- Per-connection accept → `ProtocolHandler::handle` (alkcall ADR-002)
|
||||||
|
→ adapter dispatch → transport session.
|
||||||
|
- Server-wide blocking-pool budget for pack generation (ADR-009):
|
||||||
|
accepted/rejected at assembly time.
|
||||||
|
- Graceful shutdown: listener stop + session drain bounded by the
|
||||||
|
session wall clock (ADR-009).
|
||||||
|
|
||||||
|
## Design Decisions
|
||||||
|
|
||||||
|
| ADR | Decision | Summary |
|
||||||
|
|---|---|---|
|
||||||
|
| [001](decisions/001-crate-decomposition.md) | Crate decomposition | alkgitd = assembly only |
|
||||||
|
| [006](decisions/006-http-adapter-composition.md) | Router factory | alkgitd consumes the factory like any embedder |
|
||||||
|
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | storage roots configured server-side here |
|
||||||
|
| [009](decisions/009-bounded-resources-budget.md) | Budgets | limits overrides live in config |
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
- **OQ-06**: registry backing (deferred(scope)).
|
||||||
|
- **OQ-07**: admin op set (open — after OQ-08).
|
||||||
|
- **OQ-08**: identity providers per door (open).
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `docs/research/alk-stack.md` (integration surface, version pins)
|
||||||
|
- `docs/research/poc3-findings.md` (HttpAdapter usage, extra-routes behavior)
|
||||||
|
- alkcall ADRs: 002 (ProtocolHandler), 010 (capability injection), 017
|
||||||
|
(privilege model)
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# ADR-001: Workspace crate decomposition (5 crates)
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The workspace skeleton (created before phase 1) proposed five crates:
|
||||||
|
`alkgit-core`, `alkgit-transport`, `alkgit-http`, `alkgit-ssh`, `alkgitd`.
|
||||||
|
The decomposition has to serve two masters: the "ALPN as a service"
|
||||||
|
composability rule from `docs/research/vision.md` (downstream apps embed
|
||||||
|
core + transport and bring their own front doors) and the POC findings that
|
||||||
|
fixed the actual layering between protocol, storage, and front doors
|
||||||
|
(POC-1/2/3).
|
||||||
|
|
||||||
|
Alternatives considered:
|
||||||
|
|
||||||
|
- Single crate — simplest, but forces front-door code into the same
|
||||||
|
dependency graph as embedders who only want core+transport, and blocks
|
||||||
|
independent evolution of the http adapter.
|
||||||
|
- Split core into registry/metadata vs storage — premature; the registry
|
||||||
|
storage backing is still an open question (OQ-06) and the split would
|
||||||
|
bake in a boundary we may want to move.
|
||||||
|
- http+ssh in one `alkgit-frontends` crate — reduces crate count but
|
||||||
|
couples alkhttp to alkcall-channels consumers and vice versa.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Keep the five-crate decomposition from the skeleton:
|
||||||
|
|
||||||
|
- `alkgit-core` — repository storage: registry, refs, odb wrappers, pack
|
||||||
|
generate/ingest, fsck, access-rule input types. Transport-agnostic.
|
||||||
|
- `alkgit-transport` — smart protocol: pkt-line sessions, V2 capability
|
||||||
|
advertisement, ls-refs, fetch, receive-pack state machines.
|
||||||
|
- `alkgit-http` — http front door (smart-http over alkhttp).
|
||||||
|
- `alkgit-ssh` — ssh front door (git command exec dispatch; wire SSH
|
||||||
|
terminated by russh in alkgitd, or alkcall channels in embedded
|
||||||
|
variants — both converge on the adapter's dispatch).
|
||||||
|
- `alkgitd` — the binary: config, assembly, TLS/ACME, serving loops.
|
||||||
|
|
||||||
|
Dependency edges: core ← transport ← {http, ssh} ← alkgitd. http and ssh
|
||||||
|
have no edge between them. Core and transport depend on alkcall types only
|
||||||
|
(never alkhttp/alkgit-ssh); see ADR-002 for the exact boundary.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Downstream embedders take `alkgit-core` + `alkgit-transport` and stop
|
||||||
|
there; front-door crates are replaceable adapters.
|
||||||
|
- The binary crate carries all assembly knowledge (config schema, listener
|
||||||
|
setup, vault wiring), keeping the library crates front-door-blind.
|
||||||
|
- Crate count stays at five; any future split (e.g. metadata store) is a
|
||||||
|
new ADR.
|
||||||
|
|
||||||
|
## References
|
||||||
|
- `docs/research/vision.md` §"ALPN as a service", §"Sub-crate shape"
|
||||||
|
- `docs/research/alk-stack.md` §"Composability boundary"
|
||||||
|
- POC findings 1–3 (layering validated end-to-end)
|
||||||
|
- ADR-002 (the core boundary)
|
||||||
|
- overview.md §"Crate map"
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# ADR-002: Front-door-blind core — the session boundary
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The load-bearing composability rule ("ALPN as a service",
|
||||||
|
`docs/research/vision.md`): `alkgit-core` + `alkgit-transport` must never
|
||||||
|
know which front door is talking. POC-1 proved the exact shape survives the
|
||||||
|
wire: `Connection::accept_bi() → BiStream → tokio::io::split →
|
||||||
|
tokio-util compat → gix-packetline` ran a full V2 fetch against real git.
|
||||||
|
POC-3 proved the stateless-http variant: the same protocol state machines
|
||||||
|
ran per-POST over (request-reader, response-writer) instead of a duplex
|
||||||
|
stream.
|
||||||
|
|
||||||
|
The question this ADR settles: what is the *interface* the core/transport
|
||||||
|
exposes to adapters?
|
||||||
|
|
||||||
|
Alternatives considered:
|
||||||
|
|
||||||
|
- Adapters implement an alkcall-style `ProtocolHandler` themselves and call
|
||||||
|
into transport with raw `BiStream` — pushes too much protocol
|
||||||
|
responsibility (advertisement timing, framing errors) into every adapter.
|
||||||
|
- Transport speaks alkcall `Connection` directly — forces stateless http
|
||||||
|
(one request per connection) through a session-shaped API; wrong shape
|
||||||
|
for http, and couples transport to alkcall connection lifecycle.
|
||||||
|
- HTTP-specific abstractions in transport — violates the blind-core rule.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
The transport exposes two session entry points, both consuming the same
|
||||||
|
tuple (peer identity, resolved repo, limits):
|
||||||
|
|
||||||
|
1. **Duplex session** (ssh, git://, any stream door): transport consumes a
|
||||||
|
`BiStream`-shaped duplex byte stream (`AsyncRead + AsyncWrite + Unpin`,
|
||||||
|
the alkcall `BiStream` contract, alkcall ADR-005/009) and runs the
|
||||||
|
advertise-once → command-loop state machine. Adapters hand it over after
|
||||||
|
ACL and repo resolution (ADR-007, ADR-008).
|
||||||
|
2. **Stateless session** (smart-http): transport consumes a
|
||||||
|
(request-reader, response-writer) pair per http request and runs one
|
||||||
|
command per invocation, matching smart-http's stateless framing. The
|
||||||
|
same core state machines run under both entry points.
|
||||||
|
|
||||||
|
Storage-facing side: transport calls `alkgit-core` for advertisement data,
|
||||||
|
pack generation (want/have set in → streaming pack out), and pack
|
||||||
|
ingestion + ref CAS (receive-pack). Storage never sees pkt-lines.
|
||||||
|
|
||||||
|
The `futures-io` bridging detail (`tokio_util::compat`), the split/compat
|
||||||
|
glue, and the packetline stop-delimiter handling live inside transport —
|
||||||
|
adapters never see `futures_io` types (POC-1 follow-ups 1–2, POC-3
|
||||||
|
follow-up 2).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- The core+transport pair is embeddable with any front door; POC-1/3 are
|
||||||
|
existence proofs of both shapes.
|
||||||
|
- State machines are written once; http statelessness is a substrate
|
||||||
|
property, not a protocol fork.
|
||||||
|
- Transport depends on alkcall types only (`BiStream` shape, identity,
|
||||||
|
limits) — no alkhttp, no channel types below the stream.
|
||||||
|
- One cost: the stateless entry point needs explicit per-request limits
|
||||||
|
(body budget, wall clock) since there is no session to amortize them
|
||||||
|
(ADR-009).
|
||||||
|
|
||||||
|
## References
|
||||||
|
- `docs/research/vision.md` §"ALPN as a service"
|
||||||
|
- `docs/research/poc-1-findings.md` (duplex shape validated), `docs/research/poc3-findings.md`
|
||||||
|
(stateless shape validated)
|
||||||
|
- alkcall ADR-005 (`BiStream` type), ADR-009 (BiStream as handler leaf)
|
||||||
|
- ADR-005 (substrate types detail), ADR-007/008 (what adapters do before
|
||||||
|
calling transport)
|
||||||
|
- overview.md §"Interfaces"
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# ADR-003: Protocol V2-first with honest capability advertisement
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The git smart protocol has three versions in the wild: V0 (the original),
|
||||||
|
V1 (capability line prefixed), and V2 (command-based, default since git
|
||||||
|
2.18+). POC-1 and POC-2 validated the full V2 fetch path against real git
|
||||||
|
2.43 end-to-end (advertisement, ls-refs, fetch with sideband packs) over
|
||||||
|
both duplex and http substrates. V0/V1 stateless-http is a different
|
||||||
|
framing (per-round POSTs, stateless ack re-derivation).
|
||||||
|
|
||||||
|
The research inventory (`git-protocol.md` §"Open items") left the V0/V1
|
||||||
|
question open: support V0/V1 on http? On ssh? Which first?
|
||||||
|
|
||||||
|
Decision drivers:
|
||||||
|
|
||||||
|
- Our deployment anchor (vision §"Primary deployment target") is modern
|
||||||
|
git clients over http and ssh; every git client from the last six years
|
||||||
|
speaks V2.
|
||||||
|
- Honest capability advertisement (vision principle 5) is easiest to keep
|
||||||
|
honest with fewer served shapes.
|
||||||
|
- V0/V1 stateless-http needs multi-round negotiation state re-derivation —
|
||||||
|
meaningful complexity for a client population that is effectively
|
||||||
|
extinct.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**Serve protocol V2 first.** alkgit v1 serves V2 on both doors:
|
||||||
|
|
||||||
|
- Advertisement is once-per-session (duplex) / per-request-set (http),
|
||||||
|
exactly as observed in POC-1 (re-advertising between commands hangs real
|
||||||
|
git).
|
||||||
|
- Advertised capabilities are exactly what we serve: `ls-refs=unborn`,
|
||||||
|
`fetch=wait-for-done` (v1 fetch policy is full-closure pack on `done`;
|
||||||
|
multi-round negotiation, OQ-02, may add capability values when it
|
||||||
|
lands), `object-format=sha1` (+ sha256 under the feature flag, if and
|
||||||
|
when tested — OQ-05). Nothing unimplemented is advertised
|
||||||
|
(shallow, filter, packfile-uris, object-info, server-option are
|
||||||
|
*declined by omission*; POC-1 confirmed real git accepts this).
|
||||||
|
On the push side, `git-upload-archive` is not served at all — ssh
|
||||||
|
exec requests for it get a fixed refusal (ssh.md; ADR-008's
|
||||||
|
never-execute rule).
|
||||||
|
- HTTP requests protocol V2 only; ssh requests protocol V2 only (see
|
||||||
|
below).
|
||||||
|
|
||||||
|
**V0/V1 policy: decided now as V2-only for v1.** Both doors
|
||||||
|
(http and ssh) speak V2; clients that cannot speak V2 get a clear pkt-line
|
||||||
|
error. Reversal is wire-visible (the advertised version set is a wire
|
||||||
|
format), so treat this as effectively one-way once published — the
|
||||||
|
decision is still made now, and revisiting it needs a new ADR plus a
|
||||||
|
deprecation window for clients. The motivation stands: a concrete consumer
|
||||||
|
needing V0/V1 (e.g. very old CI images) has not been identified.
|
||||||
|
The V0/V1 receive-pack shape (used by `git push` on http stateless framing
|
||||||
|
in older clients) is unaffected by this choice: receive-pack is mostly
|
||||||
|
version-independent (see transport.md).
|
||||||
|
|
||||||
|
Sequencing note (fixes the scope contradiction the review found): the v1
|
||||||
|
fetch policy that ships first is full-closure-on-`done` (POC-validated).
|
||||||
|
Multi-round V2 negotiation (haves/acks without `done`) is in v1's *scope*
|
||||||
|
but lands after the done-path works end-to-end; its ack logic is OQ-02's
|
||||||
|
open question, and any capability-advertisement change it requires
|
||||||
|
happens then.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Smallest honest surface; POC-validated shapes only.
|
||||||
|
- Old-client support is declined explicitly (error, not silence).
|
||||||
|
- The POC's observed `done`-path shape (no acknowledgments section, no
|
||||||
|
`ready`, response ends at flush) is our normative wire behavior, not the
|
||||||
|
docs' grammar — `git-protocol.md` §V2 records this.
|
||||||
|
- If V0/V1 is ever added, the state machines in transport must be
|
||||||
|
re-shaped around a version-dispatch at session start (single point, by
|
||||||
|
design — see ADR-005).
|
||||||
|
|
||||||
|
## References
|
||||||
|
- `docs/research/git-protocol.md` §"Protocol surface inventory", §"Negotiation policy"
|
||||||
|
- `docs/research/poc-1-findings.md` (advertisement-once, capability declination), POC-3 (http V2 framing)
|
||||||
|
- ADR-005 (substrate types), ADR-007 (ACL before advertisement)
|
||||||
|
- transport.md, http.md, ssh.md
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# ADR-004: Pack pipeline — gitoxide `data::output` generation, `data::input` streaming ingestion
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Pack generation and ingestion are the two halves of fetch and receive-pack.
|
||||||
|
POC-2 resolved the generation question empirically; the ingestion tool
|
||||||
|
turned out to be mislabeled in the original research plan (the plan called
|
||||||
|
it `bundle::write` — the correct tool is `data::input` streaming) and was
|
||||||
|
corrected there.
|
||||||
|
|
||||||
|
Generation options were:
|
||||||
|
|
||||||
|
a. `gix-pack::bundle::write` into a temp dir, read back — **wrong tool**:
|
||||||
|
it is the index-from-stream machinery (consumes an *existing* pack
|
||||||
|
stream, mmaps to resolve deltas, always writes files). Not a generation
|
||||||
|
path.
|
||||||
|
b. `gix-pack::data::output::bytes` fed by an odb walk — validated live:
|
||||||
|
streams to any `io::Write` with O(counts) memory (60k-object pack →
|
||||||
|
5.3 MB out, ~11 MB extra RSS), real git 2.43 clones fsck-clean.
|
||||||
|
|
||||||
|
Ingestion (receive-pack): client sends a possibly-thin pack stream; server
|
||||||
|
must index it, fsck it, and apply ref updates via CAS.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**Fetch-side generation** is gitoxide's own pipeline, composed exactly as
|
||||||
|
`gitoxide-core/src/pack/create.rs` does (POC-2's validated composition):
|
||||||
|
|
||||||
|
```
|
||||||
|
peel wants to commit tips
|
||||||
|
→ commit-ancestry walk (gix_traverse::commit::Simple, Parents::All)
|
||||||
|
[+ non-commit tips as-is]
|
||||||
|
→ count::objects(_unthreaded, TreeContents) [per-commit tree expansion]
|
||||||
|
→ entry::iter_from_counts [chunked deflate + pack-delta copy]
|
||||||
|
→ InOrderIter → bytes::FromEntriesIter (V2, sha1)
|
||||||
|
→ io::Write sink (sideband on the wire, or http response body)
|
||||||
|
```
|
||||||
|
|
||||||
|
Two-stage closure is mandatory: `TreeContents` does **not** follow commit
|
||||||
|
parents; feeding tip commits alone produces incomplete packs (POC-2's
|
||||||
|
course correction). The odb handle needs `prevent_pack_unload()` +
|
||||||
|
`ignore_replacements = true`. `gix_odb::Cache` is not `Sync`: share
|
||||||
|
`Arc<Store>`, build a handle per session, generate on blocking threads.
|
||||||
|
|
||||||
|
Missing objects mid-generation must **abort** the fetch (sideband error
|
||||||
|
band), never emit a broken pack — check entry statistics
|
||||||
|
(`missing_objects > 0`).
|
||||||
|
|
||||||
|
**Receive-side ingestion** parses the client pack stream
|
||||||
|
(`gix-pack::data::input` with `streaming-input`), fscks it
|
||||||
|
(`gix-fsck` connectivity), and applies ref updates via `gix-ref`
|
||||||
|
transaction CAS. The exact push state machine (capability advertisement
|
||||||
|
set, CAS timing, status report, http framing) is OQ-04's investigation
|
||||||
|
target; the ingestion-tool choice above is decided.
|
||||||
|
|
||||||
|
**Delta synthesis** for loose objects is an optimization backlog item, not
|
||||||
|
a v1 commitment: existing pack deltas copy through for free; loose objects
|
||||||
|
ship as compressed bases. No upstream delta-encode API exists
|
||||||
|
(`gix-delta` is apply-only).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Fresh clones of loose-ish repos ship uncompressed bases (fine for v1;
|
||||||
|
clients re-pack at rest); repos kept packed get pack-copy efficiency.
|
||||||
|
- Memory stays O(counts); streaming under back pressure is proven on the
|
||||||
|
http path (POC-3: flat RSS under a 650 KB/s reader).
|
||||||
|
- Determinism: `objects_unthreaded` gives deterministic order; threaded
|
||||||
|
count + `InOrderIter` is the scale path later.
|
||||||
|
- `alkgit-core` exposes this as a type taking (odb handle, wants, haves) →
|
||||||
|
streaming pack; negotiation-agnostic (POC-2 follow-up 1).
|
||||||
|
|
||||||
|
## References
|
||||||
|
- `docs/research/poc2-findings.md` (the whole basis), `docs/research/poc3-findings.md` (streaming proof)
|
||||||
|
- `docs/research/gitoxide.md` §"Storage" (generation-pipeline notes)
|
||||||
|
- `docs/research/git-protocol.md` §"Server-side pack generation"
|
||||||
|
- ADR-005 (how the sink reaches the wire), ADR-009 (memory/time budgets)
|
||||||
|
- transport.md §fetch, storage.md §packs
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# ADR-005: Session substrate types — duplex + stateless over one state machine
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
POC-1 and POC-3 found recurring glue that must not be re-implemented per
|
||||||
|
adapter or per handler:
|
||||||
|
|
||||||
|
- The split → tokio-util compat → packetline bridge (POC-1).
|
||||||
|
- `StreamingPeekableIter`'s stop-delimiter contract: flush returns `None`,
|
||||||
|
the iterator is inert until `.reset()`; forgetting reset silently
|
||||||
|
swallows the next command (POC-1's second hang).
|
||||||
|
- The three-layer return type `Option<io::Result<Result<PacketLineRef,
|
||||||
|
decode::Error>>>`.
|
||||||
|
- The EOF-mid-parse error loop: an error-shaped read that resets-and-retries
|
||||||
|
spins a tokio worker at 100% (POC-3 recorded bug class) — error branches
|
||||||
|
must break unconditionally.
|
||||||
|
- The http-side request-body adapter (axum `BodyDataStream` →
|
||||||
|
`futures_io::AsyncRead`, ~40 lines, POC-3).
|
||||||
|
- The smart-http framing facts: first POST carries the capability dump
|
||||||
|
(skip two flush-delimited sections); capabilities before `0001` delim,
|
||||||
|
args after; responses end at flush, never `0002` (remote-curl dies on
|
||||||
|
it); the flush is one-shot per response (idempotent-flush guard is
|
||||||
|
mandatory); a flush-only POST is a probe answered 200-empty.
|
||||||
|
|
||||||
|
If adapters or handlers hand-roll any of this, each gets it subtly wrong.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
`alkgit-transport` owns the substrate layer; adapters and handlers see
|
||||||
|
friendly types:
|
||||||
|
|
||||||
|
1. **`Session` (duplex)** — wraps (stream, limits) into the pkt-line
|
||||||
|
session: owns the split/compat bridge, the request reader (command line
|
||||||
|
+ pre-delim lines + args + flush, with the `reset()` discipline and the
|
||||||
|
unconditional-error-break rule), and the writer (sideband sink with
|
||||||
|
65000-byte chunks, idempotent flush). Seed: POC-1's `serve_v2`.
|
||||||
|
2. **`StatelessRequest` (http)** — wraps (request-reader, response-writer)
|
||||||
|
per POST: performs the capability-dump skip, the delim-aware arg split,
|
||||||
|
and the flush-only response rule (no `0002`, idempotent flush,
|
||||||
|
200-empty for flush-only probes). Seed: POC-3's `httpservice.rs` +
|
||||||
|
`BodyReader`.
|
||||||
|
3. **One protocol core** — the V2 state machines (ADR-003) run under both
|
||||||
|
substrates; the stateless substrate presents each POST as a command
|
||||||
|
invocation. Version dispatch happens at session start, in one place.
|
||||||
|
4. **Error taxonomy** — substrate errors are a transport-level
|
||||||
|
`thiserror` enum; io errors are terminal (session ends), protocol
|
||||||
|
errors map to pkt-line error bands / http status responses per the
|
||||||
|
respective substrate's convention.
|
||||||
|
|
||||||
|
The sideband sink (band-1 chunks ≤ 65000 bytes, blocking-side `io::Write`
|
||||||
|
that parks on a bounded channel on the http path) is part of the substrate:
|
||||||
|
exactly one task drives a given sink (POC-2's `SidebandSink` contract).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Adapters (http, ssh, downstream) cannot get the framing wrong; the
|
||||||
|
POC-hang and spin-bug classes are structurally prevented.
|
||||||
|
- The packetline raw API is confined to transport internals.
|
||||||
|
- State machines are substrate-agnostic, which is what makes smart-http's
|
||||||
|
statelessness cheap (per-request state, no session to restore).
|
||||||
|
|
||||||
|
## References
|
||||||
|
- `docs/research/poc-1-findings.md` §"Answered hypotheses" 3 (packetline contracts),
|
||||||
|
follow-ups 1–2
|
||||||
|
- `docs/research/poc3-findings.md` §"Course corrections" 1, follow-ups 2, 4
|
||||||
|
- `docs/research/gitoxide.md` §"Wire format"
|
||||||
|
- ADR-002 (substrate shapes), ADR-003 (V2-first), ADR-004 (sink = io::Write)
|
||||||
|
- transport.md §substrate
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# ADR-006: HTTP adapter composition — router factory in alkgit-http
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Proposed (recommendation recorded; final call pending user review — OQ-01)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
POC-3 proved that git smart-http runs through alkhttp's
|
||||||
|
`HttpAdapter::with_extra_routes` surface with zero alkhttp changes: an axum
|
||||||
|
`Router` with internal state merges under the gateway's auth layer, request
|
||||||
|
bodies stream, responses stream under back pressure. The question is where
|
||||||
|
that router lives so downstream users can stack git onto their own alkhttp
|
||||||
|
deployment.
|
||||||
|
|
||||||
|
Two candidate shapes:
|
||||||
|
|
||||||
|
**Option A — router factory in `alkgit-http` (this crate).**
|
||||||
|
`alkgit-http` exports a builder that takes (registry, transport hooks,
|
||||||
|
identity-extractor callback, limits) and returns an axum `Router` ready to
|
||||||
|
merge via `with_extra_routes`. Downstream: depend on `alkhttp` +
|
||||||
|
`alkgit-http`, merge one router, wire their own auth into the extractor.
|
||||||
|
|
||||||
|
**Option B — `git` feature on alkhttp with alkgit as an optional
|
||||||
|
dependency.** Downstream enables `alkhttp = { features = ["git"] }` and
|
||||||
|
gets git routes directly.
|
||||||
|
|
||||||
|
Evaluation of Option B:
|
||||||
|
|
||||||
|
- Dependency direction: alkhttp is a published generic sibling (0.5); the
|
||||||
|
git adapter is alkgit's domain. Making alkhttp depend on alkgit inverts
|
||||||
|
the layering — the generic layer would know about the git member of the
|
||||||
|
family, and every alkgit adapter change would require an alkhttp
|
||||||
|
release.
|
||||||
|
- The alkcall ADR-027 precedent (`from-jsonschema-as-http-adapter`) puts
|
||||||
|
call-protocol adapters inside alkhttp, but those are alkcall-op
|
||||||
|
adapters — shared machinery for the protocol alkhttp exists to serve.
|
||||||
|
Git smart-http is a foreign wire protocol (its own content types,
|
||||||
|
framing, streaming shape), not a call adapter.
|
||||||
|
- Composability rule (vision): front doors are replaceable adapters;
|
||||||
|
"ALPN as a service" implies the service family member owns its adapter.
|
||||||
|
- One-dep ergonomics is real but buyable later: alkhttp could gain a
|
||||||
|
convenience feature *re-exporting or wiring alkgit-http* once alkgit is
|
||||||
|
published — that is an alkhttp-side decision that does not constrain
|
||||||
|
alkgit's shape now.
|
||||||
|
|
||||||
|
## Decision (proposed)
|
||||||
|
|
||||||
|
**Option A.** `alkgit-http` owns the smart-http adapter and exposes it as
|
||||||
|
an axum router factory; alkhttp stays git-agnostic and unchanged.
|
||||||
|
|
||||||
|
The factory's seam is the composability surface:
|
||||||
|
|
||||||
|
- Input: the adapter's dependencies as traits/callbacks — peer-identity
|
||||||
|
extraction (the downstream app decides *how* http requests authenticate,
|
||||||
|
OQ-08), the core registry (repo resolution + ACL inputs, ADR-007/008),
|
||||||
|
transport hooks (upload-pack/receive-pack entry points, ADR-002), and
|
||||||
|
`Limits` (ADR-009).
|
||||||
|
- Output: an axum `Router` (state finalized internally) serving exactly
|
||||||
|
`GET /{repo}/info/refs`, `POST /{repo}/git-upload-pack`,
|
||||||
|
`POST /{repo}/git-receive-pack` with the POC-3-validated framing, which
|
||||||
|
the downstream merges via `HttpAdapter::with_extra_routes`.
|
||||||
|
- The route set is small and stable; reserved-path collision checking
|
||||||
|
(POC-3 confirmed it passes for these shapes) stays with alkhttp.
|
||||||
|
- `alkgitd` is the first consumer of the factory (no special privileges);
|
||||||
|
downstream apps are second users of the same seam — this is what makes
|
||||||
|
the adapter genuinely composable rather than binary-only.
|
||||||
|
|
||||||
|
If a concrete downstream later demonstrates that the two-dep + merge
|
||||||
|
ergonomics is a real friction point, the Option-B-style sugar can be added
|
||||||
|
*in alkhttp* without any change here (alkhttp would gain an optional
|
||||||
|
alkgit-http feature re-exporting the factory). Deciding that now is not
|
||||||
|
necessary and would couple the release cadences; recording the escape
|
||||||
|
hatch here is enough.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- alkgit controls its http adapter's cadence; alkhttp is untouched.
|
||||||
|
- Downstream embedding is: two deps + one merge + one auth callback.
|
||||||
|
- The identity-extractor callback is the one place downstream auth
|
||||||
|
semantics enter; ACL itself stays in core (ADR-007) — adapters never
|
||||||
|
hand-roll authorization.
|
||||||
|
- This ADR stays Proposed until OQ-01 is discussed (user flagged the
|
||||||
|
alkhttp-feature alternative; the escape hatch above is the recorded
|
||||||
|
reconciliation path).
|
||||||
|
|
||||||
|
## References
|
||||||
|
- `docs/research/poc3-findings.md` §"alkhttp fit" (with_extra_routes surface), follow-up 1
|
||||||
|
- `docs/research/vision.md` §"ALPN as a service", §"Sub-crate shape"
|
||||||
|
- alkcall ADR-027 (precedent and its limits)
|
||||||
|
- ADR-001 (crate decomposition), ADR-002 (session boundary), ADR-007/008/009
|
||||||
|
- http.md, OQ-01, OQ-08
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# ADR-007: ACL runs before any advertisement or ref line
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Ref names leak repository existence (and structure). The advertisement is
|
||||||
|
the first thing a client sees; if ACL runs after advertisement begins, a
|
||||||
|
denied caller has already learned that a repo (or a ref within it) exists.
|
||||||
|
The vision's visible-surface = authorized-surface invariant requires the
|
||||||
|
check to run before the first byte of git protocol content.
|
||||||
|
|
||||||
|
POC-1 observed the natural enforcement point on the git:// path: the repo
|
||||||
|
name arrives in-band in the first request line, *before* any ref data — so
|
||||||
|
the resolve-then-authorize step sits structurally ahead of the first
|
||||||
|
emitted line. POC-3 noted the http extra-routes are registered permissive
|
||||||
|
by default (the gateway bearer layer does not cover them) — making the
|
||||||
|
http-side check an explicit alkgit-http responsibility, not an inherited
|
||||||
|
one.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Every session/request performs, in order, before any protocol output:
|
||||||
|
|
||||||
|
1. Extract the wire repo name (git:// first-request line; ssh exec command
|
||||||
|
string; http path segment).
|
||||||
|
2. Resolve it against the registry to (repo id, storage root) — reject
|
||||||
|
unknown repos with the same error as unauthorized ones (no existence
|
||||||
|
oracle) (ADR-008).
|
||||||
|
3. Run alkcall `AccessControl::check(peer_identity)` against the repo's
|
||||||
|
required access. Read access covers the *whole fetch surface*:
|
||||||
|
advertisement, ref listing, and pack transfer alike — anonymous access
|
||||||
|
on explicitly-public repos grants the same full read path
|
||||||
|
(vision §"Primary deployment target" makes anonymous clone first-class;
|
||||||
|
the "advertisement is the only anonymous surface" line in the same
|
||||||
|
doc's principle 1 is read as the *minimum* boundary, and this decision
|
||||||
|
sets the operative rule).
|
||||||
|
4. Only then hand the session to transport.
|
||||||
|
|
||||||
|
Adapters embed this sequence; transport asserts it (the session entry
|
||||||
|
points take an authorized-repo marker — a type the adapter constructs only
|
||||||
|
after step 3 passes — so skipping the check is a type error, not a runtime
|
||||||
|
log) but does not re-check — authorization evaluation lives in one place,
|
||||||
|
alkcall, and invocation/wiring lives in one place, the adapter.
|
||||||
|
|
||||||
|
Push is always authenticated on every repo, no exceptions (vision §
|
||||||
|
"Primary deployment target").
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- No ref/capability line is ever emitted for a repo a caller cannot see;
|
||||||
|
the gitea-class "enforcement elsewhere" bug is structurally excluded.
|
||||||
|
- Unknown-repo and unauthorized errors are indistinguishable to callers.
|
||||||
|
- The check is cheap (registry lookup + ACL check) and runs before any
|
||||||
|
expensive protocol work.
|
||||||
|
- http adapters must wire ACL explicitly for their routes (POC-3 showed
|
||||||
|
alkhttp extra routes default permissive) — http.md encodes this.
|
||||||
|
|
||||||
|
## References
|
||||||
|
- `docs/research/vision.md` §"Guiding principles" 1–2, §"Immediate threat-model notes"
|
||||||
|
- `docs/research/poc-1-findings.md` follow-up 4; `docs/research/poc3-findings.md` §"does NOT settle" (auth)
|
||||||
|
- alkcall ADR-017 (privilege model)
|
||||||
|
- ADR-008 (repo identity), http.md §auth, ssh.md §auth
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# ADR-008: Wire repo names are registry IDs, never paths
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Repo names arrive on the wire in three shapes: the git:// first-request
|
||||||
|
line (`git-upload-pack '<repo>'\0host=…`), the ssh exec command string
|
||||||
|
(`git-upload-pack '<repo>'`), and the http path segment
|
||||||
|
(`/<repo>/info/refs`). All three are attacker-controlled. Treating them as
|
||||||
|
filesystem paths (even with ad-hoc sanitization) is the classic git-server
|
||||||
|
traversal bug — and `docs/research/git-protocol.md` §"Security-relevant
|
||||||
|
protocol notes" flags exactly this.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Wire-supplied repo names are **IDs**: opaque registry keys resolved
|
||||||
|
server-side to configured storage roots.
|
||||||
|
|
||||||
|
- The registry is alkgit's authoritative (repo id → storage root +
|
||||||
|
visibility + ACL scope) mapping, owned by `alkgit-core`.
|
||||||
|
- Resolution failure and authorization failure are indistinguishable to
|
||||||
|
the caller (ADR-007 step 2's no-existence-oracle rule).
|
||||||
|
- Wire names are never joined, normalized, or canonicalized into paths.
|
||||||
|
Path construction happens only from registry-resolved roots.
|
||||||
|
- The same never-execute rule covers the ssh exec command string: it is
|
||||||
|
*parsed* (fixed grammar: service name + quoted repo argument), never
|
||||||
|
interpreted by a shell and never passed to a subprocess (convention 6's
|
||||||
|
no-shelling-out invariant). Unknown commands get a fixed refusal.
|
||||||
|
- The registry's own backing store is a separate decision (OQ-06).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Path traversal is structurally impossible on the serving path.
|
||||||
|
- Repo renames/migrations are registry edits, not filesystem moves (the
|
||||||
|
storage root indirection absorbs them).
|
||||||
|
- The registry must be available (or cached) at session start; its
|
||||||
|
unavailability is a config/ops error surfaced as session failure, never
|
||||||
|
a path-guessing fallback.
|
||||||
|
|
||||||
|
## References
|
||||||
|
- `docs/research/git-protocol.md` §"Security-relevant protocol notes"
|
||||||
|
- `docs/research/vision.md` §"Immediate threat-model notes"
|
||||||
|
- ADR-007 (the resolve-then-authorize order)
|
||||||
|
- storage.md §registry, OQ-06 (registry backing)
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# ADR-009: Bounded-resources budget model
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Git servers are internet-facing by definition; unbounded loops and buffers
|
||||||
|
are bugs. Each POC surfaced specific unbounded surfaces that need budgets:
|
||||||
|
|
||||||
|
- Negotiation rounds / haves count (fetch can loop forever without
|
||||||
|
`done`).
|
||||||
|
- receive-pack POST body size (push can be arbitrarily large; alkhttp
|
||||||
|
custom routes get hyper's unbounded stream — POC-3).
|
||||||
|
- Session wall-clock (long-lived ssh/git sessions).
|
||||||
|
- Blocking-pool usage: pack generation runs on `spawn_blocking`; a
|
||||||
|
thundering herd of fetches can starve the pool (POC-2 follow-up 2).
|
||||||
|
- Sideband chunk size is bounded (65000) but max pack size per fetch is
|
||||||
|
still unbounded above it.
|
||||||
|
- alkcall channels carry their own backpressure limits (alkcall ADR-040);
|
||||||
|
git sessions ride raw duplex streams, so those limits do not
|
||||||
|
automatically apply.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Every session carries a `Limits` value, constructed by the adapter from
|
||||||
|
server config and handed to transport as part of the session tuple
|
||||||
|
(ADR-002). Defaults are per-crate constants; overrides are server config
|
||||||
|
in `alkgitd`.
|
||||||
|
|
||||||
|
| Budget | Applies to | Default direction |
|
||||||
|
|---|---|---|
|
||||||
|
| max negotiation rounds | fetch (V2, no `done`) | tens |
|
||||||
|
| max haves per round | fetch | thousands |
|
||||||
|
| max pack size | receive-pack | config-bound (tens of MB v1) |
|
||||||
|
| max request body | http POSTs (receive-pack especially) | same as max pack size |
|
||||||
|
| session wall clock | all sessions (enforced by transport's session loop on every door — it is the one component all doors hand the session to; alkcall channel caps add a second bound where channels exist) | tens of minutes |
|
||||||
|
| max concurrent pack generations | server-wide (blocking-pool budget) | small count |
|
||||||
|
| sideband chunk size | fetch streaming | 65000 (fixed, per protocol) |
|
||||||
|
| max advertisement refs | ls-refs response | config-bound |
|
||||||
|
|
||||||
|
On breach: the session ends with a substrate-appropriate error — pkt-line
|
||||||
|
error band + close on duplex; on http, *client-fault* budgets (request
|
||||||
|
body size) map to 413, *server/session* budgets (wall clock, rounds,
|
||||||
|
generation concurrency exhaustion) map to 503. Budgets are fail-closed.
|
||||||
|
|
||||||
|
Max pack size on fetch is *not* budgeted in v1 (the pack is a function of
|
||||||
|
the repo, not the request); receive-pack is the untrusted-input path and
|
||||||
|
gets the hard cap.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- No adapter can forget a budget: transport refuses to start a session
|
||||||
|
without `Limits` (part of the tuple, ADR-002).
|
||||||
|
- Streaming stays O(counts) regardless of budgets; budgets bound
|
||||||
|
*aggregate* work, not internal buffering.
|
||||||
|
- The blocking-pool budget is enforced at assembly/acceptance time
|
||||||
|
(reject/slow-path excess concurrent generations), not per-byte.
|
||||||
|
|
||||||
|
## References
|
||||||
|
- `docs/research/vision.md` §"Guiding principles" 7
|
||||||
|
- `docs/research/poc2-findings.md` follow-ups 2–3; `docs/research/poc3-findings.md` follow-up 3
|
||||||
|
- alkcall ADR-040 (channel backpressure — the thing git sessions bypass)
|
||||||
|
- ADR-002 (session tuple), transport.md §limits, http.md §budgets
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
status: draft
|
||||||
|
last_updated: 2026-09-21
|
||||||
|
---
|
||||||
|
|
||||||
|
# alkgit-http: HTTP Front Door
|
||||||
|
|
||||||
|
## What it is
|
||||||
|
|
||||||
|
The smart-http adapter: git's http protocol served over alkhttp. Owns the
|
||||||
|
smart-http endpoints, the http-framing composition (streaming both ways),
|
||||||
|
and the ACL wiring that alkhttp's extra-routes surface does not provide by
|
||||||
|
default. **Shape partially pending OQ-01/ADR-006** (router factory vs
|
||||||
|
alkhttp feature).
|
||||||
|
|
||||||
|
## Routes (POC-3-validated shapes)
|
||||||
|
|
||||||
|
| Route | Service | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `GET /{repo}/info/refs?service=git-{upload,receive}-pack` | advertisement | smart prefix + capability dump + flush; `application/x-git-*-pack-advertisement`; no-cache |
|
||||||
|
| `GET /{repo}/info/refs?service=git-upload-archive` | — | **not served** — honest refusal (404/403 class), same rule as ssh (ADR-003) |
|
||||||
|
| `POST /{repo}/git-upload-pack` | V2 fetch commands | one command per POST |
|
||||||
|
| `POST /{repo}/git-receive-pack` | push | streaming ingestion, budgeted body (OQ-04) |
|
||||||
|
|
||||||
|
- `{repo}` is the registry ID (ADR-008) — path segment → registry lookup,
|
||||||
|
never a path.
|
||||||
|
- Reserved-path collision checks stay with alkhttp
|
||||||
|
(`HttpAdapter::with_extra_routes` verifies; POC-3 confirmed passing).
|
||||||
|
|
||||||
|
## Session flow (per request)
|
||||||
|
|
||||||
|
1. Extract repo ID from the path; resolve + authorize **before any
|
||||||
|
response bytes** (ADR-007/008). Unknown == unauthorized (no existence
|
||||||
|
oracle). Identity comes from the downstream auth model (**OQ-08**).
|
||||||
|
2. `GET info/refs`: emit advertisement (via transport stateless
|
||||||
|
substrate); ACL checked before the smart prefix line.
|
||||||
|
3. `POST git-upload-pack`: build `StatelessRequest` (ADR-005) — the
|
||||||
|
substrate handles capability-dump skipping, delim-aware parsing,
|
||||||
|
flush-only responses, probe handling.
|
||||||
|
4. `POST git-receive-pack`: streaming body → transport receive-pack;
|
||||||
|
body budgeted (ADR-009; alkhttp extra routes are unbounded by default
|
||||||
|
— the budget is *ours*, POC-3 follow-up 3).
|
||||||
|
|
||||||
|
## Streaming composition (POC-3-proven)
|
||||||
|
|
||||||
|
- Request bodies: axum `BodyDataStream` → chunk-at-a-time reader →
|
||||||
|
packetline parser; no accumulation (dribble-probe verified). The
|
||||||
|
`futures-io` reader adapter lives in transport (ADR-005).
|
||||||
|
- Responses: sideband/protocol output → bounded mpsc (8 slots) →
|
||||||
|
`Body::from_stream`; back pressure parks the blocking-side generator;
|
||||||
|
first chunk leaves in milliseconds; RSS flat under slow readers.
|
||||||
|
- One sideband chunk = one HTTP chunk (65000-byte chunks carry over).
|
||||||
|
|
||||||
|
## Composition seam (ADR-006 — proposed, OQ-01)
|
||||||
|
|
||||||
|
The crate exposes the adapter as a factory (shape per ADR-006):
|
||||||
|
dependencies in (identity extractor, registry, transport hooks, limits),
|
||||||
|
an axum `Router` out, ready for `HttpAdapter::with_extra_routes`.
|
||||||
|
`alkgitd` is the first consumer; a downstream alkhttp deployment is the
|
||||||
|
second. If adopted, this section becomes the seam spec.
|
||||||
|
|
||||||
|
`HttpAdapter` is not `Clone` (POC-3): assembly holds `Arc<HttpAdapter>`
|
||||||
|
per accept task — the intended usage.
|
||||||
|
|
||||||
|
## Auth
|
||||||
|
|
||||||
|
- The gateway bearer layer does not cover extra routes' handlers unless
|
||||||
|
registered before it (POC-3): ACL wiring here is explicit, not
|
||||||
|
inherited.
|
||||||
|
- Identity extraction mechanics (bearer/basic/…) are **OQ-08**; the
|
||||||
|
factory seam takes it as a callback so downstream apps plug theirs in.
|
||||||
|
- Anonymous fetch: per-repo opt-in; push always authenticated (ADR-007).
|
||||||
|
|
||||||
|
## Design Decisions
|
||||||
|
|
||||||
|
| ADR | Decision | Summary |
|
||||||
|
|---|---|---|
|
||||||
|
| [002](decisions/002-front-door-blind-core.md) | Session boundary | stateless substrate per POST |
|
||||||
|
| [005](decisions/005-session-substrate-types.md) | Substrate types | http framing rules live in transport |
|
||||||
|
| [006](decisions/006-http-adapter-composition.md) | Router factory | **proposed** — OQ-01 open |
|
||||||
|
| [007](decisions/007-acl-before-advertisement.md) | ACL first | explicit wiring, not inherited |
|
||||||
|
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | path segment = registry ID |
|
||||||
|
| [009](decisions/009-bounded-resources-budget.md) | Budgets | request-body cap on POSTs |
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
- **OQ-01**: adapter home/composability (partially resolved — ADR-006
|
||||||
|
proposed).
|
||||||
|
- **OQ-08**: identity sources (open — blocks the auth section).
|
||||||
|
- **OQ-04**: receive-pack over http (deferred(unclear)).
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `docs/research/poc3-findings.md` (route shapes, framing facts, streaming measurements
|
||||||
|
— normative)
|
||||||
|
- `docs/research/alk-stack.md` §"Interfaces in alkcall terms"
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
---
|
||||||
|
status: draft
|
||||||
|
last_updated: 2026-09-21
|
||||||
|
---
|
||||||
|
|
||||||
|
# Open Questions
|
||||||
|
|
||||||
|
All unresolved architecture questions, centrally tracked. Status values:
|
||||||
|
`open` (needs resolution now), `partially resolved` (decision made but a
|
||||||
|
narrower question remains — named in the entry), `resolved` (decision
|
||||||
|
made, ADR recorded), `deferred(scope)` (waiting on external information —
|
||||||
|
blocked-on condition stated), `deferred(unclear)` (pieces exist, shape
|
||||||
|
needs investigation). See `docs/sdd_process.md` for the deferral protocol
|
||||||
|
(blocker tasks in `tasks/architecture/`).
|
||||||
|
|
||||||
|
**Door type** classifies reversal cost: `one-way` decisions are
|
||||||
|
expensive/impossible to reverse once published (wire formats, public API
|
||||||
|
shapes); `two-way` decisions can be revisited while nothing is published.
|
||||||
|
Door type does not change urgency — all decisions here need resolution
|
||||||
|
when their impacts say so; it records how careful the resolution must be.
|
||||||
|
|
||||||
|
## Deferred / Blocked summary
|
||||||
|
|
||||||
|
| OQ | Status | Blocked on / investigation |
|
||||||
|
|---|---|---|
|
||||||
|
| OQ-04 | deferred(unclear) | receive-pack walkthrough (capabilities, shallow, thin-pack, CAS timing) + push POC |
|
||||||
|
| OQ-06 | deferred(scope) | concrete metadata-scale requirements (feeders: OQ-07, OQ-08 outputs) |
|
||||||
|
| OQ-05 | deferred(scope) | ecosystem need for sha256 |
|
||||||
|
|
||||||
|
## Theme: composition / crate shapes
|
||||||
|
|
||||||
|
### OQ-01: HTTP adapter home and composability (alkhttp `git` feature vs alkgit-owned factory)
|
||||||
|
|
||||||
|
- **Origin**: [overview.md], [http.md], user session question
|
||||||
|
- **Status**: partially resolved — ADR-006 written **Proposed** with
|
||||||
|
Option A (alkgit-owned router factory) as the recommendation and the
|
||||||
|
alkhttp-side sugar as a recorded escape hatch. Needs user review.
|
||||||
|
- **Door type**: two-way (adapter shape can change before anything is
|
||||||
|
published)
|
||||||
|
- **Priority**: high
|
||||||
|
- **Impacts**: blocks finalizing http.md and ADR-006; small effect on
|
||||||
|
downstream ergonomics.
|
||||||
|
- **Resolution path**: user reviews ADR-006; accept → ADR becomes
|
||||||
|
Accepted; or choose Option B (alkhttp feature) → ADR reworked.
|
||||||
|
- **Cross-references**: ADR-001, ADR-006, http.md
|
||||||
|
|
||||||
|
### OQ-03: Downstream embedding surface (what "embeds core + transport" means concretely)
|
||||||
|
|
||||||
|
- **Origin**: [overview.md], [transport.md], [ssh.md], vision §ALPN as a
|
||||||
|
service
|
||||||
|
- **Status**: partially resolved — v1 ssh-door decision made: alkgitd
|
||||||
|
terminates SSH via russh for stock git clients (ssh.md); what remains
|
||||||
|
deferred is whether a *pure-alkcall-channels* ssh variant and a
|
||||||
|
russh-flavored adapter are exported for embedders.
|
||||||
|
- **Door type**: two-way
|
||||||
|
- **Priority**: medium
|
||||||
|
- **Impacts**: blocks nothing in v1 (alkgitd is the only consumer); shapes
|
||||||
|
the crates' public API freeze before publish.
|
||||||
|
- **Blocked on**: a concrete downstream embedder use case (e.g. a real
|
||||||
|
gitea-like app or test harness wanting to serve git) — until one
|
||||||
|
exists, the embedding seam is designed by example (alkgitd) only.
|
||||||
|
Tracker task: `tasks/architecture/oq-03-embedder.md`.
|
||||||
|
- **Cross-references**: ADR-001, ADR-002, transport.md §public API,
|
||||||
|
ssh.md §russh
|
||||||
|
|
||||||
|
## Theme: transport / protocol
|
||||||
|
|
||||||
|
### OQ-02: V2 multi-round negotiation (ack/NAK logic, `wait-for-done` retirement)
|
||||||
|
|
||||||
|
- **Origin**: [transport.md], poc2-findings §"does NOT settle"
|
||||||
|
- **Status**: open
|
||||||
|
- **Priority**: medium (full-closure-on-`done` works; multi-round is an
|
||||||
|
efficiency feature, not correctness)
|
||||||
|
- **Impacts**: fetch efficiency on repos with large shared history;
|
||||||
|
capability advertisement text (`fetch=` value).
|
||||||
|
- **Resolution path**: design the ack loop (rounds budget per ADR-009)
|
||||||
|
when transport implementation begins; POC-2's generator is
|
||||||
|
negotiation-agnostic already.
|
||||||
|
- **Cross-references**: ADR-003, ADR-004, ADR-009, transport.md §fetch
|
||||||
|
|
||||||
|
### OQ-04: receive-pack (push) — validation gap
|
||||||
|
|
||||||
|
- **Origin**: [transport.md], poc3-findings §"does NOT settle"
|
||||||
|
- **Status**: deferred(unclear)
|
||||||
|
- **Door type**: two-way
|
||||||
|
- **Priority**: high
|
||||||
|
- **Impacts**: blocks receive-pack implementation tasks; push is the
|
||||||
|
always-authenticated half of the wire surface.
|
||||||
|
- **Investigation**: the pieces are decided (POC-2: pack ingestion via
|
||||||
|
`gix-pack::data::input` with `streaming-input` (ADR-004); `gix-ref`
|
||||||
|
transaction CAS; fsck via `gix-fsck`; POC-3: request bodies stream). The shape to
|
||||||
|
work through: the full push state machine —
|
||||||
|
(a) the receive-pack **capability advertisement set**
|
||||||
|
(`report-status`/`report-status-v2`, `delete-refs`, `push-options`,
|
||||||
|
`atomic`, `side-band-64k`, `object-format`) under the honest-advertisement
|
||||||
|
invariant (ADR-003); (b) request-line parsing
|
||||||
|
(`<old> <new> <ref>` + shallow lines policy — expected resolution:
|
||||||
|
**reject shallow on push for v1**, mirroring fetch's decline-by-omission
|
||||||
|
in ADR-003, so depth semantics stay symmetric; confirm against real
|
||||||
|
`git push` behavior);
|
||||||
|
(c) thin-pack acceptance on push (client packs may be thin; accepting
|
||||||
|
implies base-object availability requirements); (d) pack ingestion
|
||||||
|
mid-stream; (e) CAS validation timing (before vs after pack index);
|
||||||
|
(f) status report (`unpack ok|ng` + per-ref lines); (g) the
|
||||||
|
receive-pack version/framing surface over http (which framing `git push`
|
||||||
|
uses against us; ADR-003's V2 decision covers fetch only). Method:
|
||||||
|
walkthrough against real `git push` captures, then a small POC if the
|
||||||
|
ingestion composition is not obvious from POC-2's findings. Tracker
|
||||||
|
task: `tasks/architecture/oq-04-receive-pack.md`.
|
||||||
|
- **Cross-references**: ADR-003, ADR-004, ADR-009, transport.md
|
||||||
|
§receive-pack, storage.md §ref transactions, http.md
|
||||||
|
|
||||||
|
### OQ-05: sha256 support policy
|
||||||
|
|
||||||
|
- **Origin**: [transport.md], git-protocol.md §"Open items"
|
||||||
|
- **Status**: deferred(scope)
|
||||||
|
- **Door type**: two-way
|
||||||
|
- **Priority**: low
|
||||||
|
- **Impacts**: none for v1 (sha1 pinned); feature-flag passthrough
|
||||||
|
compiles but is untested end-to-end.
|
||||||
|
- **Blocked on**: ecosystem need (a real client/repo requiring sha256) or
|
||||||
|
upstream gix sha256 maturity; POC-2 left the pipeline hash-generic but
|
||||||
|
untested. Tracker task: `tasks/architecture/oq-05-sha256.md`.
|
||||||
|
- **Cross-references**: ADR-003, ADR-004
|
||||||
|
|
||||||
|
## Theme: identity / auth
|
||||||
|
|
||||||
|
### OQ-08: Identity sources per front door (v1 auth mechanics)
|
||||||
|
|
||||||
|
- **Origin**: [overview.md], [http.md], [ssh.md], [alkgitd.md]
|
||||||
|
- **Status**: open
|
||||||
|
- **Priority**: high
|
||||||
|
- **Impacts**: blocks http.md and ssh.md auth sections finalizing; blocks
|
||||||
|
the identity-extractor callback design in ADR-006's seam; blocks
|
||||||
|
alkgitd config schema and the OQ-07 admin op shapes.
|
||||||
|
- **Resolution path**: decide per door — http (bearer token? basic? via
|
||||||
|
alkvault-stored credentials), ssh (russh-terminated public-key auth →
|
||||||
|
alkgit identity mapping per ssh.md), and which identities exist in
|
||||||
|
v1's registry — including whether identity records live in the same
|
||||||
|
metadata store as repo records (OQ-06's field list may grow for this).
|
||||||
|
- **Cross-references**: ADR-006, ADR-007, OQ-06 (metadata backing where
|
||||||
|
identity records live),
|
||||||
|
http.md §auth, ssh.md §identity, alkgitd.md, OQ-07
|
||||||
|
|
||||||
|
## Theme: storage / metadata
|
||||||
|
|
||||||
|
### OQ-06: Registry/metadata backing store
|
||||||
|
|
||||||
|
- **Origin**: [storage.md], ADR-008
|
||||||
|
- **Status**: deferred(scope)
|
||||||
|
- **Door type**: two-way (backing choice is swappable behind the core
|
||||||
|
trait)
|
||||||
|
- **Priority**: high for v1 config story, but choice deferrable because
|
||||||
|
the trait boundary is what matters
|
||||||
|
- **Impacts**: blocks storage.md's registry section finalizing and
|
||||||
|
alkgitd's config schema; does NOT block core/transport work (they code
|
||||||
|
against the trait).
|
||||||
|
- **Blocked on**: concrete metadata-scale requirements (how many repos,
|
||||||
|
what metadata fields beyond id/root/visibility/ACL scope, whether
|
||||||
|
alkcall-hub integration lands in v1). A config-file or embedded-store
|
||||||
|
decision without those inputs would be a guess. Tracker task:
|
||||||
|
`tasks/architecture/oq-06-metadata-backing.md`.
|
||||||
|
- **Cross-references**: ADR-008, storage.md §registry, alkgitd.md §config,
|
||||||
|
OQ-08 (whose identity-records question may extend this store's field
|
||||||
|
list)
|
||||||
|
|
||||||
|
### OQ-07: Admin API operation set (v1 scope)
|
||||||
|
|
||||||
|
- **Origin**: [overview.md], [alkgitd.md], alk-stack.md §"The gitea lesson"
|
||||||
|
- **Status**: open
|
||||||
|
- **Priority**: medium
|
||||||
|
- **Impacts**: blocks the admin-ops inventory (repo create/delete,
|
||||||
|
visibility set, ACL grant/revoke, user/identity management — if v1 has
|
||||||
|
users at all, which is OQ-08 territory). Deliberately small: everything
|
||||||
|
is `Visibility::Internal` alkcall ops over the admin surface.
|
||||||
|
- **Resolution path**: one dedicated session once OQ-08's identity model
|
||||||
|
exists (the ops' shapes depend on what identities/credentials mean).
|
||||||
|
- **Cross-references**: ADR-007, OQ-08, alkgitd.md §admin API
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
---
|
||||||
|
status: draft
|
||||||
|
last_updated: 2026-09-21
|
||||||
|
---
|
||||||
|
|
||||||
|
# Overview: alkgit
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
alkgit is a self-hosted git server: repository storage, the git smart
|
||||||
|
protocol served over http and ssh interfaces, and the `alkgitd` binary that
|
||||||
|
assembles it all. It exists to provide a small, security-first git platform
|
||||||
|
whose exposure model inverts the mainstream pattern (visible-surface =
|
||||||
|
authorized-surface, authenticated-by-default, no plaintext secrets, no
|
||||||
|
plugin execution). See `docs/research/vision.md` for the full WHY.
|
||||||
|
|
||||||
|
## The one-line architecture
|
||||||
|
|
||||||
|
**Storage + ACL + protocol adapters.** `alkgit-core` owns repository storage
|
||||||
|
and access-rule types; `alkgit-transport` owns the git protocol state
|
||||||
|
machines; `alkgit-http` and `alkgit-ssh` are thin front doors that
|
||||||
|
authenticate, resolve the repo, and hand a session to the transport;
|
||||||
|
`alkgitd` assembles everything with config, TLS/ACME, and the vault.
|
||||||
|
|
||||||
|
## Crate map
|
||||||
|
|
||||||
|
| Crate | Owns | Depends on | Never depends on |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `alkgit-core` | repo registry/storage roots, odb wrappers, ref store, pack generate/ingest, fsck, ACL input types | gix crates, alkcall (ACL types only) | any front-door crate |
|
||||||
|
| `alkgit-transport` | pkt-line sessions, V2 advertisement/state machine, ls-refs, fetch, receive-pack | `alkgit-core`, alkcall, gix-packetline | alkhttp, alkgit-ssh |
|
||||||
|
| `alkgit-http` | smart-http endpoints over alkhttp | transport, core, alkhttp | alkgit-ssh, alkgitd |
|
||||||
|
| `alkgit-ssh` | git-command dispatch for exec requests over alkcall channels | transport, core, alkcall | alkhttp, alkgitd |
|
||||||
|
| `alkgitd` | binary: config, assembly, TLS/ACME, listeners, vault wiring | everything | — |
|
||||||
|
|
||||||
|
Dependency rules (ADR-001, ADR-002):
|
||||||
|
|
||||||
|
1. `alkgit-core` + `alkgit-transport` are **front-door-blind**: they consume
|
||||||
|
(identity, repo id, duplex stream, limits) and depend on alkcall types
|
||||||
|
only. No http types, no ssh channel types below the stream.
|
||||||
|
2. `alkgit-http` and `alkgit-ssh` never depend on each other.
|
||||||
|
3. `alkgitd` is the only crate allowed to know the whole graph.
|
||||||
|
4. A downstream application (gitea-like) embeds core + transport and brings
|
||||||
|
its own front doors; the admin API is a set of alkcall ops it may use or
|
||||||
|
replace.
|
||||||
|
|
||||||
|
## Security invariants (spec-level, all components honor)
|
||||||
|
|
||||||
|
These are the load-bearing rules from `docs/research/vision.md` and
|
||||||
|
`docs/research/alk-stack.md`; each component doc references them where
|
||||||
|
concrete:
|
||||||
|
|
||||||
|
1. **Authenticated by default** — anonymous fetch exists only on
|
||||||
|
explicitly-public repos; push is always authenticated.
|
||||||
|
2. **Visible-surface = authorized-surface** — via alkcall ACL; ops with
|
||||||
|
`Visibility::Internal` are never wire-callable; admin ops ride an admin
|
||||||
|
surface, never the git traffic surface.
|
||||||
|
3. **ACL before advertisement** — access is checked before any ref line or
|
||||||
|
capability line is emitted (ref names leak repo existence) (ADR-007).
|
||||||
|
4. **Registry-resolved repo identity** — wire-supplied repo names are IDs
|
||||||
|
resolved to server-configured storage roots; never used as paths (ADR-008).
|
||||||
|
5. **No secret material on the wire or at rest outside alkvault** — metadata
|
||||||
|
holds vault references only; outbound credentials flow through alkcall
|
||||||
|
`Capabilities` (alkcall ADR-010), and no handler reads credentials from
|
||||||
|
env or files (the no-env-vars invariant).
|
||||||
|
6. **No shelling out to `git`** — serving path is pure Rust on gix
|
||||||
|
primitives (GPL hygiene + no process-injection surface).
|
||||||
|
7. **Bounded resources** — every session carries wall-clock, size, and round
|
||||||
|
limits (ADR-009); unbounded loops/buffers are bugs.
|
||||||
|
8. **Honest capability advertisement** — the protocol advertises exactly
|
||||||
|
what we serve (ADR-003).
|
||||||
|
|
||||||
|
## Interfaces (the boundary shape)
|
||||||
|
|
||||||
|
The core boundary, from POC-1 (`docs/research/poc-1-findings.md`, follow-up 4) and the
|
||||||
|
"ALPN as a service" rule in `docs/research/vision.md`:
|
||||||
|
|
||||||
|
- **Transport input**: one session = (peer identity from alkcall
|
||||||
|
`AuthContext`, repo id resolved against the registry, a duplex byte
|
||||||
|
stream, session limits). For the stateless http door this becomes
|
||||||
|
(identity, repo, request-reader, response-writer, limits) — the same state
|
||||||
|
machines, a different substrate (ADR-005).
|
||||||
|
- **Storage input**: the transport asks core for (a) ref advertisement data,
|
||||||
|
(b) pack generation for a want/have set, (c) pack ingestion + ref
|
||||||
|
transactions for receive-pack. Storage never sees pkt-lines.
|
||||||
|
|
||||||
|
## What is already validated (POC-backed)
|
||||||
|
|
||||||
|
- pkt-line over alkcall `BiStream` end-to-end (POC-1).
|
||||||
|
- Pack generation pipeline streaming with O(counts) memory (POC-2).
|
||||||
|
- Smart-http streaming both ways through alkhttp custom routes (POC-3).
|
||||||
|
The full V2 fetch path against real git 2.43 is proven; receive-pack is
|
||||||
|
designed but not yet exercised (OQ-04 tracks the POC/validation gap).
|
||||||
|
|
||||||
|
## Design Decisions
|
||||||
|
|
||||||
|
| ADR | Decision | Summary |
|
||||||
|
|---|---|---|
|
||||||
|
| [001](decisions/001-crate-decomposition.md) | Crate decomposition | 5 crates: core, transport, http, ssh, alkgitd |
|
||||||
|
| [002](decisions/002-front-door-blind-core.md) | Front-door-blind core | Session boundary = (identity, repo, stream, limits) |
|
||||||
|
| [003](decisions/003-protocol-v2-first.md) | V2-first protocol | V2-only fetch on both doors; honest advertisement; multi-round negotiation sequenced (OQ-02); push surface OQ-04 |
|
||||||
|
| [004](decisions/004-pack-pipeline.md) | Pack pipeline | gitoxide `data::output` generation, streaming-input ingestion |
|
||||||
|
| [005](decisions/005-session-substrate-types.md) | Substrate types | Duplex + stateless session APIs over one state machine |
|
||||||
|
| [006](decisions/006-http-adapter-composition.md) | HTTP adapter composition | Router factory in alkgit-http (**proposed**, OQ-01) |
|
||||||
|
| [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 |
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
Key cross-cutting questions tracked in [open-questions.md](open-questions.md):
|
||||||
|
|
||||||
|
- **OQ-01**: http adapter composability (where the smart-http routes live
|
||||||
|
for downstream embedding) — affects http.md and ADR-006.
|
||||||
|
- **OQ-04**: receive-pack (push) validation gap (high — the
|
||||||
|
always-authenticated half of the wire surface).
|
||||||
|
- **OQ-08**: identity sources per front door (how http and ssh authenticate
|
||||||
|
peers in v1).
|
||||||
|
- **OQ-06**: metadata store backing for the registry (config-file vs
|
||||||
|
embedded store vs alkcall-hosted).
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
---
|
||||||
|
status: draft
|
||||||
|
last_updated: 2026-09-21
|
||||||
|
---
|
||||||
|
|
||||||
|
# alkgit-ssh: SSH Front Door
|
||||||
|
|
||||||
|
## What it is
|
||||||
|
|
||||||
|
The ssh adapter: serves the git command surface over ssh. Unlike http,
|
||||||
|
ssh is session-shaped, so it maps directly onto the transport duplex
|
||||||
|
session (ADR-002).
|
||||||
|
|
||||||
|
**Client compatibility (v1 decision):** stock git clients
|
||||||
|
(`git clone ssh://git@host/repo`) speak the SSH wire protocol — an exec
|
||||||
|
request over an SSH connection. alkcall channels are *not* that protocol;
|
||||||
|
they are alkgit's internal session layer. v1 therefore terminates real SSH
|
||||||
|
wire protocol (russh is the candidate); the vision's "not a general sshd"
|
||||||
|
non-goal is untouched — only the git command surface is served. Concretely:
|
||||||
|
|
||||||
|
- `alkgitd` terminates SSH (russh under the `ssh` door) and hands the
|
||||||
|
adapter the post-auth exec request + stream.
|
||||||
|
- alkcall channels remain the *internal* substrate identity/ACL flows
|
||||||
|
through (alk-stack.md §"Interfaces in alkcall terms"); the adapter's
|
||||||
|
dispatch output is the same duplex session either way.
|
||||||
|
- A pure-alkcall-channels ssh door (no wire SSH) is a downstream-embedding
|
||||||
|
variant only — OQ-03 territory, not v1.
|
||||||
|
- Manifest note: the pre-phase-1 workspace skeleton pins only `alkcall` in
|
||||||
|
`crates/alkgit-ssh/Cargo.toml`; the russh dependency (and where it sits —
|
||||||
|
alkgitd vs alkgit-ssh) is set when this spec implements.
|
||||||
|
|
||||||
|
## What we serve
|
||||||
|
|
||||||
|
Exactly the git command surface, not a general sshd (vision non-goals):
|
||||||
|
|
||||||
|
| Command | Serves |
|
||||||
|
|---|---|
|
||||||
|
| `git-upload-pack '<repo>'` | fetch/clone (duplex V2 session) |
|
||||||
|
| `git-receive-pack '<repo>'` | push (duplex receive-pack; OQ-04) |
|
||||||
|
| `git-upload-archive` | **not served** (declined explicitly —
|
||||||
|
`docs/research/git-protocol.md` §security notes; honest refusal, not
|
||||||
|
silence) |
|
||||||
|
|
||||||
|
## Session flow
|
||||||
|
|
||||||
|
1. **Command dispatch**: parse the exec request string (`git-upload-pack`
|
||||||
|
/ `git-receive-pack` + quoted repo argument). Reject other commands
|
||||||
|
with a clear error on stderr (no shell interpretation — the command
|
||||||
|
string is parsed, never executed).
|
||||||
|
2. **Repo resolution + ACL** (ADR-007/008): the quoted repo argument is a
|
||||||
|
registry ID; resolve, authorize (read for upload-pack, write for
|
||||||
|
receive-pack — push always authenticated), all before any protocol
|
||||||
|
byte.
|
||||||
|
3. **V2 negotiation**: `GIT_PROTOCOL=version=2` arrives as an ssh env
|
||||||
|
line (`git-protocol.md` §framing). v1 policy: expect/require V2 per
|
||||||
|
ADR-003 (V0/V1 declined with a clear error).
|
||||||
|
4. **Handoff**: the post-auth stream (from the russh-terminated session)
|
||||||
|
is the duplex substrate; transport runs the duplex session
|
||||||
|
(advertise-once → command loop, sideband packs). The adapter's job
|
||||||
|
ends here — no protocol logic lives in this crate.
|
||||||
|
|
||||||
|
## Identity
|
||||||
|
|
||||||
|
- With russh terminating SSH (see the v1 decision), peer identity comes
|
||||||
|
from the ssh authentication the terminator performs (public key,
|
||||||
|
typically). How that identity becomes an alkgit/alkcall identity — and
|
||||||
|
where authorized keys live (vault involvement) — is **OQ-08**, shared
|
||||||
|
with http; one auth session settles both doors.
|
||||||
|
|
||||||
|
## Relationship to russh
|
||||||
|
|
||||||
|
russh terminates the SSH wire protocol for stock git clients (see the v1
|
||||||
|
decision above). The dispatch + ACL + handoff sequence (§Session flow) is
|
||||||
|
door-agnostic: alkcall-channel and russh inputs converge on the same
|
||||||
|
transport duplex session. Whether alkgit-ssh exports a russh-flavored
|
||||||
|
adapter separately from the alkcall one is an OQ-03 (embedding surface)
|
||||||
|
question — v1 ships the path alkgitd needs (russh termination → this
|
||||||
|
adapter's dispatch).
|
||||||
|
|
||||||
|
## Limits
|
||||||
|
|
||||||
|
The duplex session carries `Limits` (ADR-009). Channel-level caps
|
||||||
|
(alkcall ADR-041 per-identity channel cap) still apply above us; git
|
||||||
|
sessions consume one channel each (alk-stack.md §"Interfaces in alkcall
|
||||||
|
terms").
|
||||||
|
|
||||||
|
## Design Decisions
|
||||||
|
|
||||||
|
| ADR | Decision | Summary |
|
||||||
|
|---|---|---|
|
||||||
|
| [001](decisions/001-crate-decomposition.md) | Crate decomposition | ssh is a separate replaceable adapter |
|
||||||
|
| [002](decisions/002-front-door-blind-core.md) | Session boundary | duplex session = native ssh shape |
|
||||||
|
| [003](decisions/003-protocol-v2-first.md) | V2-first | V0/V1 declined via env-line check |
|
||||||
|
| [007](decisions/007-acl-before-advertisement.md) | ACL first | before advertisement, push always authed |
|
||||||
|
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | exec argument = registry ID |
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
- **OQ-08**: ssh identity mechanics (open).
|
||||||
|
- **OQ-03**: russh-flavored embedder adapter (deferred(scope)).
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `docs/research/poc-1-findings.md` (duplex session shape, git:// in-band framing
|
||||||
|
analog — ssh exec is the same resolve-before-emit order)
|
||||||
|
- `docs/research/alk-stack.md` §"Interfaces in alkcall terms"
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
---
|
||||||
|
status: draft
|
||||||
|
last_updated: 2026-09-21
|
||||||
|
---
|
||||||
|
|
||||||
|
# alkgit-core: Storage
|
||||||
|
|
||||||
|
## What it is
|
||||||
|
|
||||||
|
The repository storage layer: repo registry, object database wrappers, ref
|
||||||
|
store, pack generation/ingestion, and access-rule input types. It is
|
||||||
|
transport-agnostic and front-door-blind (ADR-001): it knows nothing about
|
||||||
|
pkt-lines, http, or ssh.
|
||||||
|
|
||||||
|
## Why this shape
|
||||||
|
|
||||||
|
gitoxide provides the primitives (odb, packs, refs, fsck, objects); the
|
||||||
|
generation-pipeline decision is ADR-004 (POC-2-validated). The crate
|
||||||
|
exists to (a) wrap gix with a coherent session-safe API (`gix_odb::Cache`
|
||||||
|
is not `Sync` — the sharing rules are enforced by the API in this crate),
|
||||||
|
(b) own the registry (ADR-008), and (c) hold the ACL input types the
|
||||||
|
adapters and alkcall share.
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### Repository registry
|
||||||
|
|
||||||
|
- Authoritative (repo id → storage root, visibility, ACL scope) mapping.
|
||||||
|
- Wire repo names are registry IDs, never paths (ADR-008); resolution
|
||||||
|
failure is indistinguishable from authorization failure (ADR-007).
|
||||||
|
- Backing store: **open** — OQ-06. Core defines the trait; alkgitd picks
|
||||||
|
the backing. Metadata holds no secrets (vault references at most).
|
||||||
|
- Storage roots are server-configured; the registry maps IDs onto them.
|
||||||
|
|
||||||
|
### Repository access (`Repo`)
|
||||||
|
|
||||||
|
- Opens a (storage root, hash algo) into an object database + ref store.
|
||||||
|
- Wraps `gix_odb::Store` sharing: `Arc<Store>` shared across sessions,
|
||||||
|
per-session handles (`to_handle_arc()` + `prevent_pack_unload()` +
|
||||||
|
`ignore_replacements = true` — the POC-2 prerequisites). The public API
|
||||||
|
hands out handles, never the raw `Cache`.
|
||||||
|
- Ref listing for advertisement: refs + peeled tags + symref targets, the
|
||||||
|
ls-refs response data.
|
||||||
|
- Ref transactions: CAS apply for receive-pack (`gix-ref` transaction
|
||||||
|
module), with name validation (git ref rules + reserved-namespace
|
||||||
|
deny-list; `docs/research/git-protocol.md` §security notes).
|
||||||
|
|
||||||
|
### Pack generation (fetch side)
|
||||||
|
|
||||||
|
- Type: (odb handle, wants, haves, limits) → streaming pack (`io::Write`
|
||||||
|
consumer). Negotiation-agnostic: negotiation produces the boundary sets,
|
||||||
|
the writer streams them (ADR-004).
|
||||||
|
- Encodes the POC-2 composition: tip peeling → commit-ancestry walk →
|
||||||
|
`TreeContents` count → entries → bytes; missing objects abort (never
|
||||||
|
emit a broken pack); entry statistics surface as metrics.
|
||||||
|
- Memory profile is O(counts) — the budget model (ADR-009) bounds
|
||||||
|
aggregate work, not internal buffering.
|
||||||
|
|
||||||
|
### Pack ingestion (receive side)
|
||||||
|
|
||||||
|
- Client pack stream → indexed pack + fsck/connectivity report + per-ref
|
||||||
|
CAS application. Uses `gix-pack` streaming-input + `gix-fsck` +
|
||||||
|
`gix-ref` transactions. Shape is designed (ADR-004); validation against
|
||||||
|
real `git push` is pending — OQ-04.
|
||||||
|
- Received pack size is budgeted (ADR-009 max pack size); ingestion runs
|
||||||
|
on blocking threads like generation.
|
||||||
|
|
||||||
|
### Access-rule input types
|
||||||
|
|
||||||
|
- The types alkcall `AccessControl::check` consumes for repos: visibility
|
||||||
|
(public/private), identity-based read/write. Deliberately *input types*:
|
||||||
|
the evaluation function lives in alkcall (single source of authorization
|
||||||
|
logic); invocation/wiring happens in the adapters (ADR-007). Core
|
||||||
|
defines what a repo's rule set looks like.
|
||||||
|
- v1 scope: repo visibility + identity read/write, nothing richer
|
||||||
|
(vision §"Primary deployment target").
|
||||||
|
|
||||||
|
## Concurrency model
|
||||||
|
|
||||||
|
- `Store`/registry structures: `parking_lot` short-held locks (project
|
||||||
|
convention 3).
|
||||||
|
- odb handles: per-session, owned, moved into `spawn_blocking` tasks.
|
||||||
|
- Ref transactions: serialized per-ref by gix-ref's lock files; the
|
||||||
|
registry mapping's read-mostly synchronization (ArcSwap-class or
|
||||||
|
equivalent) is part of OQ-06's backing decision.
|
||||||
|
- Poisoned locks: `unwrap_or_else(|e| e.into_inner())` per convention 2.
|
||||||
|
|
||||||
|
## Public API surface (v1)
|
||||||
|
|
||||||
|
`lib.rs` re-exports (module structure per convention 14): registry trait +
|
||||||
|
types, `Repo`/handle types, pack generate/ingest types, ref-transaction
|
||||||
|
API, ACL input types, error enums (`thiserror`, no panics, no unwrap
|
||||||
|
outside tests). The embedder-facing freeze point is tracked in OQ-03.
|
||||||
|
|
||||||
|
## Design Decisions
|
||||||
|
|
||||||
|
| ADR | Decision | Summary |
|
||||||
|
|---|---|---|
|
||||||
|
| [001](decisions/001-crate-decomposition.md) | Crate decomposition | core = storage, transport-blind |
|
||||||
|
| [004](decisions/004-pack-pipeline.md) | Pack pipeline | `data::output` generation, `data::input` streaming ingestion |
|
||||||
|
| [007](decisions/007-acl-before-advertisement.md) | ACL before advertisement | core supplies rule inputs, adapters enforce |
|
||||||
|
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry IDs |
|
||||||
|
| [009](decisions/009-bounded-resources-budget.md) | Budgets | limits flow into generation/ingestion |
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
- **OQ-06**: registry backing store (deferred(scope) — blocked on
|
||||||
|
metadata-scale requirements).
|
||||||
|
- **OQ-04**: receive-pack ingestion validation (deferred(unclear) — pieces
|
||||||
|
decided, push state machine needs a walkthrough/POC).
|
||||||
|
- **OQ-03**: embedder-facing API freeze (deferred(scope)).
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `docs/research/gitoxide.md` (the API contract notes are normative here)
|
||||||
|
- `docs/research/poc2-findings.md` (generation pipeline + prerequisites)
|
||||||
|
- `docs/research/git-protocol.md` §"Server-side pack generation"
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
---
|
||||||
|
status: draft
|
||||||
|
last_updated: 2026-09-21
|
||||||
|
---
|
||||||
|
|
||||||
|
# alkgit-transport: Git Smart Protocol
|
||||||
|
|
||||||
|
## What it is
|
||||||
|
|
||||||
|
The git protocol layer: pkt-line session substrates, protocol V2 state
|
||||||
|
machines (advertisement, ls-refs, fetch, receive-pack), and the glue that
|
||||||
|
keeps adapters from touching `futures_io` or raw packetline APIs. Depends
|
||||||
|
on `alkgit-core` and alkcall types only (ADR-001/002).
|
||||||
|
|
||||||
|
## Substrate layer (ADR-005)
|
||||||
|
|
||||||
|
Two session entry points over one state-machine core. ADR-005 owns the
|
||||||
|
full decision (what each substrate owns and why); the surface is:
|
||||||
|
|
||||||
|
- **Duplex session** (ssh, git://, embedded stream doors) — input:
|
||||||
|
(peer identity, resolved repo id, duplex stream, `Limits`). Encapsulates
|
||||||
|
the split/compat/packetline bridge, the request reader (delim-aware
|
||||||
|
parsing, `reset()` discipline, break-on-error), and the sideband writer.
|
||||||
|
- **Stateless session** (smart-http) — input: (peer identity, resolved
|
||||||
|
repo id, request-reader, response-writer, `Limits`) per http POST; adds
|
||||||
|
the http-framing rules (capability-dump skip, flush-only responses,
|
||||||
|
probe handling).
|
||||||
|
|
||||||
|
Both substrates feed the same V2 state machines; statelessness is a
|
||||||
|
substrate property (per-request state), not a protocol fork.
|
||||||
|
|
||||||
|
## Protocol core (ADR-003: V2-first)
|
||||||
|
|
||||||
|
### Advertisement
|
||||||
|
|
||||||
|
- Emitted once per duplex session (never between commands — real git
|
||||||
|
hangs on re-advertisement); per-request-set on http (stateless: the
|
||||||
|
client re-sends the dump).
|
||||||
|
- Honest capability list: exactly what we serve (`ls-refs=unborn`,
|
||||||
|
`fetch=wait-for-done` for the v1 done-path policy — ADR-003 pins the
|
||||||
|
values; OQ-02 may extend them when multi-round lands,
|
||||||
|
`object-format=sha1`). Unimplemented features are declined by omission
|
||||||
|
(validated against real git, POC-1).
|
||||||
|
|
||||||
|
### ls-refs
|
||||||
|
|
||||||
|
- Parse `command=ls-refs` (peel, symrefs, ref-prefix), stream ref lines
|
||||||
|
from core's listing, flush. `ref-prefix` filtering is client-driven.
|
||||||
|
|
||||||
|
### fetch
|
||||||
|
|
||||||
|
- Parse wants/haves/done/args; object-format check (reject mismatches —
|
||||||
|
POC-1 to-do).
|
||||||
|
- Negotiation policy (v1 initial): full-closure pack on `done`
|
||||||
|
(POC-validated). Multi-round ack/NAK negotiation: **OQ-02**.
|
||||||
|
- Pack generation via core (ADR-004), streamed over sideband on duplex /
|
||||||
|
sideband-in-response on http; generation runs on `spawn_blocking` with
|
||||||
|
the owned odb handle moved in (POC-2's shape: store shared, handle per
|
||||||
|
session, generation on blocking threads).
|
||||||
|
- Round/haves budgets enforced here (ADR-009).
|
||||||
|
|
||||||
|
### receive-pack (push)
|
||||||
|
|
||||||
|
- Parse update requests (`<old> <new> <ref>` + shallow lines), ingest the
|
||||||
|
pack stream (POC-3-verified: request bodies stream), apply CAS
|
||||||
|
transactions, emit status report. The capability set, shallow policy,
|
||||||
|
CAS timing, and exact status-report shape are OQ-04's investigation
|
||||||
|
target (design intent: shape per OQ-04's resolution).
|
||||||
|
- Validation pending — **OQ-04** (includes the receive-pack version
|
||||||
|
surface, which ADR-003's fetch-V2 decision does not cover).
|
||||||
|
|
||||||
|
### Error taxonomy
|
||||||
|
|
||||||
|
- io errors are terminal (session ends); protocol errors map to pkt-line
|
||||||
|
error bands (duplex) or http status + body (stateless). Substrate-level
|
||||||
|
`thiserror` enum; no panics in library code (convention 2).
|
||||||
|
|
||||||
|
## Limits
|
||||||
|
|
||||||
|
Every session carries `Limits` (ADR-009): negotiation rounds, haves per
|
||||||
|
round, receive-pack max size, wall clock, sideband chunk size (fixed
|
||||||
|
65000), advertisement ref cap. Missing `Limits` is a type-level error.
|
||||||
|
|
||||||
|
## Public API surface (v1)
|
||||||
|
|
||||||
|
`lib.rs` re-exports: `Session` (duplex) + `StatelessRequest` (http)
|
||||||
|
entry points, `Limits`, hook trait(s) connecting to core
|
||||||
|
(advertisement data, pack generation, ingestion), protocol error enums.
|
||||||
|
The exact embedder-facing freeze point: **OQ-03**.
|
||||||
|
|
||||||
|
## Design Decisions
|
||||||
|
|
||||||
|
| ADR | Decision | Summary |
|
||||||
|
|---|---|---|
|
||||||
|
| [002](decisions/002-front-door-blind-core.md) | Session boundary | duplex + stateless entry points |
|
||||||
|
| [003](decisions/003-protocol-v2-first.md) | V2-first | honest advertisement, V0/V1 declined |
|
||||||
|
| [004](decisions/004-pack-pipeline.md) | Pack pipeline | generation on blocking threads, O(counts) |
|
||||||
|
| [005](decisions/005-session-substrate-types.md) | Substrate types | request reader, sideband sink, http framing rules |
|
||||||
|
| [009](decisions/009-bounded-resources-budget.md) | Budgets | `Limits` in every session tuple |
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
- **OQ-02**: V2 multi-round negotiation (open — efficiency, not
|
||||||
|
correctness).
|
||||||
|
- **OQ-04**: receive-pack state machine + validation (deferred(unclear)).
|
||||||
|
- **OQ-03**: embedder API freeze (deferred(scope)).
|
||||||
|
- **OQ-05**: sha256 policy (deferred(scope)).
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `docs/research/poc-1-findings.md`, `docs/research/poc2-findings.md`, `docs/research/poc3-findings.md` (the
|
||||||
|
normative wire behavior — observed against real git, not docs' grammar)
|
||||||
|
- `docs/research/git-protocol.md` (inventory + observed corrections)
|
||||||
|
- `docs/research/gitoxide.md` §"Wire format" (packetline contracts)
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
id: architecture/oq-03-embedder
|
||||||
|
name: OQ-03 unblock — downstream embedder use case arrives
|
||||||
|
status: pending
|
||||||
|
depends_on: []
|
||||||
|
scope: narrow
|
||||||
|
risk: trivial
|
||||||
|
impact: component
|
||||||
|
level: research
|
||||||
|
tags: [external-trigger, deferred-oq]
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Tracker for OQ-03 (`deferred(scope)`): downstream embedding surface. Not
|
||||||
|
actionable work — tracks whether a concrete downstream embedder use case
|
||||||
|
has arrived (a real app or harness wanting to embed core + transport, or
|
||||||
|
ask for a russh-flavored ssh adapter). On trigger: mark completed, OQ-03
|
||||||
|
transitions to `open`.
|
||||||
|
|
||||||
|
## Work
|
||||||
|
|
||||||
|
None by default.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- OQ-03 status matches `docs/architecture/open-questions.md`.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- Designing the embedding seam.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
> Filled on completion.
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
id: architecture/oq-04-receive-pack
|
||||||
|
name: OQ-04 unblock — receive-pack walkthrough/POC arrives
|
||||||
|
status: pending
|
||||||
|
depends_on: []
|
||||||
|
scope: narrow
|
||||||
|
risk: trivial
|
||||||
|
impact: component
|
||||||
|
level: research
|
||||||
|
tags: [external-trigger, deferred-oq]
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Tracker for OQ-04 (`deferred(unclear)`): receive-pack (push) design
|
||||||
|
validation. This task is not actionable work — it tracks whether the
|
||||||
|
investigation has arrived. When a receive-pack design walkthrough (against
|
||||||
|
real `git push` captures) or a validation push POC is scheduled, mark this
|
||||||
|
completed; OQ-04 transitions to `open`. The investigation scope is in
|
||||||
|
`docs/architecture/open-questions.md` (OQ-04): capability advertisement
|
||||||
|
set, request-line parsing + shallow policy, thin-pack acceptance, CAS
|
||||||
|
timing, status report, http framing.
|
||||||
|
|
||||||
|
## Work
|
||||||
|
|
||||||
|
None by default. On trigger: set OQ-04 to `open` in
|
||||||
|
`docs/architecture/open-questions.md` and spawn the focused session that
|
||||||
|
works through the push state machine.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- OQ-04 status matches this task's state in
|
||||||
|
`docs/architecture/open-questions.md`.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- Actually designing/implementing receive-pack (that is the follow-on
|
||||||
|
session's work).
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
> Filled on completion.
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
id: architecture/oq-05-sha256
|
||||||
|
name: OQ-05 unblock — sha256 ecosystem need arrives
|
||||||
|
status: pending
|
||||||
|
depends_on: []
|
||||||
|
scope: narrow
|
||||||
|
risk: trivial
|
||||||
|
impact: component
|
||||||
|
level: research
|
||||||
|
tags: [external-trigger, deferred-oq]
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Tracker for OQ-05 (`deferred(scope)`): sha256 support policy. Not
|
||||||
|
actionable work — tracks whether ecosystem need arrives (a real
|
||||||
|
client/repo requiring sha256, or upstream gix sha256 maturity worth
|
||||||
|
testing). On trigger: mark completed, OQ-05 transitions to `open`.
|
||||||
|
|
||||||
|
## Work
|
||||||
|
|
||||||
|
None by default.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- OQ-05 status matches `docs/architecture/open-questions.md`.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- sha256 implementation.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
> Filled on completion.
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
id: architecture/oq-06-metadata-backing
|
||||||
|
name: OQ-06 unblock — metadata-scale requirements arrive
|
||||||
|
status: pending
|
||||||
|
depends_on: []
|
||||||
|
scope: narrow
|
||||||
|
risk: trivial
|
||||||
|
impact: component
|
||||||
|
level: research
|
||||||
|
tags: [external-trigger, deferred-oq]
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Tracker for OQ-06 (`deferred(scope)`): registry/metadata backing store.
|
||||||
|
This task is not actionable work — it tracks whether the blocking
|
||||||
|
condition has arrived: concrete metadata-scale requirements (repo count,
|
||||||
|
metadata fields beyond id/root/visibility/ACL scope, alkcall-hub
|
||||||
|
integration scope). When they exist, mark completed; OQ-06 transitions to
|
||||||
|
`open`.
|
||||||
|
|
||||||
|
## Work
|
||||||
|
|
||||||
|
None by default. On trigger: set OQ-06 to `open` and schedule the backing
|
||||||
|
decision session.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- OQ-06 status matches `docs/architecture/open-questions.md`.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- Choosing the backing store.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
> Filled on completion.
|
||||||
Reference in new issue
Block a user