feat: workspace skeleton + phase 0 research docs
- Cargo workspace with 5 sub-crates: alkgit-core (storage), alkgit-transport (smart protocol), alkgit-http, alkgit-ssh, alkgitd (binary); sha1 pinned through the gix stack, sha256 passthrough feature - docs/research/: vision, gitoxide alignment, alk stack fit, server-side protocol inventory, license/reference policy, POC plan - AGENTS.md: conventions mirroring alkcall (no comments, thiserror, tokio, no secrets on wire/at rest, visible-surface=authorized-surface, gitoxide-only serving path, bounded resources, registry-resolved repos) Verified: cargo build, clippy -D warnings, fmt, test, check --all-features
This commit is contained in:
1 parent
a633dddd6b
commit
a3cdef909b
20 files changed
+965
No files matched your search
@@ -0,0 +1,94 @@
|
||||
# Research: alk stack fit for a git server
|
||||
|
||||
**Status**: initial pass complete
|
||||
**Date**: 2026-09-19
|
||||
**Sources**: local sibling crates at `/workspace/@alkdev/{alkcall,alkhttp,alktls,alkvault}`
|
||||
(all MIT OR Apache-2.0; alkcall/alkhttp/alktls published on crates.io,
|
||||
alkvault published at 0.1.0).
|
||||
|
||||
## TL;DR
|
||||
|
||||
The alk stack covers the parts of a self-hosted git server that were
|
||||
historically the attack surface: the HTTP and SSH front doors, the wire
|
||||
framing, TLS/ACME, and secret storage. alkgit contributes the git brain
|
||||
(storage + smart protocol + repo/permission model) and the server binary that
|
||||
assembles everything. The architectural match with alkcall is unusually good:
|
||||
**alkcall's `BiStream` is exactly the byte-stream abstraction git's
|
||||
packetline-based protocol wants underneath it** — the smart protocol is a
|
||||
pair of unidirectional streams with request/response framing, which maps
|
||||
cleanly onto `BiStream::write_half` / `read_half`.
|
||||
|
||||
## How the pieces map
|
||||
|
||||
| alkgit need | alk crate | mechanism |
|
||||
|---|---|---|
|
||||
| Authenticated transport with per-peer identity | alkcall | `Connection`, `Identity`, `IdentityProvider` |
|
||||
| Internal/external operation split | alkcall | `Visibility::Internal` ops are never wire-callable (ADR-017/024) |
|
||||
| Per-repo/per-op authorization | alkcall | `AccessControl::check(peer_identity)` + `required_scopes` |
|
||||
| HTTP front door for git smart-http + admin API | alkhttp | HTTP/1.1 + HTTP/2 serving, adapters for call protocol |
|
||||
| TLS + ACME for the HTTP endpoint | alktls | rustls server configs, cert resolvers, ACME state machine |
|
||||
| Encrypted storage of tokens/keys at rest | alkvault | AES-256-GCM vault |
|
||||
| Byte streams under pkt-line | alkcall | `BiStream` handed to handlers via `ProtocolHandler::connected` |
|
||||
|
||||
## The gitea lesson, restated as alkgit design constraints
|
||||
|
||||
The pwned-gitea incident (CVE-2026-59774) was reachable because an internal
|
||||
API was exposed over the internet regardless of auth — the openapi-spec
|
||||
pattern of "describe everything, enforce somewhere" is the structural
|
||||
antipattern. alkcall already fixes the API-exposure half: only ops the caller
|
||||
has privileges for are even visible, and internal ops are structurally
|
||||
unreachable from the wire. alkgit must extend the same philosophy:
|
||||
|
||||
1. **No unauthenticated surface by default.** Even `info/refs` advertisement
|
||||
requires an authenticated session unless a repo is explicitly public.
|
||||
Anonymous-clone is a per-repo opt-in, not a global default.
|
||||
2. **Admin API = internal ops.** Repo/user/permission management rides the
|
||||
call protocol as `Visibility::Internal` ops over the admin interface
|
||||
(or via alkhttp with auth); there is no admin endpoint that exists on the
|
||||
same unauthenticated surface as git traffic.
|
||||
3. **No plaintext secrets in the DB.** Credentials/tokens go through
|
||||
alkvault; the metadata store holds references, not keys.
|
||||
4. **Blast-radius thinking carries into the design**: single binary, no
|
||||
plugin execution, no markdown renderer by default (a web UI, if it ever
|
||||
exists, is a separate phase and separate threat model).
|
||||
|
||||
## Interfaces in alkcall terms
|
||||
|
||||
Git transport framing per interface:
|
||||
|
||||
- **SSH**: the alkcall channels protocol carries a channel whose stream is
|
||||
the git pkt-line conversation. The client (`git clone ssh://...`) expects
|
||||
an exec of `git-upload-pack '<repo>'`; with alkcall the "SSH channel-like"
|
||||
layer provides the framing and identity, and we dispatch on the requested
|
||||
command string. alkcall's channel model (channel 0 = call registry, data
|
||||
channels = `BiStream`) maps to: one data channel per git session.
|
||||
- **HTTP**: alkhttp hosts the smart-http endpoints (`GET
|
||||
/<repo>/info/refs?service=git-upload-pack`, `POST /<repo>/git-upload-pack`,
|
||||
`POST /<repo>/git-receive-pack`). Smart-http is stateless per request
|
||||
(V0/V1) or one-shot (V2), which fits the http adapter model; the response
|
||||
body is a pkt-line stream we generate.
|
||||
|
||||
Both interfaces converge on the same core: **a `BiStream`-like duplex
|
||||
session + repo identity + caller identity** → `alkgit-transport` runs the
|
||||
smart protocol and produces/consumes packs via `alkgit-core`. The interfaces
|
||||
are thin adapters; all policy (auth, ACL, limits) lives before the transport
|
||||
layer gets the stream.
|
||||
|
||||
## What to verify before architecture commits (POC candidates)
|
||||
|
||||
1. **BiStream ↔ pkt-line fit**: run a real `git clone` handshake over an
|
||||
alkcall `Connection` using `gix-packetline` async codec end-to-end.
|
||||
2. **HTTP smart protocol shape**: confirm alkhttp's streaming response body
|
||||
can carry a pack (chunked) and that request bodies stream in without
|
||||
buffering the whole POST in memory (receive-pack can be gigabytes).
|
||||
3. **alkcall backpressure/limits vs git sessions**: large clones are
|
||||
long-lived, high-throughput, single-direction streams; confirm channels
|
||||
buffer limits (ADR-040) don't fight pack streaming, or route git sessions
|
||||
as raw duplex streams rather than chunk-framed channels if needed.
|
||||
4. **alktls ACME**: config shape for the http endpoint; likely trivial.
|
||||
|
||||
## Version pins in workspace manifests
|
||||
|
||||
- `alkcall 0.8` (published), `alkhttp 0.5` (published), `alktls 0.1`
|
||||
(published), `alkvault 0.1` (published). The workspace root already pins
|
||||
these; bump via workspace.dependencies when siblings release.
|
||||
Reference in new issue
Block a user