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
|
- The use case isn't concrete (e.g., "we don't know what the admin API
|
||||||
will need from the call protocol")
|
will need from the call protocol")
|
||||||
- The options depend on something that doesn't exist yet (e.g.,
|
- 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
|
- The trade-off requires data that can only come from implementation
|
||||||
(e.g., "need performance benchmarks to choose between X and Y")
|
(e.g., "need performance benchmarks to choose between X and Y")
|
||||||
- The decision is genuinely not needed for the current scope (e.g., "the
|
- 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"}})
|
8. Notify: worktree({action: "notify", args: {message: "Task completed: {{task}}. <brief summary>", level: "info"}})
|
||||||
|
|
||||||
Key project constraints (@alkdev/alkgit):
|
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
|
- Rust: use cargo build, cargo clippy, cargo fmt, cargo test
|
||||||
- No comments in code
|
- No comments in code
|
||||||
- thiserror for library error types; no panics in library 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
|
- Async via tokio runtime
|
||||||
- gitoxide (gix) for git storage/wire primitives; never shell out to git
|
- 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>`).
|
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`.
|
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
|
This is alkgit — a pure protocol crate (ADR-010, the alktty/alktunnels
|
||||||
smart protocol served over HTTP and SSH interfaces, and the `alkgitd`
|
template): the git smart protocol as producer/consumer halves on alkcall
|
||||||
binary that assembles it on the alk stack (alkcall, alkhttp, alktls,
|
channels (the `alk/git` ALPN), backend traits with a feature-gated
|
||||||
alkvault) and gitoxide (gix). The project is a cargo workspace of
|
gitoxide (gix) implementation, no binary, no front doors (doors — alkhttp
|
||||||
sub-crates under `crates/`. The conventions below apply to all work in
|
`git` feature, alkssh — are family infrastructure; see
|
||||||
`crates/*/src` and `tests/`. They mirror
|
`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
|
`.opencode/agents/implementation-specialist.md` §Project Conventions and
|
||||||
are repeated here so they apply to every session, not just spawned
|
are repeated here so they apply to every session, not just spawned
|
||||||
implementation agents.
|
implementation agents.
|
||||||
@@ -105,7 +107,7 @@ implementation agents.
|
|||||||
is always authenticated. This is a spec-level invariant.
|
is always authenticated. This is a spec-level invariant.
|
||||||
|
|
||||||
7. **gitoxide for git primitives, no shelling out** — storage and pkt-line
|
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
|
serving path never spawns the `git` binary (no GPL dependency, no
|
||||||
process-injection surface). The server half of the smart protocol is
|
process-injection surface). The server half of the smart protocol is
|
||||||
ours; see `docs/research/git-protocol.md` for the inventory.
|
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
|
exists), its shape must not change. Protocol capability advertisement
|
||||||
is honest: never advertise what we don't serve.
|
is honest: never advertise what we don't serve.
|
||||||
|
|
||||||
10. **Feature flags** — optional surface is feature-gated: `sha256` (hash
|
10. **Feature flags** — optional surface is feature-gated: `gix`
|
||||||
algorithm passthrough; `sha1` is the default and pinned in the
|
(default-on; the backend implementations) and `sha256` (hash
|
||||||
workspace manifest) and `acme` (alktls ACME wiring in the binary).
|
algorithm passthrough; `sha1` is the default and pinned in the crate
|
||||||
Base crates compile lean. Verify both `cargo test` (default) and
|
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.
|
`cargo test --all-features` pass if features are added.
|
||||||
|
|
||||||
11. **Bounded resources** — every protocol session carries wall-clock,
|
11. **Bounded resources** — every protocol session carries wall-clock,
|
||||||
@@ -144,25 +147,25 @@ implementation agents.
|
|||||||
constants.
|
constants.
|
||||||
|
|
||||||
14. **Module structure** — one module per file under `src/`, re-exported
|
14. **Module structure** — one module per file under `src/`, re-exported
|
||||||
from `src/lib.rs`. Public API surface is `lib.rs` re-exports.
|
from `src/lib.rs` (the alktty crate-root pattern). Public API surface
|
||||||
Workspace members under `crates/`: `alkgit-core` (storage),
|
is `lib.rs` re-exports. Single crate at the repo root (ADR-010); the
|
||||||
`alkgit-transport` (smart protocol), `alkgit-http` (http front door),
|
gix backend implementation lives under the default-on `gix` feature;
|
||||||
`alkgit-ssh` (ssh front door), `alkgitd` (binary).
|
`default-features = false` gives the wire/protocol layer only.
|
||||||
|
|
||||||
15. **Composability boundary ("ALPN as a service")** — `alkgit-core` +
|
15. **Composability boundary ("ALPN as a service")** — alkgit is
|
||||||
`alkgit-transport` are front-door-blind: they consume (identity, repo
|
front-door-blind: it consumes (identity, repo id, duplex stream,
|
||||||
id, duplex stream, limits) and depend on alkcall types only, never on
|
limits) and depends on alkcall types only, never on alkhttp/alkssh,
|
||||||
alkhttp/alkgit-ssh, and carry no http/channel-specific types below the
|
and carries no http/channel-specific types below the stream. Doors
|
||||||
stream. The http and ssh crates are replaceable adapters; a downstream
|
(alkhttp `git` feature, alkssh) are family infrastructure; a
|
||||||
app embeds core + transport and brings its own front doors. v1 is the
|
downstream app embeds alkgit and brings its own doors. v1 is the
|
||||||
reduction to storage + ACL + protocol adapters.
|
reduction to wire layer + backend traits + one gix implementation.
|
||||||
|
|
||||||
## Verification Commands
|
## Verification Commands
|
||||||
|
|
||||||
Run these before committing. All must pass.
|
Run these before committing. All must pass.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo test # full suite (workspace)
|
cargo test # full suite
|
||||||
cargo clippy --all-targets -- -D warnings
|
cargo clippy --all-targets -- -D warnings
|
||||||
cargo fmt --check
|
cargo fmt --check
|
||||||
cargo doc --no-deps # if docs changed
|
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
|
## Lifecycle Status
|
||||||
|
|
||||||
The project is in **SDD phase 0** (exploration) — see
|
The project is in **SDD phase 1** (architecture committed, implementation
|
||||||
`docs/sdd_process.md` and `docs/research/README.md`. There is no
|
not yet begun). Phase 0 research is in `docs/research/`; the committed
|
||||||
architecture yet (`docs/architecture/` is empty; ADR numbering starts at
|
architecture is `docs/architecture/` (ADR-010 is the structural
|
||||||
001 when phase 1 begins). POC code never merges to main — two modes:
|
decision: pure protocol crate, `alk/git` ALPN, backend traits, no
|
||||||
branch mode (POC builds on repo code; git branch, findings merged into
|
binary/doors). POC code never merges to main — two modes: branch mode
|
||||||
research docs, branch dropped) or standalone mode (POC independent of
|
(POC builds on repo code; git branch, findings merged into research
|
||||||
repo code; scratch project at `/workspace/<poc-name>`). POCs have relaxed
|
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,
|
constraints (comments/unwrap acceptable) — they are exploration tools,
|
||||||
not production code. Until phase 1
|
not production code. Open architecture questions live in
|
||||||
produces ADRs, "the architecture says" has no referent — cite
|
`docs/architecture/open-questions.md` (OQ-04 receive-pack, OQ-06
|
||||||
`docs/research/` docs instead.
|
registry backing, OQ-08 identity model are the active ones).
|
||||||
|
|
||||||
## Architecture Context
|
## Architecture Context
|
||||||
|
|
||||||
|
|||||||
+40
-28
@@ -1,46 +1,58 @@
|
|||||||
[workspace]
|
[package]
|
||||||
resolver = "2"
|
name = "alkgit"
|
||||||
members = [
|
|
||||||
"crates/alkgit-core",
|
|
||||||
"crates/alkgit-transport",
|
|
||||||
"crates/alkgit-http",
|
|
||||||
"crates/alkgit-ssh",
|
|
||||||
"crates/alkgitd",
|
|
||||||
]
|
|
||||||
|
|
||||||
[workspace.package]
|
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.88"
|
rust-version = "1.88"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://git.alk.dev/alkdev/alkgit"
|
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]
|
[lib]
|
||||||
alkgit-core = { path = "crates/alkgit-core", version = "0.0.1" }
|
name = "alkgit"
|
||||||
alkgit-transport = { path = "crates/alkgit-transport", version = "0.0.1" }
|
path = "src/lib.rs"
|
||||||
alkgit-http = { path = "crates/alkgit-http", version = "0.0.1" }
|
|
||||||
alkgit-ssh = { path = "crates/alkgit-ssh", version = "0.0.1" }
|
[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"
|
alkcall = "0.8"
|
||||||
alkhttp = "0.5"
|
gix = { version = "0.87", optional = true, default-features = false, features = ["sha1"] }
|
||||||
alktls = "0.1"
|
gix-odb = { version = "0.84", optional = true, default-features = false, features = ["sha1"] }
|
||||||
alkvault = "0.1"
|
gix-pack = { version = "0.74", optional = true, features = ["sha1"] }
|
||||||
gix = { version = "0.87", default-features = false, 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-packetline = "0.22"
|
||||||
gix-protocol = "0.65"
|
gix-protocol = "0.65"
|
||||||
gix-transport = "0.59"
|
gix-transport = "0.59"
|
||||||
gix-pack = { version = "0.74", features = ["sha1"] }
|
tokio = { version = "1", default-features = false, features = ["rt", "sync", "io-util", "macros", "time"] }
|
||||||
gix-odb = { version = "0.84", default-features = false, features = ["sha1"] }
|
tokio-util = { version = "0.7", features = ["compat"] }
|
||||||
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"] }
|
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
serde_json = "1"
|
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tokio = { version = "1", features = ["full", "test-util", "macros"] }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "thin"
|
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
|
# alkgit Architecture
|
||||||
|
|
||||||
Phase 1 (SDD) output for alkgit — the self-hosted, single-binary git server
|
Phase 1 (SDD) output for alkgit — the git payload service of the alk
|
||||||
built on the alk stack (alkcall, alkhttp, alktls, alkvault) and gitoxide.
|
family: a pure protocol crate on alkcall channels (the `alk/git` ALPN),
|
||||||
Phase 0 research lives in [docs/research/](../research/README.md); every
|
following the alktty/alktunnels template (ADR-010). Phase 0 research lives
|
||||||
design claim here traces to a POC finding or research doc, or is flagged as
|
in [docs/research/](../research/README.md); every design claim here traces
|
||||||
an open question.
|
to a POC finding or research doc, or is flagged as an open question.
|
||||||
|
|
||||||
## Current State
|
## Current State
|
||||||
|
|
||||||
Phase 1 is starting. All architecture documents below are `draft`
|
Phase 1, architecture committed to the pure-protocol-crate shape (ADR-010;
|
||||||
(ADR-006 additionally carries a Proposed ADR status pending OQ-01).
|
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;
|
POC-1/2/3 validated the git protocol half end-to-end against real git
|
||||||
the remaining design work is shape work (adapter composability, metadata/
|
2.43; the remaining design work is the receive-pack state machine (OQ-04)
|
||||||
registry backing, admin surface, receive-pack).
|
and backend/identity decisions (OQ-06, OQ-08).
|
||||||
|
|
||||||
## Architecture Documents
|
## Architecture Documents
|
||||||
|
|
||||||
| Doc | Area | Status |
|
| Doc | Area | Status |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| [overview.md](overview.md) | Cross-cutting: crate map, dependency rules, security invariants | draft |
|
| [overview.md](overview.md) | Cross-cutting: crate shape, halves, security invariants | draft |
|
||||||
| [storage.md](storage.md) | `alkgit-core`: registry, refs, odb, pack generate/ingest, ACL types | draft |
|
| [transport.md](transport.md) | Wire layer: substrates, V2 state machines, upload/receive-pack | draft |
|
||||||
| [transport.md](transport.md) | `alkgit-transport`: pkt-line sessions, V2 state machine, upload/receive-pack | draft |
|
| [backend.md](backend.md) | Backend traits + feature-gated gix implementation | draft |
|
||||||
| [http.md](http.md) | `alkgit-http`: smart-http adapter over alkhttp | draft |
|
| [doors.md](doors.md) | Door mappings: alkhttp `git` feature, alkssh requirement, native path | draft |
|
||||||
| [ssh.md](ssh.md) | `alkgit-ssh`: git-command dispatch (wire SSH terminated by russh in alkgitd) | draft |
|
|
||||||
| [alkgitd.md](alkgitd.md) | `alkgitd`: binary assembly, config, TLS/ACME, serving loops | draft |
|
|
||||||
| [open-questions.md](open-questions.md) | Centralized OQ tracker | — |
|
| [open-questions.md](open-questions.md) | Centralized OQ tracker | — |
|
||||||
|
|
||||||
## ADRs
|
## ADRs
|
||||||
|
|
||||||
| ADR | Decision | Status |
|
| ADR | Decision | Status |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| [001](decisions/001-crate-decomposition.md) | Workspace crate decomposition (5 crates) | Accepted |
|
| [001](decisions/001-crate-decomposition.md) | Workspace crate decomposition (5 crates) | Superseded (ADR-010) |
|
||||||
| [002](decisions/002-front-door-blind-core.md) | Front-door-blind core: session boundary (identity, repo, stream, limits) | Accepted |
|
| [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 |
|
| [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 |
|
| [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, request reader) | 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) | Proposed |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [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 |
|
||||||
Note: ADR-001's crate table and ssh.md record the v1 ssh-door decision
|
|
||||||
(russh terminates wire SSH in alkgitd; OQ-03 covers embedder variants).
|
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
All unresolved questions are tracked in [open-questions.md](open-questions.md)
|
All unresolved questions are tracked in [open-questions.md](open-questions.md)
|
||||||
with stable OQ-IDs, priorities, and cross-references. Highest-priority opens:
|
with stable OQ-IDs, priorities, and cross-references. Highest-priority opens:
|
||||||
OQ-09 (slim-crate model: doors as family infrastructure — may supersede
|
OQ-04 (receive-pack validation), OQ-06 (registry backing), OQ-08 (registry
|
||||||
ADR-006/001 shape), OQ-04 (receive-pack validation), OQ-06 (metadata store
|
identity space + vault placement).
|
||||||
backing), OQ-08 (identity sources per front door).
|
|
||||||
|
|
||||||
## Document Lifecycle
|
## 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)
|
# ADR-001: Workspace crate decomposition (5 crates)
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
Accepted
|
Superseded by ADR-010 (pure protocol crate — single `alkgit`, no workspace,
|
||||||
|
no front-door crates, no binary)
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Accepted
|
|||||||
## Context
|
## Context
|
||||||
|
|
||||||
The load-bearing composability rule ("ALPN as a service",
|
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
|
know which front door is talking. POC-1 proved the exact shape survives the
|
||||||
wire: `Connection::accept_bi() → BiStream → tokio::io::split →
|
wire: `Connection::accept_bi() → BiStream → tokio::io::split →
|
||||||
tokio-util compat → gix-packetline` ran a full V2 fetch against real git.
|
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
|
command per invocation, matching smart-http's stateless framing. The
|
||||||
same core state machines run under both entry points.
|
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
|
pack generation (want/have set in → streaming pack out), and pack
|
||||||
ingestion + ref CAS (receive-pack). Storage never sees pkt-lines.
|
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)
|
- alkcall ADR-005 (`BiStream` type), ADR-009 (BiStream as handler leaf)
|
||||||
- ADR-005 (substrate types detail), ADR-007/008 (what adapters do before
|
- ADR-005 (substrate types detail), ADR-007/008 (what adapters do before
|
||||||
calling transport)
|
calling transport)
|
||||||
- overview.md §"Interfaces"
|
- overview.md §"Crate map"
|
||||||
@@ -41,7 +41,7 @@ Decision drivers:
|
|||||||
(shallow, filter, packfile-uris, object-info, server-option are
|
(shallow, filter, packfile-uris, object-info, server-option are
|
||||||
*declined by omission*; POC-1 confirmed real git accepts this).
|
*declined by omission*; POC-1 confirmed real git accepts this).
|
||||||
On the push side, `git-upload-archive` is not served at all — ssh
|
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).
|
never-execute rule).
|
||||||
- HTTP requests protocol V2 only; ssh requests protocol V2 only (see
|
- HTTP requests protocol V2 only; ssh requests protocol V2 only (see
|
||||||
below).
|
below).
|
||||||
@@ -79,4 +79,4 @@ happens then.
|
|||||||
- `docs/research/git-protocol.md` §"Protocol surface inventory", §"Negotiation policy"
|
- `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)
|
- `docs/research/poc-1-findings.md` (advertisement-once, capability declination), POC-3 (http V2 framing)
|
||||||
- ADR-005 (substrate types), ADR-007 (ACL before advertisement)
|
- 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
|
## 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;
|
- Fresh clones of loose-ish repos ship uncompressed bases (fine for v1;
|
||||||
clients re-pack at rest); repos kept packed get pack-copy efficiency.
|
clients re-pack at rest); repos kept packed get pack-copy efficiency.
|
||||||
- Memory stays O(counts); streaming under back pressure is proven on the
|
- Memory stays O(counts); streaming under back pressure is proven on the
|
||||||
http path (POC-3: flat RSS under a 650 KB/s reader).
|
http path (POC-3: flat RSS under a 650 KB/s reader).
|
||||||
- Determinism: `objects_unthreaded` gives deterministic order; threaded
|
- Determinism: `objects_unthreaded` gives deterministic order; threaded
|
||||||
count + `InOrderIter` is the scale path later.
|
count + `InOrderIter` is the scale path later.
|
||||||
- `alkgit-core` exposes this as a type taking (odb handle, wants, haves) →
|
- The generation seam is negotiation-agnostic: boundary sets in → pack
|
||||||
streaming pack; negotiation-agnostic (POC-2 follow-up 1).
|
out (POC-2 follow-up 1).
|
||||||
|
|
||||||
## References
|
## References
|
||||||
- `docs/research/poc2-findings.md` (the whole basis), `docs/research/poc3-findings.md` (streaming proof)
|
- `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/gitoxide.md` §"Storage" (generation-pipeline notes)
|
||||||
- `docs/research/git-protocol.md` §"Server-side pack generation"
|
- `docs/research/git-protocol.md` §"Server-side pack generation"
|
||||||
- ADR-005 (how the sink reaches the wire), ADR-009 (memory/time budgets)
|
- 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
|
## Decision
|
||||||
|
|
||||||
`alkgit-transport` owns the substrate layer; adapters and handlers see
|
`alkgit` owns the substrate layer; doors and handlers see
|
||||||
friendly types:
|
friendly types:
|
||||||
|
|
||||||
1. **`Session` (duplex)** — wraps (stream, limits) into the pkt-line
|
1. **`Session` (duplex)** — wraps (stream, limits) into the pkt-line
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
# ADR-006: HTTP adapter composition — router factory in alkgit-http
|
# ADR-006: HTTP adapter composition — router factory in alkgit-http
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
Proposed (recommendation recorded; final call pending user review — OQ-01,
|
Superseded by ADR-010 (pure protocol crate — the http mounting moves to an
|
||||||
and now also OQ-09, whose slim-crate model may supersede this ADR's
|
alkhttp `git` feature; the stateless substrate stays in `alkgit`)
|
||||||
shape entirely)
|
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -45,7 +44,12 @@ Evaluation of Option B:
|
|||||||
published — that is an alkhttp-side decision that does not constrain
|
published — that is an alkhttp-side decision that does not constrain
|
||||||
alkgit's shape now.
|
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
|
**Option A.** `alkgit-http` owns the smart-http adapter and exposes it as
|
||||||
an axum router factory; alkhttp stays git-agnostic and unchanged.
|
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
|
downstream apps are second users of the same seam — this is what makes
|
||||||
the adapter genuinely composable rather than binary-only.
|
the adapter genuinely composable rather than binary-only.
|
||||||
|
|
||||||
If a concrete downstream later demonstrates that the two-dep + merge
|
(Historical) A concrete downstream later confirmed the ergonomics
|
||||||
ergonomics is a real friction point, the Option-B-style sugar can be added
|
friction; the Option-B sugar — an alkhttp `git` feature — was chosen as
|
||||||
*in alkhttp* without any change here (alkhttp would gain an optional
|
the outcome (OQ-01/OQ-09), and ADR-010 adopted it as the structural
|
||||||
alkgit-http feature re-exporting the factory). Deciding that now is not
|
decision, superseding this ADR.
|
||||||
necessary and would couple the release cadences; recording the escape
|
|
||||||
hatch here is enough.
|
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
@@ -81,13 +83,13 @@ hatch here is enough.
|
|||||||
- The identity-extractor callback is the one place downstream auth
|
- The identity-extractor callback is the one place downstream auth
|
||||||
semantics enter; ACL itself stays in core (ADR-007) — adapters never
|
semantics enter; ACL itself stays in core (ADR-007) — adapters never
|
||||||
hand-roll authorization.
|
hand-roll authorization.
|
||||||
- This ADR stays Proposed until OQ-01 is discussed (user flagged the
|
- (Historical) This ADR was Proposed pending OQ-01; OQ-01/OQ-09 resolved
|
||||||
alkhttp-feature alternative; the escape hatch above is the recorded
|
in favor of the alkhttp-feature alternative recorded below the
|
||||||
reconciliation path).
|
escape-hatch note, and ADR-010 superseded this ADR outright.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
- `poc3-findings.md` §"alkhttp fit" (with_extra_routes surface), follow-up 1
|
- `poc3-findings.md` §"alkhttp fit" (with_extra_routes surface), follow-up 1
|
||||||
- `docs/research/vision.md` §"ALPN as a service", §"Sub-crate shape"
|
- `docs/research/vision.md` §"ALPN as a service", §"Sub-crate shape"
|
||||||
- alkcall ADR-027 (precedent and its limits)
|
- alkcall ADR-027 (precedent and its limits)
|
||||||
- ADR-001 (crate decomposition), ADR-002 (session boundary), ADR-007/008/009
|
- 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
|
the resolve-then-authorize step sits structurally ahead of the first
|
||||||
emitted line. POC-3 noted the http extra-routes are registered permissive
|
emitted line. POC-3 noted the http extra-routes are registered permissive
|
||||||
by default (the gateway bearer layer does not cover them) — making the
|
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.
|
one.
|
||||||
|
|
||||||
## Decision
|
## 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
|
- The check is cheap (registry lookup + ACL check) and runs before any
|
||||||
expensive protocol work.
|
expensive protocol work.
|
||||||
- http adapters must wire ACL explicitly for their routes (POC-3 showed
|
- 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
|
## References
|
||||||
- `docs/research/vision.md` §"Guiding principles" 1–2, §"Immediate threat-model notes"
|
- `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)
|
- `docs/research/poc-1-findings.md` follow-up 4; `docs/research/poc3-findings.md` §"does NOT settle" (auth)
|
||||||
- alkcall ADR-017 (privilege model)
|
- 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.
|
server-side to configured storage roots.
|
||||||
|
|
||||||
- The registry is alkgit's authoritative (repo id → storage root +
|
- 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
|
- Resolution failure and authorization failure are indistinguishable to
|
||||||
the caller (ADR-007 step 2's no-existence-oracle rule).
|
the caller (ADR-007 step 2's no-existence-oracle rule).
|
||||||
- Wire names are never joined, normalized, or canonicalized into paths.
|
- 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/git-protocol.md` §"Security-relevant protocol notes"
|
||||||
- `docs/research/vision.md` §"Immediate threat-model notes"
|
- `docs/research/vision.md` §"Immediate threat-model notes"
|
||||||
- ADR-007 (the resolve-then-authorize order)
|
- 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
|
## Decision
|
||||||
|
|
||||||
Every session carries a `Limits` value, constructed by the adapter from
|
Every session carries a `Limits` value, constructed by the door/adapter
|
||||||
server config and handed to transport as part of the session tuple
|
from assembler config and handed to the wire layer as part of the session
|
||||||
(ADR-002). Defaults are per-crate constants; overrides are server config
|
tuple (ADR-002). On the native path the producer adapter is in-crate
|
||||||
in `alkgitd`.
|
(`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 |
|
| Budget | Applies to | Default direction |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -61,4 +63,4 @@ gets the hard cap.
|
|||||||
- `docs/research/vision.md` §"Guiding principles" 7
|
- `docs/research/vision.md` §"Guiding principles" 7
|
||||||
- `docs/research/poc2-findings.md` follow-ups 2–3; `docs/research/poc3-findings.md` follow-up 3
|
- `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)
|
- 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 | 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-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-06 | deferred(scope) | concrete metadata-scale requirements (feeders: OQ-07, OQ-08 outputs) |
|
||||||
| OQ-05 | deferred(scope) | ecosystem need for sha256 |
|
| OQ-05 | deferred(scope) | ecosystem need for sha256 |
|
||||||
|
|
||||||
## Theme: composition / crate shapes
|
## 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)
|
### OQ-01: HTTP adapter home and composability (alkhttp `git` feature vs alkgit-owned factory)
|
||||||
|
|
||||||
- **Origin**: [overview.md], [http.md], user session question
|
- **Origin**: user session question (OQ-01, resolved 2026-09-21)
|
||||||
- **Status**: partially resolved — ADR-006 written **Proposed** with
|
- **Status**: **resolved** (subsumed by OQ-09/ADR-010) — the smart-http
|
||||||
Option A (alkgit-owned router factory) as the recommendation and the
|
stateless substrate stays in `alkgit` (IO-abstract); the http mounting
|
||||||
alkhttp-side sugar as a recorded escape hatch. Needs user review.
|
(routes, content types, `with_extra_routes` wiring) becomes an alkhttp
|
||||||
- **Door type**: two-way (adapter shape can change before anything is
|
`git` feature published after alkgit's first publish. The ADR-006
|
||||||
published)
|
router-factory shape is superseded; the alkhttp-side feature is the
|
||||||
- **Priority**: high
|
outcome.
|
||||||
- **Impacts**: blocks finalizing http.md and ADR-006; small effect on
|
- **Cross-references**: ADR-006 (superseded), ADR-010, doors.md
|
||||||
downstream ergonomics.
|
|
||||||
- **Resolution path**: user reviews ADR-006; accept → ADR becomes
|
|
||||||
Accepted; or choose Option B (alkhttp feature) → ADR reworked.
|
|
||||||
- **Cross-references**: ADR-001, ADR-006, http.md
|
|
||||||
|
|
||||||
### OQ-03: Downstream embedding surface (what "embeds core + transport" means concretely)
|
### OQ-03: Downstream embedding surface (what "embeds alkgit" means concretely)
|
||||||
|
|
||||||
- **Origin**: [overview.md], [transport.md], [ssh.md], vision §ALPN as a
|
- **Origin**: [overview.md], [transport.md], vision §"ALPN as a service"
|
||||||
service
|
- **Status**: **partially resolved** — the shape is settled (ADR-010):
|
||||||
- **Status**: partially resolved — v1 ssh-door decision made: alkgitd
|
embedding = one crate + backend traits (own storage via
|
||||||
terminates SSH via russh for stock git clients (ssh.md); what remains
|
`default-features = false`, or the gix feature) + optional door
|
||||||
deferred is whether a *pure-alkcall-channels* ssh variant and a
|
features. What remains deferred is the publish-time API freeze itself:
|
||||||
russh-flavored adapter are exported for embedders.
|
which type/feature names are pinned at first crates.io publish.
|
||||||
- **Door type**: two-way
|
- **Door type**: one-way (API freeze is registry-visible to dependents)
|
||||||
- **Priority**: medium
|
- **Priority**: medium
|
||||||
- **Impacts**: blocks nothing in v1 (alkgitd is the only consumer); shapes
|
- **Impacts**: blocks the first publish only, not implementation.
|
||||||
the crates' public API freeze before publish.
|
- **Blocked on**: first-publish timing (a release decision, not an
|
||||||
- **Blocked on**: a concrete downstream embedder use case (e.g. a real
|
architecture question). The API surface inventory lives in
|
||||||
gitea-like app or test harness wanting to serve git) — until one
|
[backend.md](backend.md) §public API and [transport.md](transport.md)
|
||||||
exists, the embedding seam is designed by example (alkgitd) only.
|
§public API.
|
||||||
Tracker task: `tasks/architecture/oq-03-embedder.md`.
|
- **Cross-references**: ADR-010, ADR-002, backend.md, transport.md
|
||||||
- **Cross-references**: ADR-001, ADR-002, transport.md §public API,
|
|
||||||
ssh.md §russh
|
|
||||||
|
|
||||||
## Theme: transport / protocol
|
## 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
|
ingestion composition is not obvious from POC-2's findings. Tracker
|
||||||
task: `tasks/architecture/oq-04-receive-pack.md`.
|
task: `tasks/architecture/oq-04-receive-pack.md`.
|
||||||
- **Cross-references**: ADR-003, ADR-004, ADR-009, transport.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
|
### 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
|
## 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]
|
- **Origin**: [overview.md], [doors.md], [backend.md]; originally "identity
|
||||||
- **Status**: open
|
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
|
- **Priority**: high
|
||||||
- **Impacts**: blocks http.md and ssh.md auth sections finalizing; blocks
|
- **Impacts**: blocks backend.md's registry trait field list finalizing;
|
||||||
the identity-extractor callback design in ADR-006's seam; blocks
|
blocks the admin-ops shapes (OQ-07).
|
||||||
alkgitd config schema and the OQ-07 admin op shapes.
|
- **Resolution path**: one focused session on the registry identity model
|
||||||
- **Resolution path**: decide per door — http (bearer token? basic? via
|
+ alkvault placement.
|
||||||
alkvault-stored credentials), ssh (russh-terminated public-key auth →
|
- **Cross-references**: ADR-007, ADR-010, OQ-06, OQ-07, backend.md
|
||||||
alkgit identity mapping per ssh.md), and which identities exist in
|
§GitRegistry, doors.md
|
||||||
v1's registry — including whether identity records live in the same
|
|
||||||
metadata store as repo records (OQ-06's field list may grow for this).
|
|
||||||
- **Cross-references**: ADR-006, ADR-007, OQ-06 (metadata backing where
|
|
||||||
identity records live),
|
|
||||||
http.md §auth, ssh.md §identity, alkgitd.md, OQ-07
|
|
||||||
|
|
||||||
## Theme: storage / metadata
|
## 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)
|
- **Status**: deferred(scope)
|
||||||
- **Door type**: two-way (backing choice is swappable behind the core
|
- **Door type**: two-way (backing choice is swappable behind the
|
||||||
trait)
|
`GitRegistry` trait)
|
||||||
- **Priority**: high for v1 config story, but choice deferrable because
|
- **Priority**: high for the gix feature's default story, but choice
|
||||||
the trait boundary is what matters
|
deferrable because the trait boundary is what matters
|
||||||
- **Impacts**: blocks storage.md's registry section finalizing and
|
- **Impacts**: blocks backend.md's gix-feature registry impl finalizing;
|
||||||
alkgitd's config schema; does NOT block core/transport work (they code
|
does NOT block the wire layer (it codes against the trait).
|
||||||
against the trait).
|
|
||||||
- **Blocked on**: concrete metadata-scale requirements (how many repos,
|
- **Blocked on**: concrete metadata-scale requirements (how many repos,
|
||||||
what metadata fields beyond id/root/visibility/ACL scope, whether
|
what metadata fields beyond id/root/visibility/ACL scope, whether
|
||||||
alkcall-hub integration lands in v1). A config-file or embedded-store
|
identity records join — OQ-08's output, whether hub integration lands
|
||||||
decision without those inputs would be a guess. Tracker task:
|
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`.
|
`tasks/architecture/oq-06-metadata-backing.md`.
|
||||||
- **Cross-references**: ADR-008, storage.md §registry, alkgitd.md §config,
|
- **Cross-references**: ADR-008, ADR-010, backend.md §"The trait family" (GitRegistry), OQ-08
|
||||||
OQ-08 (whose identity-records question may extend this store's field
|
(whose identity-records question may extend this store's field list)
|
||||||
list)
|
|
||||||
|
|
||||||
### OQ-07: Admin API operation set (v1 scope)
|
### OQ-07: Admin API operation set (v1 scope)
|
||||||
|
|
||||||
- **Origin**: [overview.md], [alkgitd.md], alk-stack.md §"The gitea lesson"
|
- **Origin**: [overview.md], alk-stack.md §"The gitea lesson"
|
||||||
- **Status**: open
|
- **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
|
- **Priority**: medium
|
||||||
- **Impacts**: blocks the admin-ops inventory (repo create/delete,
|
- **Impacts**: blocks the gix feature's registry impl scope; OQ-08's
|
||||||
visibility set, ACL grant/revoke, user/identity management — if v1 has
|
identity model determines the op shapes.
|
||||||
users at all, which is OQ-08 territory). Deliberately small: everything
|
- **Resolution path**: decide alongside OQ-08 (one session can settle
|
||||||
is `Visibility::Internal` alkcall ops over the admin surface.
|
both).
|
||||||
- **Resolution path**: one dedicated session once OQ-08's identity model
|
- **Cross-references**: ADR-007, ADR-010, OQ-08, backend.md §"The trait family" (GitRegistry)
|
||||||
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
|
|
||||||
@@ -7,113 +7,89 @@ last_updated: 2026-09-21
|
|||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
alkgit is a self-hosted git server: repository storage, the git smart
|
alkgit is the git payload service of the alk family: a pure protocol crate
|
||||||
protocol served over http and ssh interfaces, and the `alkgitd` binary that
|
(per ADR-010, following the alktty/alktunnels template) implementing the
|
||||||
assembles it all. It exists to provide a small, security-first git platform
|
git smart protocol over alkcall channels — the `alk/git` ALPN. It provides
|
||||||
whose exposure model inverts the mainstream pattern (visible-surface =
|
repository storage as backend traits (with a feature-gated gitoxide
|
||||||
authorized-surface, authenticated-by-default, no plaintext secrets, no
|
implementation), the git smart protocol as producer/consumer halves, and
|
||||||
plugin execution). See `docs/research/vision.md` for the full WHY.
|
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
|
## The one-line architecture
|
||||||
|
|
||||||
**Storage + ACL + protocol adapters.** `alkgit-core` owns repository storage
|
**One protocol crate: producer + consumer + backend traits, gix behind a
|
||||||
and access-rule types; `alkgit-transport` owns the git protocol state
|
feature.** Doors (alkhttp, alkssh, alknet) expose it; assembly belongs to
|
||||||
machines; `alkgit-http` and `alkgit-ssh` are thin front doors that
|
downstream consumers.
|
||||||
authenticate, resolve the repo, and hand a session to the transport;
|
|
||||||
`alkgitd` assembles everything with config, TLS/ACME, and the vault.
|
|
||||||
|
|
||||||
## Crate map
|
## Crate map
|
||||||
|
|
||||||
| Crate | Owns | Depends on | Never depends on |
|
Single crate `alkgit`:
|
||||||
|---|---|---|---|
|
|
||||||
| `alkgit-core` | repo registry/storage roots, odb wrappers, ref store, pack generate/ingest, fsck, ACL input types | gix crates, alkcall (ACL types only) | any front-door crate |
|
|
||||||
| `alkgit-transport` | pkt-line sessions, V2 advertisement/state machine, ls-refs, fetch, receive-pack | `alkgit-core`, alkcall, gix-packetline | alkhttp, alkgit-ssh |
|
|
||||||
| `alkgit-http` | smart-http endpoints over alkhttp | transport, core, alkhttp | alkgit-ssh, alkgitd |
|
|
||||||
| `alkgit-ssh` | git-command dispatch for exec requests over alkcall channels | transport, core, alkcall | alkhttp, alkgitd |
|
|
||||||
| `alkgitd` | binary: config, assembly, TLS/ACME, listeners, vault wiring | everything | — |
|
|
||||||
|
|
||||||
Dependency rules (ADR-001, ADR-002):
|
| 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
|
Feature model: `default-features = false` gives the wire/protocol layer
|
||||||
(identity, repo id, duplex stream, limits) and depend on alkcall types
|
without gix (wasm-clean as a side effect, not a goal); the `sha256`
|
||||||
only. No http types, no ssh channel types below the stream.
|
passthrough and (eventually, in alkhttp) the `git` door feature ride the
|
||||||
2. `alkgit-http` and `alkgit-ssh` never depend on each other.
|
same pattern. Doors live in the door crates — see [doors.md](doors.md).
|
||||||
3. `alkgitd` is the only crate allowed to know the whole graph.
|
|
||||||
4. A downstream application (gitea-like) embeds core + transport and brings
|
|
||||||
its own front doors; the admin API is a set of alkcall ops it may use or
|
|
||||||
replace.
|
|
||||||
|
|
||||||
## Security invariants (spec-level, all components honor)
|
## Security invariants (spec-level, carried from vision/principles)
|
||||||
|
|
||||||
These are the load-bearing rules from `docs/research/vision.md` and
|
1. **Authenticated by default** — anonymous fetch only on explicitly-
|
||||||
`docs/research/alk-stack.md`; each component doc references them where
|
public repos; push always authenticated.
|
||||||
concrete:
|
2. **Visible-surface = authorized-surface** — alkcall ACL end-to-end;
|
||||||
|
internal ops never wire-callable.
|
||||||
1. **Authenticated by default** — anonymous fetch exists only on
|
3. **ACL before advertisement** — nothing is emitted before the check
|
||||||
explicitly-public repos; push is always authenticated.
|
(ADR-007); the channels open-op carrying the repo id is the natural
|
||||||
2. **Visible-surface = authorized-surface** — via alkcall ACL; ops with
|
enforcement point on the native path.
|
||||||
`Visibility::Internal` are never wire-callable; admin ops ride an admin
|
4. **Registry-resolved repo identity** — wire names are ids, never paths
|
||||||
surface, never the git traffic surface.
|
(ADR-008).
|
||||||
3. **ACL before advertisement** — access is checked before any ref line or
|
5. **No secret material on the wire or at rest outside alkvault** —
|
||||||
capability line is emitted (ref names leak repo existence) (ADR-007).
|
metadata holds vault references; no env-var credential reads.
|
||||||
4. **Registry-resolved repo identity** — wire-supplied repo names are IDs
|
6. **No shelling out to `git`** — pure Rust on gix primitives.
|
||||||
resolved to server-configured storage roots; never used as paths (ADR-008).
|
7. **Bounded resources** — every session carries `Limits` (ADR-009).
|
||||||
5. **No secret material on the wire or at rest outside alkvault** — metadata
|
8. **Honest capability advertisement** — advertise exactly what we serve
|
||||||
holds vault references only; outbound credentials flow through alkcall
|
(ADR-003).
|
||||||
`Capabilities` (alkcall ADR-010), and no handler reads credentials from
|
|
||||||
env or files (the no-env-vars invariant).
|
|
||||||
6. **No shelling out to `git`** — serving path is pure Rust on gix
|
|
||||||
primitives (GPL hygiene + no process-injection surface).
|
|
||||||
7. **Bounded resources** — every session carries wall-clock, size, and round
|
|
||||||
limits (ADR-009); unbounded loops/buffers are bugs.
|
|
||||||
8. **Honest capability advertisement** — the protocol advertises exactly
|
|
||||||
what we serve (ADR-003).
|
|
||||||
|
|
||||||
## Interfaces (the boundary shape)
|
|
||||||
|
|
||||||
The core boundary, from POC-1 (`docs/research/poc-1-findings.md`, follow-up 4) and the
|
|
||||||
"ALPN as a service" rule in `docs/research/vision.md`:
|
|
||||||
|
|
||||||
- **Transport input**: one session = (peer identity from alkcall
|
|
||||||
`AuthContext`, repo id resolved against the registry, a duplex byte
|
|
||||||
stream, session limits). For the stateless http door this becomes
|
|
||||||
(identity, repo, request-reader, response-writer, limits) — the same state
|
|
||||||
machines, a different substrate (ADR-005).
|
|
||||||
- **Storage input**: the transport asks core for (a) ref advertisement data,
|
|
||||||
(b) pack generation for a want/have set, (c) pack ingestion + ref
|
|
||||||
transactions for receive-pack. Storage never sees pkt-lines.
|
|
||||||
|
|
||||||
## What is already validated (POC-backed)
|
## What is already validated (POC-backed)
|
||||||
|
|
||||||
- pkt-line over alkcall `BiStream` end-to-end (POC-1).
|
- pkt-line over alkcall `BiStream` end-to-end (POC-1 → producer half).
|
||||||
- Pack generation pipeline streaming with O(counts) memory (POC-2).
|
- Pack generation pipeline streaming with O(counts) memory (POC-2 → gix
|
||||||
- Smart-http streaming both ways through alkhttp custom routes (POC-3).
|
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
|
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
|
## Design Decisions
|
||||||
|
|
||||||
| ADR | Decision | Summary |
|
| ADR | Decision | Summary |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| [001](decisions/001-crate-decomposition.md) | Crate decomposition | 5 crates: core, transport, http, ssh, alkgitd |
|
| [001](decisions/001-crate-decomposition.md) | Crate decomposition | **superseded by ADR-010** |
|
||||||
| [002](decisions/002-front-door-blind-core.md) | Front-door-blind core | Session boundary = (identity, repo, stream, limits) |
|
| [002](decisions/002-front-door-blind-core.md) | Session boundary | (identity, repo, stream, limits) — unchanged, load-bearing |
|
||||||
| [003](decisions/003-protocol-v2-first.md) | V2-first protocol | V2-only fetch on both doors; honest advertisement; multi-round negotiation sequenced (OQ-02); push surface OQ-04 |
|
| [003](decisions/003-protocol-v2-first.md) | V2-first protocol | V2-only both doors; honest advertisement |
|
||||||
| [004](decisions/004-pack-pipeline.md) | Pack pipeline | gitoxide `data::output` generation, streaming-input ingestion |
|
| [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 session APIs over one state machine |
|
| [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 | Router factory in alkgit-http (**proposed**, OQ-01) |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [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
|
## 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
|
- **OQ-04**: receive-pack (push) validation gap (high — the
|
||||||
always-authenticated half of the wire surface).
|
always-authenticated half of the wire surface).
|
||||||
- **OQ-08**: identity sources per front door (how http and ssh authenticate
|
- **OQ-06**: registry backing store for the gix feature (deferred on
|
||||||
peers in v1).
|
scale requirements).
|
||||||
- **OQ-06**: metadata store backing for the registry (config-file vs
|
- **OQ-08**: registry identity space + vault placement (narrowed by
|
||||||
embedded store vs alkcall-hosted).
|
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
|
last_updated: 2026-09-21
|
||||||
---
|
---
|
||||||
|
|
||||||
# alkgit-transport: Git Smart Protocol
|
# alkgit: Git Smart Protocol (wire layer)
|
||||||
|
|
||||||
## What it is
|
## What it is
|
||||||
|
|
||||||
The git protocol layer: pkt-line session substrates, protocol V2 state
|
The wire half of the `alkgit` protocol crate: pkt-line session substrates,
|
||||||
machines (advertisement, ls-refs, fetch, receive-pack), and the glue that
|
protocol V2 state machines (advertisement, ls-refs, fetch, receive-pack),
|
||||||
keeps adapters from touching `futures_io` or raw packetline APIs. Depends
|
and the substrate types that keep doors and handlers from touching
|
||||||
on `alkgit-core` and alkcall types only (ADR-001/002).
|
`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)
|
## Substrate layer (ADR-005)
|
||||||
|
|
||||||
Two session entry points over one state-machine core. ADR-005 owns the
|
Two session entry points over one state-machine core. ADR-005 owns the
|
||||||
full decision (what each substrate owns and why); the surface is:
|
full decision (what each substrate owns and why); the surface is:
|
||||||
|
|
||||||
- **Duplex session** (ssh, git://, embedded stream doors) — input:
|
- **Duplex session** (the `alk/git` ALPN producer, channels-opened git
|
||||||
(peer identity, resolved repo id, duplex stream, `Limits`). Encapsulates
|
sessions, embedder stream doors) — input: (peer identity, resolved repo
|
||||||
the split/compat/packetline bridge, the request reader (delim-aware
|
id, duplex stream, `Limits`). Encapsulates the split/compat/packetline
|
||||||
parsing, `reset()` discipline, break-on-error), and the sideband writer.
|
bridge, the request reader (delim-aware parsing, `reset()` discipline,
|
||||||
- **Stateless session** (smart-http) — input: (peer identity, resolved
|
break-on-error), and the sideband writer.
|
||||||
repo id, request-reader, response-writer, `Limits`) per http POST; adds
|
- **Stateless session** (smart-http doors, e.g. alkhttp's future `git`
|
||||||
the http-framing rules (capability-dump skip, flush-only responses,
|
feature) — input: (peer identity, resolved repo id, request-reader,
|
||||||
probe handling).
|
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
|
Both substrates feed the same V2 state machines; statelessness is a
|
||||||
substrate property (per-request state), not a protocol fork.
|
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
|
`fetch=wait-for-done` for the v1 done-path policy — ADR-003 pins the
|
||||||
values; OQ-02 may extend them when multi-round lands,
|
values; OQ-02 may extend them when multi-round lands,
|
||||||
`object-format=sha1`). Unimplemented features are declined by omission
|
`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
|
### ls-refs
|
||||||
|
|
||||||
- Parse `command=ls-refs` (peel, symrefs, ref-prefix), stream ref lines
|
- 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
|
### fetch
|
||||||
|
|
||||||
@@ -53,10 +62,10 @@ substrate property (per-request state), not a protocol fork.
|
|||||||
POC-1 to-do).
|
POC-1 to-do).
|
||||||
- Negotiation policy (v1 initial): full-closure pack on `done`
|
- Negotiation policy (v1 initial): full-closure pack on `done`
|
||||||
(POC-validated). Multi-round ack/NAK negotiation: **OQ-02**.
|
(POC-validated). Multi-round ack/NAK negotiation: **OQ-02**.
|
||||||
- Pack generation via core (ADR-004), streamed over sideband on duplex /
|
- Pack generation via `GitPackGen` (ADR-004), streamed over sideband on
|
||||||
sideband-in-response on http; generation runs on `spawn_blocking` with
|
duplex / sideband-in-response on http; generation runs on
|
||||||
the owned odb handle moved in (POC-2's shape: store shared, handle per
|
`spawn_blocking` with the owned handle moved in (POC-2's shape: store
|
||||||
session, generation on blocking threads).
|
shared, handle per session, generation on blocking threads).
|
||||||
- Round/haves budgets enforced here (ADR-009).
|
- Round/haves budgets enforced here (ADR-009).
|
||||||
|
|
||||||
### receive-pack (push)
|
### 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,
|
transactions, emit status report. The capability set, shallow policy,
|
||||||
CAS timing, and exact status-report shape are OQ-04's investigation
|
CAS timing, and exact status-report shape are OQ-04's investigation
|
||||||
target (design intent: shape per OQ-04's resolution).
|
target (design intent: shape per OQ-04's resolution).
|
||||||
- Validation pending — **OQ-04** (includes the receive-pack version
|
- Ingestion via `GitPackIngest` (ADR-004); validation pending — **OQ-04**
|
||||||
surface, which ADR-003's fetch-V2 decision does not cover).
|
(includes the receive-pack version surface, which ADR-003's fetch-V2
|
||||||
|
decision does not cover).
|
||||||
|
|
||||||
### Error taxonomy
|
### Error taxonomy
|
||||||
|
|
||||||
@@ -83,10 +93,11 @@ round, receive-pack max size, wall clock, sideband chunk size (fixed
|
|||||||
|
|
||||||
## Public API surface (v1)
|
## Public API surface (v1)
|
||||||
|
|
||||||
`lib.rs` re-exports: `Session` (duplex) + `StatelessRequest` (http)
|
Crate-root re-exports (alktty pattern; the full list in
|
||||||
entry points, `Limits`, hook trait(s) connecting to core
|
[backend.md](backend.md) §public API): `GitAdapter` + `register_openable`,
|
||||||
(advertisement data, pack generation, ingestion), protocol error enums.
|
`GitSession`, substrate types, `Limits`, the backend traits (ADR-010's
|
||||||
The exact embedder-facing freeze point: **OQ-03**.
|
seam), protocol error enums; gix-feature types under the feature.
|
||||||
|
Publish-freeze point: **OQ-03**.
|
||||||
|
|
||||||
## Design Decisions
|
## 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) |
|
| [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 |
|
| [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 |
|
| [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
|
## Open Questions
|
||||||
|
|
||||||
- **OQ-02**: V2 multi-round negotiation (open — efficiency, not
|
- **OQ-02**: V2 multi-round negotiation (open — efficiency, not
|
||||||
correctness).
|
correctness).
|
||||||
- **OQ-04**: receive-pack state machine + validation (deferred(unclear)).
|
- **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)).
|
- **OQ-05**: sha256 policy (deferred(scope)).
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- `docs/research/poc-1-findings.md`, `docs/research/poc2-findings.md`, `docs/research/poc3-findings.md` (the
|
- `docs/research/poc-1-findings.md`, `docs/research/poc2-findings.md`,
|
||||||
normative wire behavior — observed against real git, not docs' grammar)
|
`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/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 |
|
| 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 |
|
| [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 |
|
| [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 |
|
| [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
|
Phase 0 is done when POC-1..3 have results and a recommended-approach
|
||||||
summary is written here (append below). Then the Architect produces
|
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
|
# 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
|
**Phase**: 0 (exploration) — this document captures WHAT we are building and
|
||||||
WHY before architecture (phase 1) commits to HOW.
|
WHY before architecture (phase 1) commits to HOW.
|
||||||
|
|
||||||
## Vision
|
## Vision
|
||||||
|
|
||||||
A self-hosted, single-binary git server in Rust: `alkgitd` serves repositories
|
A git payload service for the alk family, in Rust: alkgit implements the
|
||||||
over **http** and **ssh** interfaces with an authenticated-by-default surface,
|
git smart protocol as a **pure protocol crate** on alkcall channels (the
|
||||||
built on the alk stack (alkcall, alkhttp, alktls, alkvault) and gitoxide.
|
`alk/git` ALPN) — storage behind backend traits (gitoxide implementation
|
||||||
It exists because self-hosted git platforms (gitea/gitlab) are large
|
behind a feature), producer/consumer halves, no binary, no front doors.
|
||||||
multi-component applications with a long-tail of exposed APIs, plaintext
|
Doors are family infrastructure: alkhttp exposes smart-http, alkssh
|
||||||
secret storage, and web-UI attack surface — and because CVE-2026-59774
|
(planned) will expose git-over-ssh, the alknet rewrite carries the native
|
||||||
(gitea) demonstrated that a single logic bug in an internally-exposed API is
|
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
|
enough for full compromise of the host. Our blast-radius and exposure model
|
||||||
is designed from day one, not retrofitted.
|
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
|
2. **Secret hygiene**: gitea stores secrets plaintext in its DB. alkgit uses
|
||||||
alkvault for anything credential-shaped; the metadata store holds vault
|
alkvault for anything credential-shaped; the metadata store holds vault
|
||||||
references, never plaintext secrets.
|
references, never plaintext secrets.
|
||||||
3. **Footprint**: a git server is a small program (storage + smart protocol +
|
3. **Footprint**: a git server is a small program (storage + smart
|
||||||
two front doors). The platform features (issues, PRs, wikis, CI) are where
|
protocol + thin doors); the platform features (issues, PRs, CI) are where
|
||||||
the CVEs live. alkgit v1 is deliberately just the git server; everything
|
the CVEs live. alkgit v1 is deliberately just the git service as a
|
||||||
else stays out.
|
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
|
4. **Language**: the whole serving path is Rust (memory-safe, no C deps in
|
||||||
the packet path).
|
the packet path).
|
||||||
|
|
||||||
@@ -62,42 +68,49 @@ is designed from day one, not retrofitted.
|
|||||||
- Issues/PR/review features.
|
- Issues/PR/review features.
|
||||||
- Git LFS (later phase, separate decision).
|
- Git LFS (later phase, separate decision).
|
||||||
- federation/replication between alkgit instances (later phase).
|
- 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
|
- Windows as a serving platform (linux first; keep code portable-ish but
|
||||||
don't test it).
|
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` (single crate) | wire layer (substrates, V2 state machines) + producer/consumer halves + backend traits |
|
||||||
| `alkgit-transport` | smart protocol: pkt-line sessions, advertise, ls-refs, fetch, receive-pack |
|
| `gix` feature (default on) | gitoxide-backed implementation of the backend traits |
|
||||||
| `alkgit-http` | http front door (smart-http endpoints + admin API via alkhttp) |
|
| alkhttp `git` feature (future) | smart-http mounting of the stateless substrate |
|
||||||
| `alkgit-ssh` | ssh front door (git commands over alkcall channels) |
|
| alkssh (future family crate) | git-over-ssh door |
|
||||||
| `alkgitd` | binary: config, assembly, TLS/ACME, serving loops |
|
| downstream assembly | the actual deployment (config, listeners, TLS/ACME, vault) |
|
||||||
|
|
||||||
## Composability: "ALPN as a service"
|
## Composability: "ALPN as a service"
|
||||||
|
|
||||||
alkgit is the git member of the alk "ALPN as a service" family — alktty,
|
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
|
alktunnels, and alksocks (socks5) follow the same pattern, and the alknet
|
||||||
mono-repo is being decomposed into exactly these pieces for rewrite. The
|
mono-repo is being decomposed into exactly these pieces for rewrite. The
|
||||||
pattern's load-bearing rule: **`alkgit-core` + `alkgit-transport` never know
|
pattern's load-bearing rule: **alkgit never knows which front door is
|
||||||
which front door is talking.** The transport layer consumes (identity, repo
|
talking.** The protocol crate consumes (identity, repo
|
||||||
id, duplex stream, limits) and speaks git; everything above the stream is
|
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:
|
use:
|
||||||
|
|
||||||
- A future gitea/gitlab-like application should be able to embed
|
- A future gitea/gitlab-like application embeds `alkgit` (wire layer, with
|
||||||
`alkgit-core` + `alkgit-transport` (or talk to `alkgitd`) and add its own
|
its own backend implementation via the traits or the gix feature) and
|
||||||
UI/issues/PR layer without forking anything here.
|
adds its own UI/issues/PR layer without forking anything here.
|
||||||
- The admin API is a set of alkcall ops, not an embedded web framework —
|
- Registry management ops, if the gix feature ships any, are a set of
|
||||||
a downstream app can either use it or replace it.
|
alkcall ops, not an embedded web framework — a downstream app can either
|
||||||
- Nothing in the core may reach upward into alkhttp/alkssh concerns
|
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).
|
(no http types, no channel types below the transport boundary).
|
||||||
|
|
||||||
The v1 reduction follows from this: **storage + ACL + protocol adapters**.
|
The v1 reduction follows from this: **wire layer + backend traits + one
|
||||||
A simple static/template web UI for the public repo listing is explicitly
|
gix implementation**. A simple static/template web UI for the public repo
|
||||||
out of scope here (would be a separate downstream thing on top).
|
listing is explicitly out of scope here (would be a separate downstream
|
||||||
|
thing on top).
|
||||||
|
|
||||||
## Primary deployment target
|
## Primary deployment target
|
||||||
|
|
||||||
@@ -117,19 +130,19 @@ in use**. So:
|
|||||||
- [x] alk stack fit + integration surface (`alk-stack.md`)
|
- [x] alk stack fit + integration surface (`alk-stack.md`)
|
||||||
- [x] server-side protocol inventory (`git-protocol.md`)
|
- [x] server-side protocol inventory (`git-protocol.md`)
|
||||||
- [x] license/reference policy (`reference-policy.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)
|
(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
|
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)
|
(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
|
## Immediate threat-model notes carried into architecture
|
||||||
|
|
||||||
- The admin API (repo/user/permission management) is alkcall
|
- Registry management ops, if the gix feature ships any, are alkcall
|
||||||
`Visibility::Internal` ops over an admin-only listener; it is never part
|
`Visibility::Internal` ops over an admin-only listener; they are never
|
||||||
of the git traffic surface.
|
part of the git traffic surface (OQ-07).
|
||||||
- Repo names arriving on the wire are registry IDs, never paths; storage
|
- Repo names arriving on the wire are registry IDs, never paths; storage
|
||||||
roots are configured server-side only.
|
roots are configured server-side only.
|
||||||
- The advertisement phase runs ACL before the first ref line is emitted.
|
- 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