spec: close gaps phase 4 exposed in the branch-ACL and inbox-routing rules
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.
No reviews of this commit yet — record a verdict below.
Start a review
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
--