Export one credential-store URL predicate and its vector table #30

Merged
vnprc merged 1 commit from agent/credential-url-predicate into master 2026-09-19 22:12:15 +01:00
Member

Nothing changes for a running machine: this adds exports and checks to the identity template and removes nothing, so every existing consumer reads exactly what it read before. What is new is one place that owns the https://user:token@host template rule, which allod/archetypes, allod/nexus and allod/tools each spell out today and keep aligned by comment: credential-store-url.json holds the URL grammar, the blank-line class, and 26 accept and reject vectors, and the flake exports the predicate over it, plus a per-group projection of the local auth refresh metadata that is validated on the way out the same way the token registry already is. What proves it is nix flake check, which now runs the predicate over every vector and the projection over the real registry plus one sabotage registry per refresh rule, each check shown to go red when its input is broken. If this is wrong, the symptom is a consumer's evaluation refusing a registry it accepted before, or the new checks failing, and the fix is a straight revert of this one commit, because no consumer depends on the new exports until its own PR lands. A deployment's private fork of this template must merge this change before the allod/nexus and allod/tools follow-ups reach its machines, since those follow-ups refuse a secrets checkout without these exports and say so by name.

Refs allod/secrets#29

What changed

  • credential-store-url.json: the grammar as data (line, blank_line) and vectors, each a registry credential object with a name and an accept bit. The blank-line class is space and tab only, because modules/netrc.nix in allod/archetypes drops lines with awk 'NF { print }', whose default field splitting keeps a line holding only a carriage return; the vectors pin the carriage-return, vertical-tab and form-feed cases as refusals for that reason.
  • flake.nix: lib.credentialStoreUrl (the parsed file); lib.isCredentialStoreUrlTemplate (the grammar test on one credential, no format guard, so a downstream registry validator with its own format policy can compose it, as asked in the issue's comment) and lib.isCredentialStoreUrlSource (the guard composed with it, which is what archetypes checks today); lib.localAuthRefreshDiagnostics (the rules refresh-local-auth applies in jq today, as a function over a registry so checks can feed fixtures) and lib.localAuthRefreshSources (the projection, asserted on read). An explicit local_auth_refresh: null counts as no entries, as jq's // treats it; false is refused because a boolean is not a list. New checks credential-store-url and local-auth-refresh.
  • README.md: the new file, exports and checks in the lists that enumerate them.

Risk

R2. Localized: new exports and checks, no consumer switched, nothing removed. The projection asserts on read, so a consumer that adopts it fails evaluation on a registry the jq path would also have refused; that is the intended fail-closed behaviour and it is exercised by the check's sabotage registries. Rollback is a straight revert.

Validation

Run by the overseer in the worktree at commit 66b873d:

  • nix flake check path:. — all checks passed! (external-ssh-trust-targets, credential-registry, credential-store-url, local-auth-refresh, pi-credential-registry, credential-inventory).
  • nix eval --json path:.#lib.localAuthRefreshSources — the public registry's one refresh entry, projected under its group alias.
  • nix eval --json path:.#lib.credentialStoreUrl | jq '.vectors | length' — 26 (4 accept, 22 reject).
  • lib.localAuthRefreshDiagnostics on a group with local_auth_refresh = null — [].
  • Real consumers against this worktree: the allod/nexus script from its PR reads the projection, selects the group, passes preflight and stops at the decrypt step for lack of a key; the allod/tools build from its PR reads lib.credentialStoreUrl in a rotate dry run. Both refuse a checkout at current master by name.

Run by the worker, output read by the overseer:

  • Sabotage of credential-store-url: flipping the carriage-return blank line vector to accept fails the check naming that vector; a template predicate that accepts everything fails it naming the 20 format-free reject vectors.
  • Sabotage of local-auth-refresh: a positive fixture with a path-suffixed template fails with is not a credential-store URL source; treating null as a non-list fails the null assertion.

Cold cross-family review: codex gpt-5.6-sol, reasoning effort high, on the commit before the fix round (2fd1dfb). One major, fixed: an explicit local_auth_refresh: null was refused where jq accepted it; now null is no entries, with sabotage coverage. One minor, fixed: README and a flake comment claimed the consumers already read the table; reworded to intent. Findings after the fix round are settled by the checks above, not by another reading.

Nothing changes for a running machine: this adds exports and checks to the identity template and removes nothing, so every existing consumer reads exactly what it read before. What is new is one place that owns the `https://user:token@host` template rule, which allod/archetypes, allod/nexus and allod/tools each spell out today and keep aligned by comment: `credential-store-url.json` holds the URL grammar, the blank-line class, and 26 accept and reject vectors, and the flake exports the predicate over it, plus a per-group projection of the local auth refresh metadata that is validated on the way out the same way the token registry already is. What proves it is `nix flake check`, which now runs the predicate over every vector and the projection over the real registry plus one sabotage registry per refresh rule, each check shown to go red when its input is broken. If this is wrong, the symptom is a consumer's evaluation refusing a registry it accepted before, or the new checks failing, and the fix is a straight revert of this one commit, because no consumer depends on the new exports until its own PR lands. A deployment's private fork of this template must merge this change before the allod/nexus and allod/tools follow-ups reach its machines, since those follow-ups refuse a secrets checkout without these exports and say so by name. Refs allod/secrets#29 ## What changed - `credential-store-url.json`: the grammar as data (`line`, `blank_line`) and `vectors`, each a registry credential object with a name and an `accept` bit. The blank-line class is space and tab only, because `modules/netrc.nix` in allod/archetypes drops lines with `awk 'NF { print }'`, whose default field splitting keeps a line holding only a carriage return; the vectors pin the carriage-return, vertical-tab and form-feed cases as refusals for that reason. - `flake.nix`: `lib.credentialStoreUrl` (the parsed file); `lib.isCredentialStoreUrlTemplate` (the grammar test on one credential, no `format` guard, so a downstream registry validator with its own `format` policy can compose it, as asked in the issue's comment) and `lib.isCredentialStoreUrlSource` (the guard composed with it, which is what archetypes checks today); `lib.localAuthRefreshDiagnostics` (the rules `refresh-local-auth` applies in jq today, as a function over a registry so checks can feed fixtures) and `lib.localAuthRefreshSources` (the projection, asserted on read). An explicit `local_auth_refresh: null` counts as no entries, as jq's `//` treats it; `false` is refused because a boolean is not a list. New checks `credential-store-url` and `local-auth-refresh`. - `README.md`: the new file, exports and checks in the lists that enumerate them. ## Risk R2. Localized: new exports and checks, no consumer switched, nothing removed. The projection asserts on read, so a consumer that adopts it fails evaluation on a registry the jq path would also have refused; that is the intended fail-closed behaviour and it is exercised by the check's sabotage registries. Rollback is a straight revert. ## Validation Run by the overseer in the worktree at commit 66b873d: - `nix flake check path:.` — `all checks passed!` (external-ssh-trust-targets, credential-registry, credential-store-url, local-auth-refresh, pi-credential-registry, credential-inventory). - `nix eval --json path:.#lib.localAuthRefreshSources` — the public registry's one refresh entry, projected under its group alias. - `nix eval --json path:.#lib.credentialStoreUrl | jq '.vectors | length'` — 26 (4 accept, 22 reject). - `lib.localAuthRefreshDiagnostics` on a group with `local_auth_refresh = null` — `[]`. - Real consumers against this worktree: the allod/nexus script from its PR reads the projection, selects the group, passes preflight and stops at the decrypt step for lack of a key; the allod/tools build from its PR reads `lib.credentialStoreUrl` in a rotate dry run. Both refuse a checkout at current master by name. Run by the worker, output read by the overseer: - Sabotage of `credential-store-url`: flipping the `carriage-return blank line` vector to accept fails the check naming that vector; a template predicate that accepts everything fails it naming the 20 format-free reject vectors. - Sabotage of `local-auth-refresh`: a positive fixture with a path-suffixed template fails with `is not a credential-store URL source`; treating null as a non-list fails the null assertion. Cold cross-family review: codex `gpt-5.6-sol`, reasoning effort `high`, on the commit before the fix round (2fd1dfb). One major, fixed: an explicit `local_auth_refresh: null` was refused where jq accepted it; now null is no entries, with sabotage coverage. One minor, fixed: README and a flake comment claimed the consumers already read the table; reworded to intent. Findings after the fix round are settled by the checks above, not by another reading.
The https://user:token@host grammar that decides which credentials can
feed a local auth refresh is currently written out separately in
allod/archetypes, allod/nexus, and allod/tools, kept in step only by a
comment in each. This repo now owns it once, as data plus a predicate
its consumers read.

credential-store-url.json holds the grammar (the URL line and the
blank-line class) and 26 accept/reject vectors, including the
carriage-return, vertical-tab, and form-feed lines that the deployed
netrc parser keeps and so must be refused. The blank-line class is
space and tab only, because that parser drops lines with awk's default
field splitting.

The flake exports lib.credentialStoreUrl, lib.isCredentialStoreUrlSource,
lib.localAuthRefreshDiagnostics, and lib.localAuthRefreshSources. The
last is the refresh metadata refresh-local-auth validates in jq today,
projected per group alias and asserted on read the same way the
credential registry already is, so the script can stop deriving the URL
grammar itself.

The grammar and this registry's rule about `format` are exported as two
predicates. lib.isCredentialStoreUrlTemplate is the grammar alone, and
lib.isCredentialStoreUrlSource is that plus the refusal of any credential
carrying `format`. A consumer whose own policy for `format` differs
composes the template predicate instead of respelling the grammar, and the
check pins the two to the same answer on every vector carrying no `format`.

Two checks: credential-store-url asserts the predicate agrees with
every vector and refuses a table with no accept or no reject vector;
local-auth-refresh forces the public registry's projection and runs one
sabotage registry per refresh rule, each pinned to its own diagnostic.

A group may declare `local_auth_refresh` as null and mean "nothing to
refresh", which the jq read through `//` and the registry validator
accepts, so that is treated as no entries; any other non-list value,
including false, is still refused.

Nothing was removed; consumers switch over in their own repos.

Refs allod/secrets#29
vnprc approved these changes 2026-09-19 22:12:06 +01:00
vnprc merged commit 66b873d9f9 into master 2026-09-19 22:12:15 +01:00
vnprc deleted branch agent/credential-url-predicate 2026-09-19 22:12:15 +01:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
allod/secrets!30
No description provided.