Repository navigation
feat(policy): host baseline push policy judged with every repository's own - #98
Open
jonasgrosch wants to merge 1 commit into
Open
jonasgrosch wants to merge 1 commit into
jonasgrosch wants to merge 1 commit into
Conversation
…s own
A repository has no policy.json until an admin writes one, so a new repository
is allow-all until then, and a per-repo write races the first push.
`[policy] baseline = "<path>"` names one more document in the policy.json
language. It is read at startup with the same parser and validator; an
unreadable or invalid file stops the process and fails `walgit config check`.
It is held in memory.
Combination: both apply. Receive-pack judges a push against the baseline, then
against the repository's own policy.json, and the push must pass both. This is
protect's AND carried across documents, so order only decides which rule the ng
line names (`rejected by baseline rule '<name>'` comes first). Each document
resolves `group:` against its own roster. The language has no admitting effect,
so a repository can add restrictions and never lift one: neither a policy.json
written for a local reason nor a PUT of an empty document can drop the host's
protection. The settings dry-run judges after the baseline the same way.
GET …/policy still returns the repository's own document, which the editor
round-trips through PUT. The new GET …/policy/effective lists the layers in
evaluation order, `{layers: [{source: baseline|repository, policy}]}`.
Round trips: unchanged. Receive-pack still does one policy.json GET (a 404 costs
nothing) and the baseline adds 0 store requests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nibzard
added a commit
to nibzard/walgit
that referenced
this pull request
Oct 14, 2026
… every repository's own Co-Authored-By: Claude Code <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A repository has no
policy.jsonuntil an admin writes one, so a new repository is allow-all until then — and a per-repo write races the first push. Deployments that want every repository to protect a ref from the first push had no way to say so.What
[policy] baseline = "<path>"names one more document in thepolicy.jsonlanguage (docs/POLICY.md, "Host baseline"), read once at startup with the same parser and validator; unreadable or invalid stops the process (and failswalgit config check). Held in memory: 0 extra store requests on a push.Combination law: both apply
A push is judged against the baseline, then the repository's own
policy.json, and must pass both —protect's AND carried across documents. Order only decides which rule a rejection names (rejected by baseline rule '<name>'first). Each document resolvesgroup:against its own roster.Why not "own replaces baseline": repositories get a
policy.jsonfor local reasons; under replacement that write — or aPUTof an empty document — would silently drop the host's protection. The language has no admitting effect, so a repository can add restrictions and never lift one; exceptions are the operator's.The overlapping-bypass lockout check stays per document (across documents it would refuse most repository rules under a baseline over
refs/heads/**).POST …/policy/dry-runjudges after the baseline exactly like receive-pack, so a cross-document lockout is visible before it bites.API
GET …/policyunchanged (the Settings editor round-trips it throughPUT); newGET …/policy/effective→{layers:[{source:"baseline"|"repository", policy}]}; dry-run evaluates after the baseline.Notes
Host-only config; every host that runs receive-pack must carry the same file. docs/POLICY.md carries a worked baseline (members in
draft/**/explore/**, production for one deploy principal, the rest ofrefs/heads+refs/tagsfor automation); a test pins it verbatim. Follow-ups: the Settings page's "(empty = allow-all)" hint and an SDKrepo.policy.effective().Tests
Unit: composition (baseline alone, own adds never lifts, empty own document, rule naming, roster isolation), the documented example's full principal × ref × op matrix, every policy.json refusal also refuses the baseline. Integration: receive-pack with and without an own policy.json,
…/policyvs…/policy/effective, dry-run with/without a baseline, startup refusing a missing/unparsable/lockout baseline. Config: key parses, empty path and unknown keys refused, repo settings can't set[policy].Part of #94. Each of these PRs claims the next decision number in
AGENTS.md(D50/D5x); renumber on merge as you prefer.🤖 Generated with Claude Code