zuka
zuka/AGENTS.md

worklyn / zukapublic

Agent-first git hosting. One Rust binary: git over HTTP and SSH, a REST API, MCP, CI, and multi-tenant isolation.

Get a copy: git clone https://zuka.worklyn.com/worklyn/zuka.git
zuka/AGENTS.md
MDAGENTS.md6.5 KBFormattedDownload
1# AGENTS.md — zuka
2
3Read [`../AGENTS.md`](../AGENTS.md) first; conventions cascade. This file records the
4exceptions and the rules that only apply here.
5
6Before working on a topic, read the doc that owns it: [`SPEC.md`](SPEC.md) for
7architecture, API, security and data model; [`ROADMAP.md`](ROADMAP.md) for sequencing,
8risk and open decisions; [`README.md`](README.md) for what the product is and is not.
9
10## Exception: the runtime is Rust, not Deno
11
12`product/AGENTS.md` states "Runtime is Deno. TypeScript throughout." **zuka is
13Rust**, and this is a stated override rather than a silent one.
14
15Why: the workload is git object manipulation, subprocess supervision and long-lived
16byte streams, and it ships as a single self-hostable binary a stranger can drop on a
17VM. A Deno service would need a runtime installed and would still shell out for every
18git operation. The two existing Rust services in `yangu/` (`atlas`, `kazi`) set the
19in-house precedent this follows.
20
21Deno still applies to **tests** — the live suite in `test/live/` is TypeScript,
22spawning the compiled binary and driving it with a real `git` CLI and real `fetch`.
23
24The service now has one UI: the read-only browser viewer in `src/web/` (SPEC §4.9).
25It is server-rendered from the binary with no bundler and no CDN, because zuka ships
26as a single self-hostable file — so Motheo tokens and Worklyn Sans, which arrive
27through `@ori/motheo` and a font pipeline, do **not** apply to it. It uses a system
28font stack and its own small stylesheet. The marketing page in `yangu/website/`
29follows the design system there, where a build step is available.
30
31If that ever stops being true — if the viewer grows enough to justify a build — this
32exception is the thing to revisit first.
33
34## Rust conventions
35
36Follow `yangu/atlas` and `yangu/kazi`, whose patterns are already load-bearing in
37production:
38
39- `hyper` 1.x + `tokio`, no web framework. `anyhow` internally, one typed `Error` at
40 the HTTP boundary (SPEC §8.3). `serde` on the wire.
41- `edition = "2021"`, standalone crate, committed `Cargo.lock`, `.gitignore` = `target/`,
42 `[profile.release] opt-level = 3`, `lto = "thin"`.
43- Config is `std::env::var` with defaults gathered into `Config::from_env() ->
44 Result<Config>` that fails boot loudly on an invalid value. Prefix `ZUKA_`.
45- Tests are inline `#[cfg(test)] mod tests` with sentence-named cases
46 (`rejects_ref_write_without_if_match`), not `test_`-prefixed.
47- CI gates on `cargo test` → `cargo clippy --all-targets -- -D warnings` →
48 `cargo build --release`, following `yangu/.github/workflows/build-kazi.yml`.
49
50**The live suite must run in CI.** It self-skips without `ZUKA_LIVE_TEST`, and a
51proof that never executes is not a proof — SPEC calls the live tests the place the
52wire protocol and REST contract are actually proven, so the CI job sets the flag and
53installs `git`.
54
55### Deviation: nested `src/`, not flat modules
56
57`yangu/atlas` and `yangu/kazi` keep everything in one `main.rs` (1,158 and 3,050
58lines). zuka nests by concern (SPEC §8) because it carries four protocol surfaces
59against atlas's one. This is a judgment call, recorded as such — do not restate it as
60a measured failure of the flat pattern.
61
62## Rules specific to this service
63
64- **`git::exec::Git` is the only way to invoke git.** Not `Command::new("git")`. It
65 clears the environment and sets `GIT_CONFIG_NOSYSTEM`, so host configuration cannot
66 change what our git calls do. Five wrappers with three different policies existed
67 before it, and `/etc/gitconfig` applied to the ref rule and the quota accounting but
68 not to the wire.
69- **Exit 1 is an answer; anything higher is a fault.** `Git::query` encodes this.
70 Collapsing every non-zero exit into `404` once meant a corrupt repository reported
71 as empty and nothing paged anyone.
72- **Git work never runs on a runtime worker.** Use `git::exec::blocking`. A fork+exec
73 on a tokio worker stalls unrelated requests including `/healthz`, and the pool is
74 only as wide as the core count.
75- **Shared operations live in `core/`.** REST and MCP each had their own repository
76 create and delete once; the copies drifted in four ways before anyone noticed. If a
77 facade needs a rule, the rule goes in `core/` or `git/` and the facade calls it.
78- **`AppState::open_repo` is how a repository is resolved.** It carries the access
79 check, the ready check and — for `Access::Write` — the disk reserve and the storage
80 quota, so no write path can forget them.
81
82- **Validation in SPEC §2.5 is normative.** Ref names, repo names, tree paths and file
83 modes are security controls, not input hygiene. A ref name reaching the filesystem
84 unvalidated is remote code execution. Never hand-roll ref validation; use
85 `gix-validate`.
86- **Every ref mutation goes through a `gix-ref` transaction** with the precondition
87 enforced *inside* it (SPEC §1.3). Check-then-write is a race against `receive-pack`.
88- **One rule, one implementation.** The `update` hook and the REST ref endpoint call
89 the same core function. If a rule appears in both a hook and a handler, it is in the
90 wrong layer.
91- **git is the only writer.** No endpoint and no MCP tool modifies a repository's
92 contents or moves a ref; that is `git push`. Adding a write path means a second
93 home for the fast-forward rule, a concurrency model and a merge story, all of
94 which git already has (SPEC §1.3). If a task seems to need one, the answer is
95 almost certainly that the caller should run git.
96- **Facades translate; they do not decide.** `api/` and `mcp/` may fill defaults and
97 reshape payloads. They may not carry authorization logic — that lives on
98 `Identity`, so REST, MCP and SSH cannot drift. A facade must never call into
99 another facade.
100- **The git paths are not REST.** `/{account}/{repo}.git/*` has its own error contract:
101 `WWW-Authenticate` on `401`, `application/x-git-*` content types, never
102 `problem+json` (SPEC §4.7).
103- **Scheduled work lives in `jobs/`** and its own systemd unit, never the web
104 entrypoint.
105- **CI isolation claims must name a mechanism.** "No network" means a network
106 namespace; "isolated" means a uid, a cgroup and a scrubbed environment. An intention
107 is not a control (SPEC §6.3).
108
109## The gate
110
111Run all four before pushing; CI runs the same set and gates on every one.
112
113```sh
114cargo fmt --check
115cargo clippy --all-targets -- -D warnings
116cargo test
117deno task test:live
118```
119
120## Status
121
122Standalone is complete: M0–M5 and M7. M6 (multi-tenant control plane) is deferred and
123unbuilt — see `ROADMAP.md` D9.
124
125`SPEC.md` describes how the service works; `ROADMAP.md` says what exists and why. When
126they disagree about what is built, the roadmap is right.