git-ents.gitmain
⌘K
foforge
commit 1458026
spec: close gaps phase 4 exposed in the branch-ACL and inbox-routing rules

gate.adoc: add gate.branch-acl-undefined (Deferred), giving the refs/heads/* transport-level ACL a citable home — receive’s TransportAuth was landing with no policy to interpret it against; scope gate.verdict-reason’s illustrative example to refs/meta/, since the enumerated requirements never fire against refs/heads/. roots.adoc: flag the same gap in the advisory-story NOTE’s refs/heads/main illustration; add roots.redaction-pack-serving (Deferred) — no composition root through the single-node hosted root wraps upload-pack, so a redacted object already reachable in the odb is still served to any fetch regardless of refs/meta/redactions/*. receive.adoc: cross-reference the new deferred requirement from receive.proposal-shape; scope receive.redaction-ingest’s "withheld from every generated pack" clause to new admission, which is all receive can actually guarantee. meta-ref.adoc: define meta-ref.inbox’s <id> as the canonical ref’s full path below refs/meta/, not a bare id — the ents-sync implementation and an earlier ents-model doctest had silently settled on two different shapes for the identical routing scenario.

Assisted-by: Claude:claude-sonnet-5

Joseph D. Carpinelli · 1 month ago

Reviews

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

Start a review

verdict

docs/spec/gate.adoc @@ -115,6 +115,11 @@ MUST render this reason to the user — for example, "your signing key is not authorized for `refs/heads/main`" — so a negative verdict is actionable before a push is ever attempted. +This guarantee is scoped to `refs/meta/*`: the enumerated requirements +never fire against `refs/heads/*` (<<gate.principled-split>>), whose own +authorization mechanism is not yet specified +(<<gate.branch-acl-undefined>>), so the illustrative refname above is +aspirational until that mechanism exists. -- === Adoption @@ -168,6 +173,25 @@ invariant. -- +[role="requirement", id="gate.branch-acl-undefined"] +.Branch-Ref Transport Authorization Is Unspecified (Deferred) +-- +<<gate.principled-split>> requires `refs/heads/*` to keep transport-level +authorization instead of the tip invariant, but no document in this spec +defines that mechanism: what evidence it consults, where its policy lives, +or how a verdict is derived from it. +`receive.proposal-shape`'s connection-level ACL input for `refs/heads/*` +MUST therefore be threaded through uninterpreted — never substituted for +the tip invariant on a `refs/meta/*` update, which remains fully specified +— until a future spec addition defines this policy. +No advisory call site (<<gate.advisory-local>>, <<sync.pre-flight>>) MAY +render a `refs/heads/*` verdict in the meantime; the illustrative +verdict-reason examples in this file and `roots.adoc` describe tip-invariant +predictions against meta-refs only. +This direction is chosen but not yet designed, the same acknowledged gap +as <<gate.bootstrap>>. +-- + === Bootstrap [role="requirement", id="gate.bootstrap"]
docs/spec/meta-ref.adoc @@ -41,8 +41,17 @@ [role="requirement", id="meta-ref.inbox"] .Inbox and Self-Run Namespaces -- -`refs/meta/inbox/*` MUST hold entities authored by someone not authorized -for the corresponding canonical ref, awaiting adoption. +`refs/meta/inbox/<member>/<canonical-suffix>` MUST hold entities authored +by someone not authorized for the corresponding canonical ref, awaiting +adoption. +`<canonical-suffix>` MUST be the corresponding canonical ref's entire path +below `refs/meta/`, not a bare entity id: `refs/meta/issues/42` routes to +`refs/meta/inbox/<member>/issues/42`, and a multi-segment canonical ref +such as `refs/meta/results/<effect>/<short-oid>` +(<<effect.results-writeback>>) routes to +`refs/meta/inbox/<member>/results/<effect>/<short-oid>` in full — the +namespace segment MUST be preserved so two different entity kinds can +never collide under the same inbox id. `refs/meta/results/~<member>/<effect>/<short-oid>` MUST hold results a member produced on their own executor rather than a designated worker, mirroring the canonical results pattern (<<effect.results-writeback>>)
docs/spec/receive.adoc @@ -32,6 +32,9 @@ `refs/heads/*` (<<gate.principled-split>>) only; gate evaluation for meta-ref admission MUST ignore it entirely and MUST NOT consult it in place of the tip invariant (<<gate.signature-artifact>>). +No policy for that `refs/heads/*` ACL input is specified yet +(<<gate.branch-acl-undefined>>); `receive` MUST thread the evidence +through uninterpreted until one is. -- [role="requirement", id="receive.shared-path"] @@ -132,8 +135,15 @@ `receive` MUST check every incoming object against the redaction list recorded under `refs/meta/redactions/*` at ingest time, so a redacted hole cannot be silently refilled by re-pushing the same bytes. +This binds new admission only: for an object redacted after it was already +accepted, `receive` refusing future pushes of the same bytes does not by +itself withhold the bytes already sitting in the object store. A redacted object's bytes MUST be withheld from the object store and from every generated pack; the oid MUST remain in history as evidence. +Guaranteeing this for bytes already stored before their redaction is not +`receive`'s job — it belongs to whichever component actually generates +outgoing packs, which is not yet specified for every root +(<<roots.redaction-pack-serving>>). A reader resolving a redacted object MUST receive a redaction marker, never an error. --
docs/spec/roots.adoc @@ -114,6 +114,11 @@ gate, evaluated against policy as of Joey's last fetch (<<gate.policy-as-state>>), rendered by the same in-process signing this section specifies — never a separate git-serving transport. +The `refs/heads/main` case is aspirational as stated: it is a +transport-level check, not the tip invariant (<<gate.principled-split>>), +and this spec does not yet define that mechanism +(<<gate.branch-acl-undefined>>), so the local UI has no policy to predict +against for it today. ==== [role="requirement", id="roots.worktree-update"] @@ -171,6 +176,27 @@ repository creation; this direction is chosen but not yet enforced. -- +[role="requirement", id="roots.redaction-pack-serving"] +.Outgoing Pack Serving Ignores Redaction (Deferred) +-- +Every composition root through the single-node hosted root +(<<roots.local>>, <<roots.hosted>>) serves fetches and clones via stock +git's own `upload-pack` acting directly on a real on-disk object +database, which has no knowledge of `refs/meta/redactions/*` and will +serve a redacted object's bytes to any fetch if they are already +reachable in that odb. +`receive`'s ingest-time check (<<receive.redaction-ingest>>) only +refuses a redacted hole being refilled by a new push; it cannot evict +bytes already stored before the redaction record existed, nor bytes a +fetch already served. +`gix-receive` (<<roots.honesty-test>>) replaces only incoming pack +ingestion at the scale-out root; nothing in this design wraps outgoing +pack generation for any root. +Making pack generation redaction-aware, or otherwise physically evicting +redacted bytes from an on-disk repository, is chosen direction but not +yet designed. +-- + [role="requirement", id="roots.embeddable"] .Embeddable Server --