docs: identify the load-bearing abstractions and their command surface
commit
edb26b7docs: 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
Reviews
No reviews of this commit yet — record a verdict below.
Start a review
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."