git-ents.gitmain
⌘K
foforge
commit edb26b7
docs: identify the load-bearing abstractions and their command surface

Seven abstractions (meta-ref, typed tree, anchor, signed push, check, rendering registry, embeddable server), the primitive-vs-porcelain command split, and a proposed git ents serve local shape over real repos next to the hosted bare-repo shape.

Assisted-by: Claude:claude-fable-5

Joseph D. Carpinelli · 1 month ago

Reviews

No reviews of this commit yet — record a verdict below.

Start a review

verdict

docs/abstractions.adoc @@ -1,0 +1,189 @@ += git-ents abstractions +:toc: + +What this codebase has settled on as its load-bearing abstractions, and the +command surface those abstractions imply. The framing rule: an abstraction +earns a place here only if it already carries weight in more than one crate +or command — nothing speculative. + +== The abstractions + +=== 1. The meta-ref + +One entity (or one small set) per ref under `refs/meta/*`: +`member/<username>`, `account`, `checks`, `toolchains/<name>`, +`comments/<id>`, `revoked`, `config`. + +The meta-ref is simultaneously the unit of: + +* *storage* — the ref points at a commit whose tree is the entity; +* *sync* — fetch or push exactly the entities you care about; +* *authorization* — pre-receive rules match on refnames, so roles gate + entity kinds naturally; +* *history* — the ref's commit log is the entity's audit trail, free. + +Decomposed refs, aggregated views: writes touch one narrow ref (no merge +conflicts between two people editing different members), reads fan in +(`members list` walks `refs/meta/member/*`). + +=== 2. The typed tree (`git-store` + facet) + +A Rust struct annotated with `#[derive(Facet)]` *is* the schema; facet-git-tree +maps it to/from a git tree. No serialization format to version separately — +but the flip side is the struct is the on-disk format, so changing one is a +migration, not a refactor. + +Everything else builds on this: members, checks, comments, toolchains, and +accounts are all just typed trees behind meta-refs. + +=== 3. The anchor (`git-anchor`) + +A durable pointer into content: a blob (and optionally a line range) *at a +commit*. Because the anchor names the commit, it never dangles; because it +can be projected onto a newer commit, a comment placed on last month's code +finds its lines in today's. This is the abstraction that makes review data +storable forever while the code underneath keeps moving. + +=== 4. The signed push (the only write path) + +There is exactly one way to mutate a repository, including its metadata: a +real `git push` carrying a signed-push certificate, verified by `pre-receive` +against the member refs. The web UI has no side door — a browser edit becomes +a real push signed by the server's own key, whose public half must itself be +a member. Auth state *is* repository state: no sessions database, no tokens +table, revocation is a ref update. + +=== 5. The check (and the toolchain that feeds it) + +A check is data (`refs/meta/checks`), not configuration living outside the +repo. `post-receive` enqueues; a worker runs checks asynchronously in a +Sprite; nothing ever blocks a push. A check's toolchains are git trees +(`refs/meta/toolchains/<name>`) extracted into the Sprite and activated via +`PATH` — the repository carries its own CI environment, with `recipe` +provenance and the ref log as the audit trail of who put which compiler +there. + +=== 6. The rendering registry + +Documents render by MIME type through one lookup table +(`git-ents-server/src/render.rs`), to HTML for the web UI or plain text for +the CLI. A table rather than a trait hierarchy because MIME is an open +namespace: unknown types fall through to passthrough instead of refusing to +render. The CLI and the web UI show the *same* comment body because they call +the same function. + +=== 7. The embeddable server + +`git-ents-server` is a library first. `git ents server` and the standalone +binary are the same code; the hooks (`pre-receive`, `post-receive`) are +subcommands of it, not separate programs. This is the hinge for hackability: +anyone who can run the CLI can run the forge. + +== The command surface + +The abstractions map onto git subcommands. The umbrella porcelain is +`git ents`; the primitives underneath deserve to graduate into standalone +`git-$cmd` extensions (their crates already exist with exactly those names), +so a hacker can compose them without buying the whole forge. + +=== Primitives (hackable pieces) + +[source,console] +---- +$ git store show refs/meta/checks # <1> +$ git store show refs/meta/member/joey # any typed tree, pretty-printed + +$ git anchor resolve <comment-id> # <2> +$ git anchor project <comment-id> HEAD # where do those lines live now? + +$ git comment add --file src/lib.rs --lines 40-52 -m "why saturating_add?" +$ git comment show <id> # body rendered as AsciiDoc text + +$ git toolchain import rustup:1.88-aarch64-apple-darwin +$ git toolchain view rust-1.88 # provenance + on-disk footprint +$ git toolchain log rust-1.88 # the ref log as audit trail +---- +<1> `git store` is the escape hatch: render *any* meta-ref via the + compile-time facet walk, including kinds `git ents` has no verb for yet. + This is what makes bring-your-own-schema real rather than aspirational. +<2> Anchors resolve and project independently of comments, so other tools + (reviews, TODO trackers, blame overlays) can reuse them. + +=== The porcelain + +[source,console] +---- +$ git ents setup # sign-my-pushes client config +$ git ents members list|add|remove|revoke|unrevoke|check +$ git ents account create|link +$ git ents checks list|add|run|debug +$ git ents toolchain … # same verbs as git toolchain, plus sync +$ git ents comment … # same verbs as git comment, plus sync +$ git ents login # sign a challenge, like the web UI does +$ git ents server # the whole forge, embedded +---- + +The porcelain's added value over the primitives is exactly one thing: +*remotes*. `git ents comment show` fetches `refs/meta/comments/*` from +`--remote` first; `git comment show` would read only what is already local. +Primitive = local plumbing, porcelain = synced workflow. + +== Local server vs. hosted server + +The same binary serves two shapes, and the difference should stay this small: + +[cols="1,2,2"] +|=== +| | local (`git ents serve`) | hosted (`git-ents-server` on Fly.io) + +| repositories +| *real* (non-bare) repos — your working checkouts +| bare repos under `/data/repos` + +| discovery +| a directory of checkouts, or the current repo +| created on first push + +| auth +| signed pushes *optional* by default (loopback, it is your machine); + `--require-signatures` to rehearse the hosted gate +| signed pushes required (`CERT_NONCE_SEED` set) + +| checks +| run directly on the host (you already trust your machine) +| run in a Fly.io Sprite (isolation) + +| purpose +| hack on the forge, browse your own repos, demo to a friend on LAN +| be the forge +|=== + +Proposed UX for the local shape: + +[source,console] +---- +$ cd ~/code/my-project +$ git ents serve # serve *this* checkout on :8080 +serving my-project at http://localhost:8080/my-project + +$ git ents serve ~/code # serve every repo found one level down +serving 12 repos at http://localhost:8080/ +---- + +Two wrinkles the local shape must own, because real repos differ from bare +ones in exactly these ways: + +* *Push-to-checked-out-branch.* A bare repo accepts any push; a real repo + refuses pushes to the checked-out branch. The local server sets + `receive.denyCurrentBranch=updateInstead` on the repos it serves, so a + push updates the working tree too — which is the behavior a hacker + actually wants from a local forge. +* *Meta-refs vs. working tree.* Pushes to `refs/meta/*` never touch the + working tree, so metadata (comments, members, checks) flows identically + in both shapes. This is not a coincidence; it falls out of abstraction 1, + and it is the reason the local/hosted split can stay a deployment detail + rather than a fork in the data model. + +`git ents server` (the current command) remains the hosted shape verbatim — +hooks, queue, bare repos. `serve` is the porcelain verb for "this machine, +these checkouts, low ceremony."