refactor(architecture): ADR-010 — pure protocol crate (alktty template)
Structural decision (OQ-09 resolved): alkgit follows the alktty/ alktunnels template — a single published protocol crate on alkcall channels, no binary, no front doors. - ADR-010 supersedes ADR-001 (crate decomposition) and ADR-006 (http router factory); both marked Superseded - Single crate at repo root: Cargo.toml with gix feature (default-on backend implementations; wire layer compiles without it — gix-hash always-on with sha1 per the compile-time-rejected invariant), crates/ workspace deleted, src/lib.rs stub in place - doors.md replaces http.md/ssh.md/alkgitd.md: alkhttp git-feature sequencing (after first publish), alkssh requirement (fixed-grammar exec dispatch), native alk/git path, downstream assembly - backend.md replaces storage.md: GitRegistry/GitRefs/GitPackGen/ GitPackIngest traits (ingest validates, refs commits — single CAS home), gix feature encodes POC-2 prerequisites - transport.md reframed for the single crate; backend traits replace hook traits in the public API - OQ-09 resolved (all five sub-decisions in ADR-010), OQ-01 resolved (subsumed), OQ-03 narrowed to publish-freeze, OQ-08 narrowed to registry identity + vault placement, OQ-07 rescoped to the gix feature's registry impl - vision.md v2: single-binary/monorepo framing corrected as init-agent artifact; POC checklist marked complete - AGENTS.md + .opencode agent specs updated to the new shape Verification: cargo build (default + no-default-features), cargo test --all-features, clippy --all-features -D warnings, fmt --check all pass. Third review round: zero critical, all warnings/suggestions addressed (GitPackGen signature amended in ADR-004, stale anchors fixed, ADR-006 body tense normalized, CAS split stated, vision residuals cleaned).
This commit is contained in:
1 parent
de922253a4
commit
86bf5a0cf0
37 files changed
+730
-947
No files matched your search
@@ -329,7 +329,7 @@ A decision should be `deferred(scope)` when:
|
||||
- The use case isn't concrete (e.g., "we don't know what the admin API
|
||||
will need from the call protocol")
|
||||
- The options depend on something that doesn't exist yet (e.g.,
|
||||
"depends on the alkgit-http streaming adapter shape")
|
||||
"depends on the alkhttp git-feature shape")
|
||||
- The trade-off requires data that can only come from implementation
|
||||
(e.g., "need performance benchmarks to choose between X and Y")
|
||||
- The decision is genuinely not needed for the current scope (e.g., "the
|
||||
|
||||
@@ -206,11 +206,12 @@ Your task: {{task}}
|
||||
8. Notify: worktree({action: "notify", args: {message: "Task completed: {{task}}. <brief summary>", level: "info"}})
|
||||
|
||||
Key project constraints (@alkdev/alkgit):
|
||||
- Rust workspace: crates/alkgit-core, -transport, -http, -ssh, alkgitd
|
||||
- Pure protocol crate: single `alkgit` at repo root (ADR-010); no
|
||||
workspace, no binary, no front-door crates
|
||||
- Rust: use cargo build, cargo clippy, cargo fmt, cargo test
|
||||
- No comments in code
|
||||
- thiserror for library error types; no panics in library code
|
||||
- Feature flags for optional surface (sha256, acme)
|
||||
- Feature flags for optional surface (gix default-on, sha256)
|
||||
- Async via tokio runtime
|
||||
- gitoxide (gix) for git storage/wire primitives; never shell out to git
|
||||
```
|
||||
|
||||
@@ -50,14 +50,16 @@ failed one.
|
||||
Git identity is preconfigured (`glm-5.3-flash <glm-5.3-flash@alk.dev>`).
|
||||
Do not change `git config`, skip hooks, or use `git commit -i`.
|
||||
|
||||
## Project Conventions (Rust / git-server workspace)
|
||||
## Project Conventions (Rust / protocol crate)
|
||||
|
||||
This is alkgit — a self-hosted git server: repository storage, the git
|
||||
smart protocol served over HTTP and SSH interfaces, and the `alkgitd`
|
||||
binary that assembles it on the alk stack (alkcall, alkhttp, alktls,
|
||||
alkvault) and gitoxide (gix). The project is a cargo workspace of
|
||||
sub-crates under `crates/`. The conventions below apply to all work in
|
||||
`crates/*/src` and `tests/`. They mirror
|
||||
This is alkgit — a pure protocol crate (ADR-010, the alktty/alktunnels
|
||||
template): the git smart protocol as producer/consumer halves on alkcall
|
||||
channels (the `alk/git` ALPN), backend traits with a feature-gated
|
||||
gitoxide (gix) implementation, no binary, no front doors (doors — alkhttp
|
||||
`git` feature, alkssh — are family infrastructure; see
|
||||
`docs/architecture/doors.md`). Single crate at the repo root (`src/`,
|
||||
`tests/`). The conventions below apply to all work in `src/` and
|
||||
`tests/`. They mirror
|
||||
`.opencode/agents/implementation-specialist.md` §Project Conventions and
|
||||
are repeated here so they apply to every session, not just spawned
|
||||
implementation agents.
|
||||
@@ -105,7 +107,7 @@ implementation agents.
|
||||
is always authenticated. This is a spec-level invariant.
|
||||
|
||||
7. **gitoxide for git primitives, no shelling out** — storage and pkt-line
|
||||
come from the gix crates (pinned in the workspace manifest). The
|
||||
come from the gix crates (pinned in the crate manifest). The
|
||||
serving path never spawns the `git` binary (no GPL dependency, no
|
||||
process-injection surface). The server half of the smart protocol is
|
||||
ours; see `docs/research/git-protocol.md` for the inventory.
|
||||
@@ -122,10 +124,11 @@ implementation agents.
|
||||
exists), its shape must not change. Protocol capability advertisement
|
||||
is honest: never advertise what we don't serve.
|
||||
|
||||
10. **Feature flags** — optional surface is feature-gated: `sha256` (hash
|
||||
algorithm passthrough; `sha1` is the default and pinned in the
|
||||
workspace manifest) and `acme` (alktls ACME wiring in the binary).
|
||||
Base crates compile lean. Verify both `cargo test` (default) and
|
||||
10. **Feature flags** — optional surface is feature-gated: `gix`
|
||||
(default-on; the backend implementations) and `sha256` (hash
|
||||
algorithm passthrough; `sha1` is the default and pinned in the crate
|
||||
manifest). The wire layer compiles without gix
|
||||
(`default-features = false`). Verify both `cargo test` (default) and
|
||||
`cargo test --all-features` pass if features are added.
|
||||
|
||||
11. **Bounded resources** — every protocol session carries wall-clock,
|
||||
@@ -144,25 +147,25 @@ implementation agents.
|
||||
constants.
|
||||
|
||||
14. **Module structure** — one module per file under `src/`, re-exported
|
||||
from `src/lib.rs`. Public API surface is `lib.rs` re-exports.
|
||||
Workspace members under `crates/`: `alkgit-core` (storage),
|
||||
`alkgit-transport` (smart protocol), `alkgit-http` (http front door),
|
||||
`alkgit-ssh` (ssh front door), `alkgitd` (binary).
|
||||
from `src/lib.rs` (the alktty crate-root pattern). Public API surface
|
||||
is `lib.rs` re-exports. Single crate at the repo root (ADR-010); the
|
||||
gix backend implementation lives under the default-on `gix` feature;
|
||||
`default-features = false` gives the wire/protocol layer only.
|
||||
|
||||
15. **Composability boundary ("ALPN as a service")** — `alkgit-core` +
|
||||
`alkgit-transport` are front-door-blind: they consume (identity, repo
|
||||
id, duplex stream, limits) and depend on alkcall types only, never on
|
||||
alkhttp/alkgit-ssh, and carry no http/channel-specific types below the
|
||||
stream. The http and ssh crates are replaceable adapters; a downstream
|
||||
app embeds core + transport and brings its own front doors. v1 is the
|
||||
reduction to storage + ACL + protocol adapters.
|
||||
15. **Composability boundary ("ALPN as a service")** — alkgit is
|
||||
front-door-blind: it consumes (identity, repo id, duplex stream,
|
||||
limits) and depends on alkcall types only, never on alkhttp/alkssh,
|
||||
and carries no http/channel-specific types below the stream. Doors
|
||||
(alkhttp `git` feature, alkssh) are family infrastructure; a
|
||||
downstream app embeds alkgit and brings its own doors. v1 is the
|
||||
reduction to wire layer + backend traits + one gix implementation.
|
||||
|
||||
## Verification Commands
|
||||
|
||||
Run these before committing. All must pass.
|
||||
|
||||
```bash
|
||||
cargo test # full suite (workspace)
|
||||
cargo test # full suite
|
||||
cargo clippy --all-targets -- -D warnings
|
||||
cargo fmt --check
|
||||
cargo doc --no-deps # if docs changed
|
||||
@@ -174,17 +177,18 @@ If feature flags are added, also run `cargo test --all-features` and
|
||||
|
||||
## Lifecycle Status
|
||||
|
||||
The project is in **SDD phase 0** (exploration) — see
|
||||
`docs/sdd_process.md` and `docs/research/README.md`. There is no
|
||||
architecture yet (`docs/architecture/` is empty; ADR numbering starts at
|
||||
001 when phase 1 begins). POC code never merges to main — two modes:
|
||||
branch mode (POC builds on repo code; git branch, findings merged into
|
||||
research docs, branch dropped) or standalone mode (POC independent of
|
||||
repo code; scratch project at `/workspace/<poc-name>`). POCs have relaxed
|
||||
The project is in **SDD phase 1** (architecture committed, implementation
|
||||
not yet begun). Phase 0 research is in `docs/research/`; the committed
|
||||
architecture is `docs/architecture/` (ADR-010 is the structural
|
||||
decision: pure protocol crate, `alk/git` ALPN, backend traits, no
|
||||
binary/doors). POC code never merges to main — two modes: branch mode
|
||||
(POC builds on repo code; git branch, findings merged into research
|
||||
docs, branch dropped) or standalone mode (POC independent of repo code;
|
||||
scratch project at `/workspace/<poc-name>`). POCs have relaxed
|
||||
constraints (comments/unwrap acceptable) — they are exploration tools,
|
||||
not production code. Until phase 1
|
||||
produces ADRs, "the architecture says" has no referent — cite
|
||||
`docs/research/` docs instead.
|
||||
not production code. Open architecture questions live in
|
||||
`docs/architecture/open-questions.md` (OQ-04 receive-pack, OQ-06
|
||||
registry backing, OQ-08 identity model are the active ones).
|
||||
|
||||
## Architecture Context
|
||||
|
||||
|
||||
+40
-28
@@ -1,46 +1,58 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"crates/alkgit-core",
|
||||
"crates/alkgit-transport",
|
||||
"crates/alkgit-http",
|
||||
"crates/alkgit-ssh",
|
||||
"crates/alkgitd",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
[package]
|
||||
name = "alkgit"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.88"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://git.alk.dev/alkdev/alkgit"
|
||||
description = "Git smart protocol for the alk family: pkt-line substrates, protocol V2 serving (upload-pack/receive-pack), producer/consumer halves on alkcall channels, and backend traits with a feature-gated gitoxide implementation"
|
||||
keywords = ["git", "protocol", "alkcall", "vcs", "network"]
|
||||
categories = ["network-programming", "asynchronous"]
|
||||
|
||||
[workspace.dependencies]
|
||||
alkgit-core = { path = "crates/alkgit-core", version = "0.0.1" }
|
||||
alkgit-transport = { path = "crates/alkgit-transport", version = "0.0.1" }
|
||||
alkgit-http = { path = "crates/alkgit-http", version = "0.0.1" }
|
||||
alkgit-ssh = { path = "crates/alkgit-ssh", version = "0.0.1" }
|
||||
[lib]
|
||||
name = "alkgit"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
default = ["gix"]
|
||||
# gitoxide-backed backend implementations (registry, refs, pack gen/ingest).
|
||||
# Disable for a wire/protocol-only embed with your own backends.
|
||||
gix = [
|
||||
"dep:gix",
|
||||
"dep:gix-odb",
|
||||
"dep:gix-pack",
|
||||
"dep:gix-ref",
|
||||
"dep:gix-object",
|
||||
"dep:gix-fsck",
|
||||
]
|
||||
# sha256 passthrough (untested end-to-end; sha1 is pinned by default — OQ-05).
|
||||
# gix-hash stays always-on with sha1: the wire layer needs it, and gix-hash
|
||||
# fails to compile with neither hash feature selected (compile-time-rejected
|
||||
# invariant, see docs/research/gitoxide.md).
|
||||
sha256 = ["gix-hash/sha256"]
|
||||
|
||||
[dependencies]
|
||||
alkcall = "0.8"
|
||||
alkhttp = "0.5"
|
||||
alktls = "0.1"
|
||||
alkvault = "0.1"
|
||||
gix = { version = "0.87", default-features = false, features = ["sha1"] }
|
||||
gix = { version = "0.87", optional = true, default-features = false, features = ["sha1"] }
|
||||
gix-odb = { version = "0.84", optional = true, default-features = false, features = ["sha1"] }
|
||||
gix-pack = { version = "0.74", optional = true, features = ["sha1"] }
|
||||
gix-ref = { version = "0.67", optional = true }
|
||||
gix-object = { version = "0.64", optional = true, features = ["sha1"] }
|
||||
gix-fsck = { version = "0.25", optional = true, features = ["sha1"] }
|
||||
gix-hash = { version = "0.26", features = ["sha1"] }
|
||||
gix-packetline = "0.22"
|
||||
gix-protocol = "0.65"
|
||||
gix-transport = "0.59"
|
||||
gix-pack = { version = "0.74", features = ["sha1"] }
|
||||
gix-odb = { version = "0.84", default-features = false, features = ["sha1"] }
|
||||
gix-ref = "0.67"
|
||||
gix-object = { version = "0.64", features = ["sha1"] }
|
||||
gix-hash = { version = "0.26", features = ["sha1"] }
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "io-util", "net", "fs", "time", "sync", "macros"] }
|
||||
tokio = { version = "1", default-features = false, features = ["rt", "sync", "io-util", "macros", "time"] }
|
||||
tokio-util = { version = "0.7", features = ["compat"] }
|
||||
futures = "0.3"
|
||||
bytes = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
thiserror = "2"
|
||||
tracing = "0.1"
|
||||
parking_lot = "0.12"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["full", "test-util", "macros"] }
|
||||
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
@@ -1,22 +0,0 @@
|
||||
[package]
|
||||
name = "alkgit-core"
|
||||
description = "Core git plumbing for alkgit: repository storage, ref management, pack handling, and access rules — transport-agnostic"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
gix = { workspace = true }
|
||||
gix-odb = { workspace = true }
|
||||
gix-pack = { workspace = true }
|
||||
gix-ref = { workspace = true }
|
||||
gix-object = { workspace = true }
|
||||
gix-hash = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
sha256 = ["gix/sha256"]
|
||||
@@ -1 +0,0 @@
|
||||
#![forbid(unsafe_code)]
|
||||
@@ -1,25 +0,0 @@
|
||||
[package]
|
||||
name = "alkgit-http"
|
||||
description = "HTTP interface for alkgit: git smart-http (info/refs, git-upload-pack, git-receive-pack) and the admin API over alkhttp"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
alkgit-core = { workspace = true }
|
||||
alkgit-transport = { workspace = true }
|
||||
alkhttp = { workspace = true }
|
||||
alkcall = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
sha256 = ["alkgit-transport/sha256"]
|
||||
@@ -1 +0,0 @@
|
||||
#![forbid(unsafe_code)]
|
||||
@@ -1,21 +0,0 @@
|
||||
[package]
|
||||
name = "alkgit-ssh"
|
||||
description = "SSH interface for alkgit: git-over-ssh command serving (git-upload-pack / git-receive-pack) on alkcall channels"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
alkgit-core = { workspace = true }
|
||||
alkgit-transport = { workspace = true }
|
||||
alkcall = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
sha256 = ["alkgit-transport/sha256"]
|
||||
@@ -1 +0,0 @@
|
||||
#![forbid(unsafe_code)]
|
||||
@@ -1,25 +0,0 @@
|
||||
[package]
|
||||
name = "alkgit-transport"
|
||||
description = "Git smart-protocol transport for alkgit: packetline framing, capability advertisement, upload-pack and receive-pack serving over alkcall BiStreams"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
alkgit-core = { workspace = true }
|
||||
alkcall = { workspace = true }
|
||||
gix-packetline = { workspace = true }
|
||||
gix-protocol = { workspace = true }
|
||||
gix-transport = { workspace = true }
|
||||
gix-hash = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
sha256 = ["alkgit-core/sha256"]
|
||||
@@ -1 +0,0 @@
|
||||
#![forbid(unsafe_code)]
|
||||
@@ -1,31 +0,0 @@
|
||||
[package]
|
||||
name = "alkgitd"
|
||||
description = "The alkgit server binary: assembles storage, smart protocol, HTTP, SSH, and TLS into one self-hosted git server"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "alkgitd"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
alkgit-core = { workspace = true }
|
||||
alkgit-transport = { workspace = true }
|
||||
alkgit-http = { workspace = true }
|
||||
alkgit-ssh = { workspace = true }
|
||||
alkhttp = { workspace = true }
|
||||
alktls = { workspace = true }
|
||||
alkvault = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
sha256 = ["alkgit-http/sha256"]
|
||||
acme = ["alktls/acme"]
|
||||
@@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
println!("alkgitd: skeleton — server assembly lands after architecture (SDD phase 1)");
|
||||
}
|
||||
+23
-28
@@ -5,56 +5,51 @@ 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.
|
||||
Phase 1 (SDD) output for alkgit — the git payload service of the alk
|
||||
family: a pure protocol crate on alkcall channels (the `alk/git` ALPN),
|
||||
following the alktty/alktunnels template (ADR-010). 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).
|
||||
Phase 1, architecture committed to the pure-protocol-crate shape (ADR-010;
|
||||
OQ-09 resolved). All docs below are `draft` except the superseded ADRs.
|
||||
POC-1/2/3 validated the git protocol half end-to-end against real git
|
||||
2.43; the remaining design work is the receive-pack state machine (OQ-04)
|
||||
and backend/identity decisions (OQ-06, OQ-08).
|
||||
|
||||
## 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 |
|
||||
| [overview.md](overview.md) | Cross-cutting: crate shape, halves, security invariants | draft |
|
||||
| [transport.md](transport.md) | Wire layer: substrates, V2 state machines, upload/receive-pack | draft |
|
||||
| [backend.md](backend.md) | Backend traits + feature-gated gix implementation | draft |
|
||||
| [doors.md](doors.md) | Door mappings: alkhttp `git` feature, alkssh requirement, native path | 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 |
|
||||
| [001](decisions/001-crate-decomposition.md) | Workspace crate decomposition (5 crates) | Superseded (ADR-010) |
|
||||
| [002](decisions/002-front-door-blind-core.md) | 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 |
|
||||
| [004](decisions/004-pack-pipeline.md) | Pack pipeline (`data::output` gen / `data::input` ingestion) | Accepted |
|
||||
| [005](decisions/005-session-substrate-types.md) | Session substrate types (duplex + stateless APIs) | Accepted |
|
||||
| [006](decisions/006-http-adapter-composition.md) | HTTP adapter composition (alkgit-owned router factory) | Superseded (ADR-010) |
|
||||
| [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).
|
||||
| [009](decisions/009-bounded-resources-budget.md) | Bounded-resources budget model | Accepted |
|
||||
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate (alktty/alktunnels template) | Accepted |
|
||||
|
||||
## 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-09 (slim-crate model: doors as family infrastructure — may supersede
|
||||
ADR-006/001 shape), OQ-04 (receive-pack validation), OQ-06 (metadata store
|
||||
backing), OQ-08 (identity sources per front door).
|
||||
OQ-04 (receive-pack validation), OQ-06 (registry backing), OQ-08 (registry
|
||||
identity space + vault placement).
|
||||
|
||||
## Document Lifecycle
|
||||
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
---
|
||||
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,103 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-09-21
|
||||
---
|
||||
|
||||
# Backend traits: the storage seam
|
||||
|
||||
## What this is
|
||||
|
||||
The payload side of the protocol crate: the traits a service deployer
|
||||
implements (or consumes via the feature-gated gix implementation) so the
|
||||
git protocol can talk to storage. Per ADR-010 this mirrors alktty's
|
||||
`TtyBackend` pattern — traits in-crate, real implementation behind a
|
||||
feature. Unlike alktunnels (no backend trait), git needs the seam: pack
|
||||
generation/ingestion is too heavy to hard-wire.
|
||||
|
||||
## The trait family (ADR-010 sub-decision 4)
|
||||
|
||||
Four traits, kept small and orthogonal — the protocol crate never sees
|
||||
gix types:
|
||||
|
||||
1. **`GitRegistry`** — repo id → (storage root, visibility, ACL scope).
|
||||
The authoritative mapping (ADR-008); resolution failure is
|
||||
indistinguishable from authorization failure (ADR-007). Metadata holds
|
||||
vault references, never secrets.
|
||||
2. **`GitRefs`** — listing for advertisement (refs + peeled tags + symref
|
||||
targets, the ls-refs response data) and ref transactions (CAS apply
|
||||
for receive-pack, name validation per git ref rules + reserved-
|
||||
namespace deny-list).
|
||||
3. **`GitPackGen`** — (repo, wants, haves, limits) → streaming pack
|
||||
(`io::Write` consumer). Negotiation-agnostic. Missing objects abort
|
||||
with an error, never a broken pack (ADR-004).
|
||||
4. **`GitPackIngest`** — client pack stream → indexed pack + fsck/
|
||||
connectivity report. It *prepares* the validated ref updates; the
|
||||
transaction itself is applied by `GitRefs` (single CAS home — ingest
|
||||
validates, refs commits). Budgeted (ADR-009 max pack size);
|
||||
blocking-thread friendly.
|
||||
|
||||
Minimal-vs-full was the open sub-question; resolved as **full family** —
|
||||
the four traits are each one or two methods plus types, and collapsing
|
||||
them (e.g. refs into the registry) would force one impl block per
|
||||
downstream where independent seams are cheaper to satisfy. The `gix`
|
||||
feature implements all four; a downstream with its own object store
|
||||
implements 3–4 and reuses 1–2, or none of it.
|
||||
|
||||
## The gix feature (default on)
|
||||
|
||||
- `alkgit = { default-features = true }` — wire layer + gix backend;
|
||||
`default-features = false` — wire/protocol layer only (an embedder
|
||||
brings its own backend). Hash: `sha1` pinned (the compile-time-rejected
|
||||
invariant from `docs/research/gitoxide.md`); `sha256` passthrough
|
||||
feature (OQ-05 policy unchanged).
|
||||
- Encodes the POC-2 prerequisites by construction: odb handle sharing
|
||||
(`Arc<Store>` shared, per-session handles, `prevent_pack_unload()` +
|
||||
`ignore_replacements = true`), generation on blocking threads,
|
||||
O(counts) memory, missing-objects abort.
|
||||
- Received-pack ingestion via `gix-pack::data::input` (`streaming-input`)
|
||||
+ `gix-fsck` + `gix-ref` transactions (ADR-004). Validation against
|
||||
real `git push` is OQ-04.
|
||||
|
||||
## Concurrency model
|
||||
|
||||
- `gix` structures: `parking_lot` short-held locks; per-session handles
|
||||
moved into `spawn_blocking` tasks (POC-2's shape: store shared, handle
|
||||
per session, generation on blocking threads).
|
||||
- Poisoned locks: `unwrap_or_else(|e| e.into_inner())` (convention 2).
|
||||
- The traits are `Send + Sync` object-safe; impls run under the
|
||||
adapter's tokio context.
|
||||
|
||||
## Public API surface
|
||||
|
||||
Crate-root re-exports (the alktty pattern): backend traits + types,
|
||||
`GitAdapter`/`register_openable` (producer), `GitSession` (consumer),
|
||||
substrate types, `Limits`, protocol error enums; `gix`-feature types
|
||||
(`GixBackend`-family) exported under the feature. The embedder-facing
|
||||
freeze point remains OQ-03 (narrowed: it is now this crate's own publish,
|
||||
not a multi-crate freeze).
|
||||
|
||||
## Design Decisions
|
||||
|
||||
| ADR | Decision | Summary |
|
||||
|---|---|---|
|
||||
| [004](decisions/004-pack-pipeline.md) | Pack pipeline | `data::output` gen / `data::input` ingestion |
|
||||
| [007](decisions/007-acl-before-advertisement.md) | ACL first | registry returns rule inputs |
|
||||
| [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 gen/ingest |
|
||||
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | traits in-crate, gix behind a feature |
|
||||
|
||||
## Open Questions
|
||||
|
||||
- **OQ-06**: registry backing store (deferred(scope) — the trait is what
|
||||
matters; the gix feature can ship a config-file/classic-on-disk impl,
|
||||
and richer backing is downstream's choice).
|
||||
- **OQ-04**: pack ingestion validation (deferred(unclear)).
|
||||
- **OQ-05**: sha256 policy (deferred(scope)).
|
||||
|
||||
## References
|
||||
|
||||
- `docs/research/gitoxide.md` (API contract notes — normative for the
|
||||
gix impl)
|
||||
- `docs/research/poc2-findings.md` (generation pipeline + prerequisites)
|
||||
- alktty `backend.rs`/`local` module (the trait + feature template)
|
||||
- ADR-010 (the structural decision)
|
||||
@@ -1,7 +1,8 @@
|
||||
# ADR-001: Workspace crate decomposition (5 crates)
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
Superseded by ADR-010 (pure protocol crate — single `alkgit`, no workspace,
|
||||
no front-door crates, no binary)
|
||||
|
||||
## Context
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Accepted
|
||||
## Context
|
||||
|
||||
The load-bearing composability rule ("ALPN as a service",
|
||||
`docs/research/vision.md`): `alkgit-core` + `alkgit-transport` must never
|
||||
`docs/research/vision.md`): alkgit (the single crate) 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.
|
||||
@@ -42,7 +42,7 @@ tuple (peer identity, resolved repo, limits):
|
||||
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,
|
||||
Storage-facing side: the wire layer calls the backend traits for advertisement data,
|
||||
pack generation (want/have set in → streaming pack out), and pack
|
||||
ingestion + ref CAS (receive-pack). Storage never sees pkt-lines.
|
||||
|
||||
@@ -70,4 +70,4 @@ follow-up 2).
|
||||
- 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"
|
||||
- overview.md §"Crate map"
|
||||
@@ -41,7 +41,7 @@ Decision drivers:
|
||||
(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
|
||||
exec requests for it get a fixed refusal (doors.md; ADR-008's
|
||||
never-execute rule).
|
||||
- HTTP requests protocol V2 only; ssh requests protocol V2 only (see
|
||||
below).
|
||||
@@ -79,4 +79,4 @@ happens then.
|
||||
- `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
|
||||
- transport.md, doors.md
|
||||
@@ -63,18 +63,23 @@ ship as compressed bases. No upstream delta-encode API exists
|
||||
|
||||
## Consequences
|
||||
|
||||
(2026-09-21 amendment, ADR-010: the crate layout changed — the type
|
||||
below is now the `GitPackGen` backend trait in the single crate,
|
||||
gix-free by signature `(repo, wants, haves, limits)`; backend.md is
|
||||
authoritative for the trait shape. The pipeline itself is unchanged.)
|
||||
|
||||
- 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).
|
||||
- The generation seam is negotiation-agnostic: boundary sets in → pack
|
||||
out (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
|
||||
- transport.md §fetch, backend.md §"The trait family" (GitPackGen)
|
||||
@@ -29,7 +29,7 @@ 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
|
||||
`alkgit` owns the substrate layer; doors and handlers see
|
||||
friendly types:
|
||||
|
||||
1. **`Session` (duplex)** — wraps (stream, limits) into the pkt-line
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
# ADR-006: HTTP adapter composition — router factory in alkgit-http
|
||||
|
||||
## Status
|
||||
Proposed (recommendation recorded; final call pending user review — OQ-01,
|
||||
and now also OQ-09, whose slim-crate model may supersede this ADR's
|
||||
shape entirely)
|
||||
Superseded by ADR-010 (pure protocol crate — the http mounting moves to an
|
||||
alkhttp `git` feature; the stateless substrate stays in `alkgit`)
|
||||
|
||||
## Context
|
||||
|
||||
@@ -45,7 +44,12 @@ Evaluation of Option B:
|
||||
published — that is an alkhttp-side decision that does not constrain
|
||||
alkgit's shape now.
|
||||
|
||||
## Decision (proposed)
|
||||
## Decision
|
||||
|
||||
(Historical: this ADR was written as a proposal with the router factory
|
||||
recommended; OQ-09/OQ-01 later resolved in favor of the alkhttp feature
|
||||
instead, and ADR-010 superseded this ADR. Text below preserved as
|
||||
written.)
|
||||
|
||||
**Option A.** `alkgit-http` owns the smart-http adapter and exposes it as
|
||||
an axum router factory; alkhttp stays git-agnostic and unchanged.
|
||||
@@ -67,12 +71,10 @@ The factory's seam is the composability surface:
|
||||
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.
|
||||
(Historical) A concrete downstream later confirmed the ergonomics
|
||||
friction; the Option-B sugar — an alkhttp `git` feature — was chosen as
|
||||
the outcome (OQ-01/OQ-09), and ADR-010 adopted it as the structural
|
||||
decision, superseding this ADR.
|
||||
|
||||
## Consequences
|
||||
|
||||
@@ -81,13 +83,13 @@ hatch here is enough.
|
||||
- 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).
|
||||
- (Historical) This ADR was Proposed pending OQ-01; OQ-01/OQ-09 resolved
|
||||
in favor of the alkhttp-feature alternative recorded below the
|
||||
escape-hatch note, and ADR-010 superseded this ADR outright.
|
||||
|
||||
## References
|
||||
- `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, OQ-09 (may supersede this ADR)
|
||||
- OQ-01 (resolved), OQ-08, OQ-09; superseded by ADR-010
|
||||
@@ -16,7 +16,7 @@ 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
|
||||
http-side check an explicit door responsibility (alkhttp `git` feature), not an inherited
|
||||
one.
|
||||
|
||||
## Decision
|
||||
@@ -55,10 +55,10 @@ Push is always authenticated on every repo, no exceptions (vision §
|
||||
- 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.
|
||||
alkhttp extra routes default permissive) — doors.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
|
||||
- ADR-008 (repo identity), doors.md (door auth mechanics)
|
||||
@@ -19,7 +19,7 @@ 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`.
|
||||
visibility + ACL scope) mapping, owned by the `GitRegistry` backend trait.
|
||||
- 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.
|
||||
@@ -43,4 +43,4 @@ server-side to configured storage roots.
|
||||
- `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)
|
||||
- backend.md §"The trait family" (GitRegistry), OQ-06 (registry backing)
|
||||
@@ -23,10 +23,12 @@ are bugs. Each POC surfaced specific unbounded surfaces that need budgets:
|
||||
|
||||
## 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`.
|
||||
Every session carries a `Limits` value, constructed by the door/adapter
|
||||
from assembler config and handed to the wire layer as part of the session
|
||||
tuple (ADR-002). On the native path the producer adapter is in-crate
|
||||
(`GitAdapter`), but `Limits` still originates from assembler config —
|
||||
the adapter's constructor takes it. Defaults are crate constants;
|
||||
overrides are assembler config.
|
||||
|
||||
| Budget | Applies to | Default direction |
|
||||
|---|---|---|
|
||||
@@ -61,4 +63,4 @@ gets the hard cap.
|
||||
- `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
|
||||
- ADR-002 (session tuple), transport.md §Limits
|
||||
@@ -0,0 +1,107 @@
|
||||
# ADR-010: Pure protocol crate — alkgit follows the alktty/alktunnels template
|
||||
|
||||
## Status
|
||||
Accepted (supersedes ADR-001, ADR-006)
|
||||
|
||||
## Context
|
||||
|
||||
OQ-09 captured a structural correction to how alkgit was initially framed.
|
||||
The monorepo + binary setup (`alkgit-core`/`alkgit-transport`/`alkgit-http`/
|
||||
`alkgit-ssh`/`alkgitd`) came from the repo-initialization agent's rendering
|
||||
of loosely-scoped use-case discussions — not a deliberate architecture
|
||||
decision. It diverged from the established family pattern: the alk
|
||||
protocol crates (alktty, alktunnels, alksocks in progress) are single
|
||||
published crates implementing a service as a producer/consumer protocol on
|
||||
top of alkcall channels, with doors (http, ssh, net) owned by separate
|
||||
family crates that downstream consumers assemble.
|
||||
|
||||
The git service fits that pattern exactly:
|
||||
|
||||
- **Producer half** = POC-1's substrate verbatim: an adapter implementing
|
||||
alkcall `ProtocolHandler` for the `alk/git` ALPN → `accept_bi()` →
|
||||
duplex V2 session, plus a `register_openable` helper so a git session is
|
||||
openable through the `alk/channels` multiplexer (alktty ADR-007/009
|
||||
pattern: the channels open-op carries the negotiation — here, the repo
|
||||
id as params, which is also the natural ACL enforcement point).
|
||||
- **Consumer half** = a typed `GitSession` client (alktty's `TtySession`
|
||||
analog) driving fetch/push against a remote alkgit service — the
|
||||
alkcall-native primitive for replication/mirroring in the alknet
|
||||
rewrite.
|
||||
- **Backend seam** = traits for registry, ref listing, pack generation,
|
||||
and pack ingestion; the gix implementation ships behind a feature flag
|
||||
(alktty's `local`-feature analog). Unlike alktunnels (no backend trait,
|
||||
their ADR-004), git needs the storage seam — pack generation/ingestion
|
||||
is too heavy to hard-wire — and unlike alktty, wasm-cleanliness is NOT a
|
||||
goal: it falls out of the wire layer being gix-free, but nothing targets
|
||||
wasm.
|
||||
- **Smart-http** = the stateless substrate (ADR-005) stays in the protocol
|
||||
crate; the http *mounting* (routes, content types,
|
||||
`HttpAdapter::with_extra_routes` wiring) becomes an alkhttp `git`
|
||||
feature — published after alkgit (see doors.md). POC-3 proved that
|
||||
mapping is thin (its route layer was a thin wrapper over the stateless
|
||||
substrate).
|
||||
- **git-over-ssh** = alkssh's job when that crate exists (after alksocks).
|
||||
The temporary russh-in-alkgitd decision (ssh.md) is dropped, not
|
||||
shipped. The requirement alkgit places on alkssh is small and recorded
|
||||
in doors.md.
|
||||
|
||||
Why now: nothing is implemented (the crates/ tree is an empty skeleton),
|
||||
so the entire migration is documentation and manifest surgery. POC work
|
||||
maps 1:1 onto the new shape, so no validation is lost.
|
||||
|
||||
## Decision
|
||||
|
||||
**alkgit is a single protocol crate** following the alktty/alktunnels
|
||||
template:
|
||||
|
||||
- One crate, one repo (no cargo workspace, no sub-crates). Published to
|
||||
crates.io as `alkgit`.
|
||||
- **Producer half**: `GitAdapter` (direct `alk/git` ALPN via
|
||||
`ProtocolHandler`) + channels `register_openable` (open-op params carry
|
||||
the repo id — the negotiation, and the ACL enforcement point).
|
||||
- **Consumer half**: `GitSession` typed client with `connect_direct` and
|
||||
`open_via_channels` constructors.
|
||||
- **Backend traits** (registry, refs, pack-gen, pack-ingest) in-crate;
|
||||
`gix` implementation behind the default-on `gix` feature (disable it to
|
||||
embed your own storage).
|
||||
- **Substrate layer in-crate**: duplex session (ADR-002 boundary) +
|
||||
stateless request/response substrate (ADR-005) — the stateless side is
|
||||
IO-abstract (request-reader/response-writer), so alkhttp's feature maps
|
||||
routes onto it without alkgit knowing http exists.
|
||||
- **No binary. No front doors.** Assembly is downstream's job (the
|
||||
platform deployment, or a future tiny assembly crate once alkssh/alknet
|
||||
exist to assemble against).
|
||||
- ALPN: `alk/git` (matches `alk/tty`, `alk/tunnel`, `alk/socks5`).
|
||||
- Session tuple, security invariants (ADR-007/008/009), V2-first protocol
|
||||
(ADR-003), pack pipeline (ADR-004), substrate types (ADR-005) all carry
|
||||
over unchanged — they are pattern-independent.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Positive: the embedder seam is *stronger* than ADR-001's shape (backend
|
||||
traits let a downstream use its own storage instead of dragging gix
|
||||
in); release surface shrinks to one crate; git becomes the first
|
||||
payload service proven across three doors (alkhttp, alkssh, alknet)
|
||||
with one protocol core; auth semantics collapse to "the door's auth"
|
||||
(OQ-08 narrows sharply); POC-validated shapes are preserved verbatim.
|
||||
- Negative: `vision.md`'s "single-binary git server" framing is amended —
|
||||
the binary was never the user's intent (OQ-09 context); git-over-http
|
||||
now ships on alkhttp's release cadence (feature lands in alkhttp 0.6
|
||||
after `alkgit` is published); git-over-ssh waits for alkssh (no ssh
|
||||
path in the interim unless a downstream adds its own wire-ssh
|
||||
termination via the duplex session — that is a legitimate embedder
|
||||
path, not alkgit scope).
|
||||
- Neutral: crate granularity sub-decision resolved as "merge" (the
|
||||
storage-only embedder concern is served by feature-gating, not
|
||||
splitting: `default-features = false` gives the wire/protocol layer
|
||||
without gix).
|
||||
- ADR-001 (crate decomposition) and ADR-006 (http router factory) are
|
||||
superseded; ssh.md/http.md/alkgitd.md are replaced by doors.md.
|
||||
|
||||
## References
|
||||
- OQ-09 (the discussion this resolves), OQ-01 (subsumed), OQ-03 (narrowed)
|
||||
- alktty docs/architecture (template), alktunnels docs/architecture (the
|
||||
no-binary, feature-gated `local` precedent)
|
||||
- POC-1/2/3 findings (the 1:1 mapping evidence)
|
||||
- ADR-002 (unchanged, load-bearing), ADR-003/004/005/007/008/009
|
||||
(carry over), doors.md, backend.md, overview.md
|
||||
@@ -0,0 +1,115 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-09-21
|
||||
---
|
||||
|
||||
# Doors: how alkgit is exposed
|
||||
|
||||
## What this is
|
||||
|
||||
alkgit (per ADR-010) owns no front doors. This document records the two
|
||||
door mappings that exist or are planned in the family, and the requirements
|
||||
alkgit places on each. The protocol crate itself is door-blind (ADR-002):
|
||||
every door converges on the same two substrates.
|
||||
|
||||
## The door pattern
|
||||
|
||||
Doors are family infrastructure: alkhttp (exists), alkssh (planned, after
|
||||
alksocks), the alknet rewrite (coming). A door wraps alkcall's
|
||||
producer/consumer in its wire protocol; services like git, tty, tunnels,
|
||||
and socks5 are payloads doors optionally expose. Downstream consumers
|
||||
(our platform deployment, a future gitea-like app) assemble the doors they
|
||||
want with the payloads they want.
|
||||
|
||||
Auth semantics are the door's auth (http: the door's token mechanism; ssh:
|
||||
the door's key-based identity). alkgit consumes the resulting alkcall
|
||||
identity — the identity-extractor seam that ADR-006 needed exists only in
|
||||
the door, where it belongs.
|
||||
|
||||
## alkhttp `git` feature (http mounting)
|
||||
|
||||
**Scope**: an alkhttp feature that maps two route shapes onto alkgit's
|
||||
stateless substrate (ADR-005):
|
||||
|
||||
| Route | Service |
|
||||
|---|---|
|
||||
| `GET /{repo}/info/refs?service=git-{upload,receive}-pack` | advertisement (smart prefix + capability dump + flush) |
|
||||
| `POST /{repo}/git-upload-pack` | V2 fetch commands (one command per POST) |
|
||||
| `POST /{repo}/git-receive-pack` | push (streaming ingestion, budgeted body) |
|
||||
| `GET /{repo}/info/refs?service=git-upload-archive` | honest refusal (not served) |
|
||||
|
||||
`{repo}` is the registry id (ADR-008); resolution + ACL run before any
|
||||
response byte (ADR-007).
|
||||
|
||||
**Sequencing**: the feature requires `alkgit` on crates.io (optional
|
||||
dependencies must resolve), so it lands in alkhttp 0.6 after alkgit's
|
||||
first publish. Until then, git-over-http is served by any downstream that
|
||||
mounts the stateless substrate directly — POC-3's `httpservice.rs` is the
|
||||
reference implementation of exactly that mapping.
|
||||
|
||||
**Framing facts the feature must honor** (all POC-3-validated, encoded in
|
||||
the substrate, not re-decided): responses end at flush (never `0002`);
|
||||
flush is one-shot per response; flush-only POSTs are probes answered
|
||||
200-empty; request bodies stream (no accumulation); response bodies
|
||||
stream under back pressure (bounded mpsc → `Body::from_stream`); request
|
||||
bodies carry a budget (ADR-009 — alkhttp custom routes are unbounded by
|
||||
default).
|
||||
|
||||
## alkssh (git-over-ssh, future)
|
||||
|
||||
**alkgit's requirement on alkssh** (to record in alkssh's spec when it
|
||||
exists): parse the exec-request string with a fixed grammar —
|
||||
`git-upload-pack '<repo>'` / `git-receive-pack '<repo>'` — never shell-
|
||||
interpret it (ADR-008's never-execute rule), map the door's key-based
|
||||
identity to the alkcall identity space, resolve the repo id against the
|
||||
registry, run ACL, and hand (identity, repo, post-auth stream, limits)
|
||||
to alkgit's duplex session. `git-upload-archive` gets a fixed refusal.
|
||||
V2 is expected (ADR-003); `GIT_PROTOCOL=version=2` rides the ssh env
|
||||
mechanism.
|
||||
|
||||
**Interim**: no git-over-ssh path ships with alkgit. A downstream that
|
||||
needs it before alkssh lands can terminate wire-ssh itself (russh or
|
||||
otherwise) and consume the duplex session — that is exactly the embedder
|
||||
path ADR-002 defines, and it works today against the POC-1 shape. It is
|
||||
an embedder assembly concern, not alkgit scope.
|
||||
|
||||
## The alkcall-native path (no adapter at all)
|
||||
|
||||
The `alk/git` ALPN producer and the channels open-op (repo id in the
|
||||
open-op params) need zero door code — POC-1 is that shape verbatim. Any
|
||||
alkcall-speaking client (including alkgit's own consumer half,
|
||||
`GitSession`) can use it. This is the baseline path; the http/ssh doors
|
||||
are conveniences layered on top for stock git clients.
|
||||
|
||||
## Assembly (downstream responsibility)
|
||||
|
||||
There is no alkgit binary (ADR-010). A deployment assembles: alkcall
|
||||
connection sources (alkhttp/alktls for http, alkssh later, raw ALPN for
|
||||
the native path) + `GitAdapter` + a backend implementation (the `gix`
|
||||
feature's, or its own) + `Limits` from config + vault for any credential
|
||||
material. Reference sequence for our platform deployment lives in that
|
||||
deployment's docs, not here.
|
||||
|
||||
## Design Decisions
|
||||
|
||||
| ADR | Decision | Summary |
|
||||
|---|---|---|
|
||||
| [002](decisions/002-front-door-blind-core.md) | Session boundary | every door consumes the same tuple |
|
||||
| [003](decisions/003-protocol-v2-first.md) | V2-first | honest advertisement per door |
|
||||
| [007](decisions/007-acl-before-advertisement.md) | ACL first | before any protocol byte |
|
||||
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry ids |
|
||||
| [009](decisions/009-bounded-resources-budget.md) | Budgets | limits on every session |
|
||||
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | doors are family infrastructure |
|
||||
|
||||
## Open Questions
|
||||
|
||||
- **OQ-08**: registry identity space + vault placement (narrowed by
|
||||
ADR-010; door auth mechanics belong to the door crates).
|
||||
|
||||
## References
|
||||
|
||||
- `docs/research/poc-1-findings.md` (duplex shape), `docs/research/poc3-findings.md`
|
||||
(http mounting evidence, framing facts)
|
||||
- alktty/alktunnels architecture docs (the template this follows)
|
||||
- ADR-010 (supersedes ADR-001/006; this file replaces http.md, ssh.md,
|
||||
alkgitd.md)
|
||||
@@ -1,96 +0,0 @@
|
||||
---
|
||||
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"
|
||||
@@ -23,46 +23,54 @@ when their impacts say so; it records how careful the resolution must be.
|
||||
|
||||
| OQ | Status | Blocked on / investigation |
|
||||
|---|---|---|
|
||||
| OQ-09 | open (discussion) | slim-crate model: doors as family infrastructure; subsumes OQ-01 if Slim-B |
|
||||
| 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-09: Slim-crate model — doors as family infrastructure, git as a payload service
|
||||
|
||||
- **Origin**: user session (2026-09-21); ADR-006, ADR-001, ssh.md
|
||||
- **Status**: **resolved** — ADR-010 (pure protocol crate, the alktty/
|
||||
alktunnels template): single `alkgit` crate, producer/consumer halves,
|
||||
backend traits with feature-gated gix, no doors, no binary; ALPN
|
||||
`alk/git`; http mounting → alkhttp `git` feature; git-over-ssh →
|
||||
alkssh (russh scaffolding dropped); the monorepo/binary framing was an
|
||||
init-agent artifact (vision amended).
|
||||
- **Resolution**: [decisions/010-pure-protocol-crate.md]. All five
|
||||
sub-decisions recorded there (ssh deletion, http home, crate
|
||||
granularity, backend-trait surface, binary fate).
|
||||
- **Cross-references**: ADR-001/006 (superseded), OQ-01, OQ-03, OQ-08,
|
||||
doors.md, backend.md, overview.md
|
||||
|
||||
### 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
|
||||
- **Origin**: user session question (OQ-01, resolved 2026-09-21)
|
||||
- **Status**: **resolved** (subsumed by OQ-09/ADR-010) — the smart-http
|
||||
stateless substrate stays in `alkgit` (IO-abstract); the http mounting
|
||||
(routes, content types, `with_extra_routes` wiring) becomes an alkhttp
|
||||
`git` feature published after alkgit's first publish. The ADR-006
|
||||
router-factory shape is superseded; the alkhttp-side feature is the
|
||||
outcome.
|
||||
- **Cross-references**: ADR-006 (superseded), ADR-010, doors.md
|
||||
|
||||
### OQ-03: Downstream embedding surface (what "embeds core + transport" means concretely)
|
||||
### OQ-03: Downstream embedding surface (what "embeds alkgit" 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
|
||||
- **Origin**: [overview.md], [transport.md], vision §"ALPN as a service"
|
||||
- **Status**: **partially resolved** — the shape is settled (ADR-010):
|
||||
embedding = one crate + backend traits (own storage via
|
||||
`default-features = false`, or the gix feature) + optional door
|
||||
features. What remains deferred is the publish-time API freeze itself:
|
||||
which type/feature names are pinned at first crates.io publish.
|
||||
- **Door type**: one-way (API freeze is registry-visible to dependents)
|
||||
- **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
|
||||
- **Impacts**: blocks the first publish only, not implementation.
|
||||
- **Blocked on**: first-publish timing (a release decision, not an
|
||||
architecture question). The API surface inventory lives in
|
||||
[backend.md](backend.md) §public API and [transport.md](transport.md)
|
||||
§public API.
|
||||
- **Cross-references**: ADR-010, ADR-002, backend.md, transport.md
|
||||
|
||||
## Theme: transport / protocol
|
||||
|
||||
@@ -109,7 +117,7 @@ when their impacts say so; it records how careful the resolution must be.
|
||||
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
|
||||
§receive-pack, backend.md §"The trait family" (GitRefs), doors.md
|
||||
|
||||
### OQ-05: sha256 support policy
|
||||
|
||||
@@ -126,106 +134,59 @@ when their impacts say so; it records how careful the resolution must be.
|
||||
|
||||
## Theme: identity / auth
|
||||
|
||||
### OQ-08: Identity sources per front door (v1 auth mechanics)
|
||||
### OQ-08: Registry identity space + vault placement (narrowed)
|
||||
|
||||
- **Origin**: [overview.md], [http.md], [ssh.md], [alkgitd.md]
|
||||
- **Status**: open
|
||||
- **Origin**: [overview.md], [doors.md], [backend.md]; originally "identity
|
||||
sources per front door"
|
||||
- **Status**: open — narrowed by ADR-010. Door auth mechanics (http token
|
||||
handling, ssh keys) belong to the door crates; what remains for alkgit
|
||||
is: the identity model the `GitRegistry` knows (what an identity is,
|
||||
what identity records exist, whether they live in the same metadata
|
||||
store as repo records — OQ-06's field list may grow), and where
|
||||
credential material lives (alkvault; metadata holds references only).
|
||||
- **Door type**: two-way
|
||||
- **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
|
||||
- **Impacts**: blocks backend.md's registry trait field list finalizing;
|
||||
blocks the admin-ops shapes (OQ-07).
|
||||
- **Resolution path**: one focused session on the registry identity model
|
||||
+ alkvault placement.
|
||||
- **Cross-references**: ADR-007, ADR-010, OQ-06, OQ-07, backend.md
|
||||
§GitRegistry, doors.md
|
||||
|
||||
## Theme: storage / metadata
|
||||
|
||||
### OQ-06: Registry/metadata backing store
|
||||
### OQ-06: Registry/metadata backing store (gix feature)
|
||||
|
||||
- **Origin**: [storage.md], ADR-008
|
||||
- **Origin**: [backend.md] (was 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).
|
||||
- **Door type**: two-way (backing choice is swappable behind the
|
||||
`GitRegistry` trait)
|
||||
- **Priority**: high for the gix feature's default story, but choice
|
||||
deferrable because the trait boundary is what matters
|
||||
- **Impacts**: blocks backend.md's gix-feature registry impl finalizing;
|
||||
does NOT block the wire layer (it codes 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:
|
||||
identity records join — OQ-08's output, whether 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)
|
||||
- **Cross-references**: ADR-008, ADR-010, backend.md §"The trait family" (GitRegistry), 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
|
||||
- **Origin**: [overview.md], alk-stack.md §"The gitea lesson"
|
||||
- **Status**: open — rescope note (ADR-010): there is no alkgit binary, so
|
||||
there is no alkgit-owned admin surface. The question narrows to whether
|
||||
the gix feature's `GitRegistry` implementation ships with any management
|
||||
ops (repo create/delete, visibility, ACL grant) as reusable alkcall ops,
|
||||
or whether registry mutation is entirely downstream assembly work. If
|
||||
shipped, they are `Visibility::Internal` alkcall ops over the
|
||||
assembler's admin listener — never the git traffic surface.
|
||||
- **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
|
||||
|
||||
### OQ-09: Slim-crate model — doors as family infrastructure, git as a payload service
|
||||
|
||||
- **Origin**: user session (2026-09-21); ADR-006, ADR-001, ssh.md
|
||||
- **Status**: open — under discussion, no commitment
|
||||
- **Door type**: one-way once anything is published (crate deletion and
|
||||
feature-promotion are wire/registry-visible to dependents)
|
||||
- **Priority**: high
|
||||
- **Impacts**: blocks ADR-006 finalization (OQ-01 is subsumed by this if
|
||||
Slim-B is chosen); reshapes ADR-001 (crate set), ssh.md (delete or
|
||||
defer), OQ-08 (narrows: git consumes door identity), alkgitd's assembly
|
||||
surface, and publish sequencing (alkhttp `git` feature requires
|
||||
alkgit-transport on crates.io first).
|
||||
- **Context**: the alk family shares doors — alkhttp exists, alkssh is
|
||||
planned (after alksocks), alknet rewrite coming. Doors wrap alkcall
|
||||
producer/consumer in their wire protocol; services (git, tty, tunnels,
|
||||
socks) are payloads doors optionally expose. git should be a service
|
||||
exposed by downstream consumers rather than owning its own doors.
|
||||
Candidate end-states: **Slim-A** — keep alkgit-http factory, 5-crate
|
||||
layout, ADR-006 Option A as written; **Slim-B** — protocol crates +
|
||||
alkhttp `git` feature for smart-http (requires alkgit published first);
|
||||
**Option C (pure protocol crate)** — follow the alktty/alktunnels
|
||||
template exactly: single `alkgit` crate, producer half (`GitAdapter`
|
||||
for `alk/git` ALPN + channels `register_openable`, POC-1 substrate),
|
||||
consumer half (typed `GitSession` — the replication/mirroring
|
||||
primitive), backend traits (registry/refs/pack-gen/pack-ingest) with
|
||||
the gix implementation feature-gated (mirrors alktty's `local`; NOT a
|
||||
wasm goal, the cleanliness just falls out), smart-http stateless
|
||||
substrate stays in-crate while the http mounting becomes alkhttp's
|
||||
`git` feature. No binary; assembly is downstream's. Consequences to
|
||||
record on commitment: vision.md's "single-binary git server" framing
|
||||
is amended (assembly belongs to downstream consumers); ADR-001/006
|
||||
superseded by a new ADR; ssh.md defers entirely to alkssh (the
|
||||
temporary russh scaffolding decision is dropped, not shipped); OQ-08
|
||||
narrows to the registry identity space + vault placement.
|
||||
- **Sub-decisions**: (1) delete `alkgit-ssh` now and defer git-over-ssh to
|
||||
alkssh, or keep temporary russh scaffolding in alkgitd until then
|
||||
(hinges on whether our deployment needs git-over-ssh before alkssh
|
||||
lands); (2) http adapter home — Slim-A (keep alkgit-http, ADR-006
|
||||
Option A as written) vs Slim-B (alkhttp 0.6 `git` feature; requires
|
||||
alkgit published first); (3) crate granularity — keep core+transport
|
||||
split (embedders wanting storage-only) vs merge into one `alkgit`
|
||||
(Option C's shape); (4) under Option C, backend-trait surface: full
|
||||
family (registry, refs, pack-gen, pack-ingest) vs minimal (pack +
|
||||
registry, refs ride the registry trait); (5) alkgitd's fate — deleted,
|
||||
or kept as a thin reference assembly once doors exist to assemble
|
||||
against.
|
||||
- **Resolution path**: dedicated discussion session; decide the three
|
||||
sub-decisions, then rewrite ADR-001/006 and ssh.md (a new ADR superseding
|
||||
the affected parts, per ADR stability rules), and re-scope OQ-08.
|
||||
- **Cross-references**: OQ-01, OQ-03, OQ-08, ADR-001, ADR-006, ssh.md,
|
||||
alkgitd.md
|
||||
- **Impacts**: blocks the gix feature's registry impl scope; OQ-08's
|
||||
identity model determines the op shapes.
|
||||
- **Resolution path**: decide alongside OQ-08 (one session can settle
|
||||
both).
|
||||
- **Cross-references**: ADR-007, ADR-010, OQ-08, backend.md §"The trait family" (GitRegistry)
|
||||
@@ -7,113 +7,89 @@ last_updated: 2026-09-21
|
||||
|
||||
## 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.
|
||||
alkgit is the git payload service of the alk family: a pure protocol crate
|
||||
(per ADR-010, following the alktty/alktunnels template) implementing the
|
||||
git smart protocol over alkcall channels — the `alk/git` ALPN. It provides
|
||||
repository storage as backend traits (with a feature-gated gitoxide
|
||||
implementation), the git smart protocol as producer/consumer halves, and
|
||||
nothing else: no binary, no front doors. The original framing of this repo
|
||||
(a monorepo with an `alkgitd` binary and its own http/ssh crates) was an
|
||||
init-agent artifact corrected by OQ-09/ADR-010; `docs/research/vision.md`
|
||||
is amended accordingly.
|
||||
|
||||
## The one-line architecture
|
||||
|
||||
**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.
|
||||
**One protocol crate: producer + consumer + backend traits, gix behind a
|
||||
feature.** Doors (alkhttp, alkssh, alknet) expose it; assembly belongs to
|
||||
downstream consumers.
|
||||
|
||||
## 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 | — |
|
||||
Single crate `alkgit`:
|
||||
|
||||
Dependency rules (ADR-001, ADR-002):
|
||||
| Half | Contents | POC evidence |
|
||||
|---|---|---|
|
||||
| Producer | `GitAdapter` (`alk/git` ALPN via alkcall `ProtocolHandler`), channels `register_openable` (repo id in open-op params — the negotiation + ACL point) | POC-1 verbatim |
|
||||
| Consumer | `GitSession` typed client (`connect_direct`, `open_via_channels`) — the replication/mirroring primitive for alknet | new, small (TtySession analog) |
|
||||
| Substrate | duplex session + stateless request/response layer (ADR-005); wire framing, V2 state machines (ADR-003) | POC-1, POC-3 |
|
||||
| Backends | `GitRegistry`, `GitRefs`, `GitPackGen`, `GitPackIngest` traits; gix impl behind the default-on `gix` feature | POC-2 (gix impl) |
|
||||
|
||||
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.
|
||||
Feature model: `default-features = false` gives the wire/protocol layer
|
||||
without gix (wasm-clean as a side effect, not a goal); the `sha256`
|
||||
passthrough and (eventually, in alkhttp) the `git` door feature ride the
|
||||
same pattern. Doors live in the door crates — see [doors.md](doors.md).
|
||||
|
||||
## Security invariants (spec-level, all components honor)
|
||||
## Security invariants (spec-level, carried from vision/principles)
|
||||
|
||||
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.
|
||||
1. **Authenticated by default** — anonymous fetch only on explicitly-
|
||||
public repos; push always authenticated.
|
||||
2. **Visible-surface = authorized-surface** — alkcall ACL end-to-end;
|
||||
internal ops never wire-callable.
|
||||
3. **ACL before advertisement** — nothing is emitted before the check
|
||||
(ADR-007); the channels open-op carrying the repo id is the natural
|
||||
enforcement point on the native path.
|
||||
4. **Registry-resolved repo identity** — wire names are ids, never paths
|
||||
(ADR-008).
|
||||
5. **No secret material on the wire or at rest outside alkvault** —
|
||||
metadata holds vault references; no env-var credential reads.
|
||||
6. **No shelling out to `git`** — pure Rust on gix primitives.
|
||||
7. **Bounded resources** — every session carries `Limits` (ADR-009).
|
||||
8. **Honest capability advertisement** — advertise exactly what we serve
|
||||
(ADR-003).
|
||||
|
||||
## What is already validated (POC-backed)
|
||||
|
||||
- pkt-line over alkcall `BiStream` end-to-end (POC-1).
|
||||
- Pack generation pipeline streaming with O(counts) memory (POC-2).
|
||||
- Smart-http streaming both ways through alkhttp custom routes (POC-3).
|
||||
- pkt-line over alkcall `BiStream` end-to-end (POC-1 → producer half).
|
||||
- Pack generation pipeline streaming with O(counts) memory (POC-2 → gix
|
||||
backend impl).
|
||||
- Smart-http streaming both ways (POC-3 → the stateless substrate that
|
||||
alkhttp's future `git` feature maps onto; `docs/research/poc3-findings.md`
|
||||
§alkhttp fit is the mounting reference).
|
||||
The full V2 fetch path against real git 2.43 is proven; receive-pack is
|
||||
designed but not yet exercised (OQ-04 tracks the POC/validation gap).
|
||||
designed but not yet exercised (OQ-04).
|
||||
|
||||
## 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 |
|
||||
| [001](decisions/001-crate-decomposition.md) | Crate decomposition | **superseded by ADR-010** |
|
||||
| [002](decisions/002-front-door-blind-core.md) | Session boundary | (identity, repo, stream, limits) — unchanged, load-bearing |
|
||||
| [003](decisions/003-protocol-v2-first.md) | V2-first protocol | V2-only both doors; honest advertisement |
|
||||
| [004](decisions/004-pack-pipeline.md) | Pack pipeline | `data::output` gen / `data::input` ingestion |
|
||||
| [005](decisions/005-session-substrate-types.md) | Substrate types | duplex + stateless APIs over one state machine |
|
||||
| [006](decisions/006-http-adapter-composition.md) | HTTP adapter composition | **superseded by ADR-010** (mounting → alkhttp feature) |
|
||||
| [007](decisions/007-acl-before-advertisement.md) | ACL first | no ref/capability line before ACL passes |
|
||||
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry IDs |
|
||||
| [009](decisions/009-bounded-resources-budget.md) | Budgets | every session carries limits |
|
||||
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | single crate, producer/consumer halves, no doors/binary |
|
||||
|
||||
## Open Questions
|
||||
|
||||
Key cross-cutting questions tracked in [open-questions.md](open-questions.md):
|
||||
Key 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).
|
||||
- **OQ-06**: registry backing store for the gix feature (deferred on
|
||||
scale requirements).
|
||||
- **OQ-08**: registry identity space + vault placement (narrowed by
|
||||
ADR-010).
|
||||
@@ -1,106 +0,0 @@
|
||||
---
|
||||
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"
|
||||
@@ -1,117 +0,0 @@
|
||||
---
|
||||
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"
|
||||
@@ -3,28 +3,34 @@ status: draft
|
||||
last_updated: 2026-09-21
|
||||
---
|
||||
|
||||
# alkgit-transport: Git Smart Protocol
|
||||
# alkgit: Git Smart Protocol (wire layer)
|
||||
|
||||
## 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).
|
||||
The wire half of the `alkgit` protocol crate: pkt-line session substrates,
|
||||
protocol V2 state machines (advertisement, ls-refs, fetch, receive-pack),
|
||||
and the substrate types that keep doors and handlers from touching
|
||||
`futures_io` or raw packetline APIs. Depends on alkcall and — only under
|
||||
the `gix` feature — the backend implementations (ADR-010). The wire layer
|
||||
itself is backend-trait-only, which is what makes `default-features =
|
||||
false` compile without gix.
|
||||
|
||||
## 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).
|
||||
- **Duplex session** (the `alk/git` ALPN producer, channels-opened git
|
||||
sessions, embedder 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 doors, e.g. alkhttp's future `git`
|
||||
feature) — 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). IO-abstract:
|
||||
the door supplies reader/writer; see [doors.md](doors.md) for the
|
||||
mounting.
|
||||
|
||||
Both substrates feed the same V2 state machines; statelessness is a
|
||||
substrate property (per-request state), not a protocol fork.
|
||||
@@ -40,12 +46,15 @@ substrate property (per-request state), not a protocol fork.
|
||||
`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).
|
||||
(validated against real git, POC-1). `git-upload-archive` is not
|
||||
served (fixed refusal — ADR-008's never-execute rule, ssh analog in
|
||||
doors.md).
|
||||
|
||||
### ls-refs
|
||||
|
||||
- Parse `command=ls-refs` (peel, symrefs, ref-prefix), stream ref lines
|
||||
from core's listing, flush. `ref-prefix` filtering is client-driven.
|
||||
from the backend's listing, flush. `ref-prefix` filtering is
|
||||
client-driven.
|
||||
|
||||
### fetch
|
||||
|
||||
@@ -53,10 +62,10 @@ substrate property (per-request state), not a protocol fork.
|
||||
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).
|
||||
- Pack generation via `GitPackGen` (ADR-004), streamed over sideband on
|
||||
duplex / sideband-in-response on http; generation runs on
|
||||
`spawn_blocking` with the owned 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)
|
||||
@@ -66,8 +75,9 @@ substrate property (per-request state), not a protocol fork.
|
||||
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).
|
||||
- Ingestion via `GitPackIngest` (ADR-004); validation pending — **OQ-04**
|
||||
(includes the receive-pack version surface, which ADR-003's fetch-V2
|
||||
decision does not cover).
|
||||
|
||||
### Error taxonomy
|
||||
|
||||
@@ -83,10 +93,11 @@ round, receive-pack max size, wall clock, sideband chunk size (fixed
|
||||
|
||||
## 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**.
|
||||
Crate-root re-exports (alktty pattern; the full list in
|
||||
[backend.md](backend.md) §public API): `GitAdapter` + `register_openable`,
|
||||
`GitSession`, substrate types, `Limits`, the backend traits (ADR-010's
|
||||
seam), protocol error enums; gix-feature types under the feature.
|
||||
Publish-freeze point: **OQ-03**.
|
||||
|
||||
## Design Decisions
|
||||
|
||||
@@ -97,18 +108,22 @@ The exact embedder-facing freeze point: **OQ-03**.
|
||||
| [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 |
|
||||
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | wire layer is backend-trait-only |
|
||||
|
||||
## 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-03**: publish/API freeze (partially resolved — single-crate shape
|
||||
settled by ADR-010).
|
||||
- **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/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)
|
||||
- `docs/research/gitoxide.md` §"Wire format" (packetline contracts)
|
||||
- alktty `wire.rs`/`session.rs`/`adapter.rs` (the template's half shapes)
|
||||
+10
-2
@@ -4,7 +4,7 @@ Phase 0 (exploration) research. Feeds phase 1 (architecture).
|
||||
|
||||
| Doc | Topic | Status |
|
||||
|---|---|---|
|
||||
| [vision.md](vision.md) | Vision, guiding principles, non-goals, phase-0 checklist | draft v1 |
|
||||
| [vision.md](vision.md) | Vision, guiding principles, non-goals, phase-0 checklist | draft v2 (amended 2026-09-21)
|
||||
| [gitoxide.md](gitoxide.md) | gitoxide (gix) capability + version alignment | initial pass complete |
|
||||
| [alk-stack.md](alk-stack.md) | alk stack fit, integration surface, gitea-lesson constraints | initial pass complete |
|
||||
| [git-protocol.md](git-protocol.md) | Server-side git smart protocol inventory (what we own) | initial pass complete |
|
||||
@@ -27,4 +27,12 @@ Phase 0 (exploration) research. Feeds phase 1 (architecture).
|
||||
|
||||
Phase 0 is done when POC-1..3 have results and a recommended-approach
|
||||
summary is written here (append below). Then the Architect produces
|
||||
`docs/architecture/` per sdd_process.
|
||||
`docs/architecture/` per sdd_process.
|
||||
|
||||
**Convergence (2026-09-21)**: phase 0 gates all pass (POC-1/2/3 proceed).
|
||||
Phase 1 opened with `docs/architecture/`; the structural decision is
|
||||
ADR-010 — pure protocol crate following the alktty/alktunnels template
|
||||
(the v1 "monorepo + alkgitd binary" framing in these research docs was an
|
||||
init-agent artifact, amended in vision.md v2). POC-1 maps to the producer
|
||||
half, POC-2 to the gix backend implementation, POC-3 to the stateless
|
||||
substrate consumed by alkhttp's future `git` feature.
|
||||
+52
-39
@@ -1,18 +1,23 @@
|
||||
# alkgit Phase 0: Vision and Guiding Principles
|
||||
|
||||
**Status**: draft v1 — 2026-09-19
|
||||
**Status**: draft v2 — 2026-09-21 (v1 2026-09-19; v2 amends the delivery
|
||||
shape — see "Sub-crate shape")
|
||||
**Phase**: 0 (exploration) — this document captures WHAT we are building and
|
||||
WHY before architecture (phase 1) commits to HOW.
|
||||
|
||||
## Vision
|
||||
|
||||
A self-hosted, single-binary git server in Rust: `alkgitd` serves repositories
|
||||
over **http** and **ssh** interfaces with an authenticated-by-default surface,
|
||||
built on the alk stack (alkcall, alkhttp, alktls, alkvault) and gitoxide.
|
||||
It exists because self-hosted git platforms (gitea/gitlab) are large
|
||||
multi-component applications with a long-tail of exposed APIs, plaintext
|
||||
secret storage, and web-UI attack surface — and because CVE-2026-59774
|
||||
(gitea) demonstrated that a single logic bug in an internally-exposed API is
|
||||
A git payload service for the alk family, in Rust: alkgit implements the
|
||||
git smart protocol as a **pure protocol crate** on alkcall channels (the
|
||||
`alk/git` ALPN) — storage behind backend traits (gitoxide implementation
|
||||
behind a feature), producer/consumer halves, no binary, no front doors.
|
||||
Doors are family infrastructure: alkhttp exposes smart-http, alkssh
|
||||
(planned) will expose git-over-ssh, the alknet rewrite carries the native
|
||||
path; downstream consumers assemble what they want. It exists because
|
||||
self-hosted git platforms (gitea/gitlab) are large multi-component
|
||||
applications with a long-tail of exposed APIs, plaintext secret storage,
|
||||
and web-UI attack surface — and because CVE-2026-59774 (gitea)
|
||||
demonstrated that a single logic bug in an internally-exposed API is
|
||||
enough for full compromise of the host. Our blast-radius and exposure model
|
||||
is designed from day one, not retrofitted.
|
||||
|
||||
@@ -27,10 +32,11 @@ is designed from day one, not retrofitted.
|
||||
2. **Secret hygiene**: gitea stores secrets plaintext in its DB. alkgit uses
|
||||
alkvault for anything credential-shaped; the metadata store holds vault
|
||||
references, never plaintext secrets.
|
||||
3. **Footprint**: a git server is a small program (storage + smart protocol +
|
||||
two front doors). The platform features (issues, PRs, wikis, CI) are where
|
||||
the CVEs live. alkgit v1 is deliberately just the git server; everything
|
||||
else stays out.
|
||||
3. **Footprint**: a git server is a small program (storage + smart
|
||||
protocol + thin doors); the platform features (issues, PRs, CI) are where
|
||||
the CVEs live. alkgit v1 is deliberately just the git service as a
|
||||
protocol crate; everything else stays out (doors live in the door
|
||||
crates — alkhttp, alkssh — per ADR-010).
|
||||
4. **Language**: the whole serving path is Rust (memory-safe, no C deps in
|
||||
the packet path).
|
||||
|
||||
@@ -62,42 +68,49 @@ is designed from day one, not retrofitted.
|
||||
- Issues/PR/review features.
|
||||
- Git LFS (later phase, separate decision).
|
||||
- federation/replication between alkgit instances (later phase).
|
||||
- Serving as a general sshd (only the git command surface).
|
||||
- Serving as a general sshd (alkgit carries no ssh surface at all;
|
||||
alkssh is the door).
|
||||
- Windows as a serving platform (linux first; keep code portable-ish but
|
||||
don't test it).
|
||||
|
||||
## Sub-crate shape (provisional, matches workspace skeleton)
|
||||
## Sub-crate shape (amended 2026-09-21 by ADR-010)
|
||||
|
||||
| crate | role |
|
||||
The v1 draft's table below was an init-agent artifact (monorepo + binary +
|
||||
own doors); the corrected shape is the alktty/alktunnels pattern — see
|
||||
`docs/architecture/decisions/010-pure-protocol-crate.md`:
|
||||
|
||||
| Piece | Role |
|
||||
|---|---|
|
||||
| `alkgit-core` | storage: repos, refs, odb, pack read/write, access-rule types |
|
||||
| `alkgit-transport` | smart protocol: pkt-line sessions, advertise, ls-refs, fetch, receive-pack |
|
||||
| `alkgit-http` | http front door (smart-http endpoints + admin API via alkhttp) |
|
||||
| `alkgit-ssh` | ssh front door (git commands over alkcall channels) |
|
||||
| `alkgitd` | binary: config, assembly, TLS/ACME, serving loops |
|
||||
| `alkgit` (single crate) | wire layer (substrates, V2 state machines) + producer/consumer halves + backend traits |
|
||||
| `gix` feature (default on) | gitoxide-backed implementation of the backend traits |
|
||||
| alkhttp `git` feature (future) | smart-http mounting of the stateless substrate |
|
||||
| alkssh (future family crate) | git-over-ssh door |
|
||||
| downstream assembly | the actual deployment (config, listeners, TLS/ACME, vault) |
|
||||
|
||||
## Composability: "ALPN as a service"
|
||||
|
||||
alkgit is the git member of the alk "ALPN as a service" family — alktty,
|
||||
alktunnels, and alksocks (socks5) follow the same pattern, and the alknet
|
||||
mono-repo is being decomposed into exactly these pieces for rewrite. The
|
||||
pattern's load-bearing rule: **`alkgit-core` + `alkgit-transport` never know
|
||||
which front door is talking.** The transport layer consumes (identity, repo
|
||||
pattern's load-bearing rule: **alkgit never knows which front door is
|
||||
talking.** The protocol crate consumes (identity, repo
|
||||
id, duplex stream, limits) and speaks git; everything above the stream is
|
||||
the adapter's problem. That is what keeps alkgit composable for downstream
|
||||
the door's problem. That is what keeps alkgit composable for downstream
|
||||
use:
|
||||
|
||||
- A future gitea/gitlab-like application should be able to embed
|
||||
`alkgit-core` + `alkgit-transport` (or talk to `alkgitd`) and add its own
|
||||
UI/issues/PR layer without forking anything here.
|
||||
- The admin API is a set of alkcall ops, not an embedded web framework —
|
||||
a downstream app can either use it or replace it.
|
||||
- Nothing in the core may reach upward into alkhttp/alkssh concerns
|
||||
- A future gitea/gitlab-like application embeds `alkgit` (wire layer, with
|
||||
its own backend implementation via the traits or the gix feature) and
|
||||
adds its own UI/issues/PR layer without forking anything here.
|
||||
- Registry management ops, if the gix feature ships any, are a set of
|
||||
alkcall ops, not an embedded web framework — a downstream app can either
|
||||
use them or replace them (OQ-07).
|
||||
- Nothing in the crate may reach upward into alkhttp/alkssh concerns
|
||||
(no http types, no channel types below the transport boundary).
|
||||
|
||||
The v1 reduction follows from this: **storage + ACL + protocol adapters**.
|
||||
A simple static/template web UI for the public repo listing is explicitly
|
||||
out of scope here (would be a separate downstream thing on top).
|
||||
The v1 reduction follows from this: **wire layer + backend traits + one
|
||||
gix implementation**. A simple static/template web UI for the public repo
|
||||
listing is explicitly out of scope here (would be a separate downstream
|
||||
thing on top).
|
||||
|
||||
## Primary deployment target
|
||||
|
||||
@@ -117,19 +130,19 @@ in use**. So:
|
||||
- [x] alk stack fit + integration surface (`alk-stack.md`)
|
||||
- [x] server-side protocol inventory (`git-protocol.md`)
|
||||
- [x] license/reference policy (`reference-policy.md`)
|
||||
- [ ] POC-1: real `git clone` over alkcall BiStream using gix-packetline
|
||||
- [x] POC-1: real `git clone` over alkcall BiStream using gix-packetline
|
||||
(validates BiStream fit + packetline async codec end-to-end)
|
||||
- [ ] POC-2: server-side pack generation composition (bundle-write vs
|
||||
- [x] POC-2: server-side pack generation composition (bundle-write vs
|
||||
entries-to-bytes) for a small want/have set
|
||||
- [ ] POC-3: http smart-endpoint streaming shape through alkhttp
|
||||
- [x] POC-3: http smart-endpoint streaming shape through alkhttp
|
||||
(chunked pack response, unbuffered receive-pack POST ingestion)
|
||||
- [ ] Convergence: recommended approach summary feeding phase 1 architecture
|
||||
- [x] Convergence: recommended approach summary feeding phase 1 architecture
|
||||
|
||||
## Immediate threat-model notes carried into architecture
|
||||
|
||||
- The admin API (repo/user/permission management) is alkcall
|
||||
`Visibility::Internal` ops over an admin-only listener; it is never part
|
||||
of the git traffic surface.
|
||||
- Registry management ops, if the gix feature ships any, are alkcall
|
||||
`Visibility::Internal` ops over an admin-only listener; they are never
|
||||
part of the git traffic surface (OQ-07).
|
||||
- Repo names arriving on the wire are registry IDs, never paths; storage
|
||||
roots are configured server-side only.
|
||||
- The advertisement phase runs ACL before the first ref line is emitted.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
//! alkgit: git smart protocol for the alk family.
|
||||
//!
|
||||
//! Producer/consumer protocol crate on alkcall channels — the `alk/git`
|
||||
//! ALPN (ADR-010, the alktty/alktunnels template). Phase 1 (SDD) has just
|
||||
//! begun: this crate currently contains only the module skeleton markers;
|
||||
//! the substrate layer, V2 state machines, and backend traits land per
|
||||
//! `docs/architecture/` (see transport.md, backend.md, doors.md).
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
Reference in new issue
Block a user