- vision: 'ALPN as a service' section (core is front-door-blind; v1 reduces to storage + ACL + protocol adapters); primary deployment target = public anonymous fetch, authenticated push, minimal ACL - alk-stack: concrete composability boundary (core+transport depend on alkcall types only; adapters replaceable) - AGENTS.md: convention 15 codifies the boundary Verified: none needed (markdown only)
137 lines
6.9 KiB
Markdown
137 lines
6.9 KiB
Markdown
# alkgit Phase 0: Vision and Guiding Principles
|
|
|
|
**Status**: draft v1 — 2026-09-19
|
|
**Phase**: 0 (exploration) — this document captures WHAT we are building and
|
|
WHY before architecture (phase 1) commits to HOW.
|
|
|
|
## Vision
|
|
|
|
A self-hosted, single-binary git server in Rust: `alkgitd` serves repositories
|
|
over **http** and **ssh** interfaces with an authenticated-by-default surface,
|
|
built on the alk stack (alkcall, alkhttp, alktls, alkvault) and gitoxide.
|
|
It exists because self-hosted git platforms (gitea/gitlab) are large
|
|
multi-component applications with a long-tail of exposed APIs, plaintext
|
|
secret storage, and web-UI attack surface — and because CVE-2026-59774
|
|
(gitea) demonstrated that a single logic bug in an internally-exposed API is
|
|
enough for full compromise of the host. Our blast-radius and exposure model
|
|
is designed from day one, not retrofitted.
|
|
|
|
## The problem, precisely
|
|
|
|
1. **Exposure model**: mainstream self-hosted git servers expose a large
|
|
HTTP API surface regardless of auth (openapi describes everything;
|
|
enforcement is somewhere else). alkcall inverts this: only operations the
|
|
caller has privileges for are visible on the wire, and `Visibility::Internal`
|
|
ops are structurally unreachable. alkgit inherits that property by using
|
|
alkcall as the transport/protocol substrate.
|
|
2. **Secret hygiene**: gitea stores secrets plaintext in its DB. alkgit uses
|
|
alkvault for anything credential-shaped; the metadata store holds vault
|
|
references, never plaintext secrets.
|
|
3. **Footprint**: a git server is a small program (storage + smart protocol +
|
|
two front doors). The platform features (issues, PRs, wikis, CI) are where
|
|
the CVEs live. alkgit v1 is deliberately just the git server; everything
|
|
else stays out.
|
|
4. **Language**: the whole serving path is Rust (memory-safe, no C deps in
|
|
the packet path).
|
|
|
|
## Guiding principles (architecture must honor)
|
|
|
|
1. **Authenticated by default** — no unauthenticated endpoint exists unless a
|
|
repo is explicitly marked public; even then, advertisement is the only
|
|
anonymous surface, and push is always authenticated.
|
|
2. **Visible-surface = authorized-surface** — the alkcall model end to end;
|
|
no endpoint exists on the wire that a caller cannot see themselves
|
|
authorized for; internal/admin ops are never wire-reachable.
|
|
3. **No plaintext secrets at rest** — alkvault or nothing.
|
|
4. **Thin interfaces, one core** — http and ssh are adapters that authenticate,
|
|
resolve a repo, and hand a duplex stream to the transport layer. Policy
|
|
lives in exactly one place. The core never knows which front door is
|
|
talking (see "ALPN as a service" below).
|
|
5. **Honest capability advertisement** — the git protocol advertises only
|
|
what we actually serve (this is both protocol correctness and the
|
|
security pattern: promise/enforce in the same place).
|
|
6. **gitoxide for storage/wire primitives** — never shell out to the `git`
|
|
binary (no GPL dependency, no process injection surface); own the smart
|
|
protocol server layer ourselves on gitoxide primitives.
|
|
7. **Bounded resources** — every protocol session carries wall-clock, size,
|
|
and round limits; git servers are internet-facing by definition.
|
|
|
|
## Non-goals for v1
|
|
|
|
- Web UI of any kind.
|
|
- Issues/PR/review features.
|
|
- Git LFS (later phase, separate decision).
|
|
- federation/replication between alkgit instances (later phase).
|
|
- Serving as a general sshd (only the git command surface).
|
|
- Windows as a serving platform (linux first; keep code portable-ish but
|
|
don't test it).
|
|
|
|
## Sub-crate shape (provisional, matches workspace skeleton)
|
|
|
|
| crate | role |
|
|
|---|---|
|
|
| `alkgit-core` | storage: repos, refs, odb, pack read/write, access-rule types |
|
|
| `alkgit-transport` | smart protocol: pkt-line sessions, advertise, ls-refs, fetch, receive-pack |
|
|
| `alkgit-http` | http front door (smart-http endpoints + admin API via alkhttp) |
|
|
| `alkgit-ssh` | ssh front door (git commands over alkcall channels) |
|
|
| `alkgitd` | binary: config, assembly, TLS/ACME, serving loops |
|
|
|
|
## Composability: "ALPN as a service"
|
|
|
|
alkgit is the git member of the alk "ALPN as a service" family — alktty,
|
|
alktunnels, and alksocks (socks5) follow the same pattern, and the alknet
|
|
mono-repo is being decomposed into exactly these pieces for rewrite. The
|
|
pattern's load-bearing rule: **`alkgit-core` + `alkgit-transport` never know
|
|
which front door is talking.** The transport layer consumes (identity, repo
|
|
id, duplex stream, limits) and speaks git; everything above the stream is
|
|
the adapter's problem. That is what keeps alkgit composable for downstream
|
|
use:
|
|
|
|
- A future gitea/gitlab-like application should be able to embed
|
|
`alkgit-core` + `alkgit-transport` (or talk to `alkgitd`) and add its own
|
|
UI/issues/PR layer without forking anything here.
|
|
- The admin API is a set of alkcall ops, not an embedded web framework —
|
|
a downstream app can either use it or replace it.
|
|
- Nothing in the core may reach upward into alkhttp/alkssh concerns
|
|
(no http types, no channel types below the transport boundary).
|
|
|
|
The v1 reduction follows from this: **storage + ACL + protocol adapters**.
|
|
A simple static/template web UI for the public repo listing is explicitly
|
|
out of scope here (would be a separate downstream thing on top).
|
|
|
|
## Primary deployment target
|
|
|
|
Our own use case is the design anchor: **public self-hosted repos with
|
|
anonymous clone over http, authenticated push, no gitea-style app features
|
|
in use**. So:
|
|
|
|
- Anonymous *fetch* (clone/fetch advertisement + pack) on explicitly-public
|
|
repos is a first-class path, not an afterthought.
|
|
- Push is always authenticated, on every repo, no exceptions.
|
|
- ACL must be simple enough to reason about completely: repo visibility
|
|
(public/private) + identity-based read/write, nothing richer in v1.
|
|
|
|
## What phase 0 must still produce before phase 1
|
|
|
|
- [x] gitoxide capability + version research (`gitoxide.md`)
|
|
- [x] alk stack fit + integration surface (`alk-stack.md`)
|
|
- [x] server-side protocol inventory (`git-protocol.md`)
|
|
- [x] license/reference policy (`reference-policy.md`)
|
|
- [ ] POC-1: real `git clone` over alkcall BiStream using gix-packetline
|
|
(validates BiStream fit + packetline async codec end-to-end)
|
|
- [ ] POC-2: server-side pack generation composition (bundle-write vs
|
|
entries-to-bytes) for a small want/have set
|
|
- [ ] POC-3: http smart-endpoint streaming shape through alkhttp
|
|
(chunked pack response, unbuffered receive-pack POST ingestion)
|
|
- [ ] Convergence: recommended approach summary feeding phase 1 architecture
|
|
|
|
## Immediate threat-model notes carried into architecture
|
|
|
|
- The admin API (repo/user/permission management) is alkcall
|
|
`Visibility::Internal` ops over an admin-only listener; it is never part
|
|
of the git traffic surface.
|
|
- Repo names arriving on the wire are registry IDs, never paths; storage
|
|
roots are configured server-side only.
|
|
- The advertisement phase runs ACL before the first ref line is emitted.
|
|
- receive-pack applies ref updates via CAS transactions; hooks/plugins do
|
|
not exist in v1 (no arbitrary code execution surface at all). |