Export the local_auth_refresh projection as a function of a registry #35

Closed
opened 2026-09-20 17:05:59 +01:00 by allod-agent · 0 comments
Member

Export the validated local_auth_refresh projection as a function of a registry, so a fork that validates its own registry calls the constructor instead of re-spelling the mapping and gating it itself.

Primary goals:

  • One constructor, one applied result — lib.mkLocalAuthRefreshSources takes a registry and returns the validated projection, and lib.localAuthRefreshSources becomes that constructor applied to this repository's registry, the same pairing lib.mkPiCredentialContract already has with lib.piCredentials and its siblings.
  • The gate travels with the mapping — the constructor asserts lib.localAuthRefreshDiagnostics is empty before projecting, so a fork cannot get the projection without the validation, and cannot get the validation's diagnostics text wrong by re-spelling it.
  • No consumer changes — allod/nexus scripts/refresh-local-auth keeps reading lib.localAuthRefreshSources with the same shape; nothing about the public registry or its projection changes.
  • The export is witnessed where the mapping already is — the existing local-auth-refresh check forces the projection and runs the sabotage fixtures through the same binding that is exported, so a drift between the constructor and the applied result is impossible by construction rather than pinned by a second check.

Current state and specifics

The projection is built in two private bindings in flake.nix: localAuthRefreshSourcesFor at flake.nix:282 maps each group's entries to { contract; system; local_username; source_credential; secret_path; }, and validateLocalAuthRefresh at flake.nix:296 asserts localAuthRefreshDiagnostics registry == [] and then applies it. Only the applied result is exported, at flake.nix:378: lib.localAuthRefreshSources = validateLocalAuthRefresh rotationRegistry;. The diagnostics function beside it, lib.localAuthRefreshDiagnostics at flake.nix:377, is exported as a function of a registry.

The private handoff on allod/secrets#29 found the gap. A deployment's fork of this repository validates its own registry, not this one, so lib.localAuthRefreshDiagnostics is usable there but lib.localAuthRefreshSources is not: the fork has to copy the field mapping from localAuthRefreshSourcesFor and wrap it in its own assertion on the diagnostics. That is a fourth spelling of a mapping this issue's parent exists to keep at one, and the assertion text a consumer sees at preflight now depends on which copy it was evaluated by.

The Pi credential contract already shows the shape the consumers expect. mkPiCredentialContract at flake.nix:20 is a function of caller-supplied data, piCredentialContract at flake.nix:22 is it applied to this repository's registry, and both are exported: the applied projections as lib.piCredentials and siblings, the constructor as lib.mkPiCredentialContract at flake.nix:389. A fork calls the constructor on its own data and gets the same validation and the same derived shape.

Default: rename validateLocalAuthRefresh to mkLocalAuthRefreshSources, export it as lib.mkLocalAuthRefreshSources, and define lib.localAuthRefreshSources = mkLocalAuthRefreshSources rotationRegistry. The local-auth-refresh check already applies that binding to the public registry and to every sabotage registry, so it witnesses the export as it stands. The README export table gains one row beside lib.localAuthRefreshSources.

Validation: nix flake check in this repository; nix eval .#lib.mkLocalAuthRefreshSources --apply 'f: f {}' evaluates to an empty attribute set, and applying it to the public registry equals lib.localAuthRefreshSources; tests/refresh-local-auth.sh in allod/nexus still passes against this checkout, since the script's contract is unchanged.

Scope

In: the exported constructor, the applied result defined through it, and the README row.

Out: the format policy inside the diagnostics. localAuthRefreshDiagnostics gates a source credential with isCredentialStoreUrlSource, this registry's own format rule; a fork whose registry validator composes a different format policy on lib.isCredentialStoreUrlTemplate inherits this one at refresh time. No fork has asked for a different answer there, so parameterising the constructor over the predicate waits for one that does. Consumer changes in allod/nexus and allod/archetypes are not needed; both keep reading the applied export.

Refs allod/secrets#29.

Export the validated `local_auth_refresh` projection as a function of a registry, so a fork that validates its own registry calls the constructor instead of re-spelling the mapping and gating it itself. Primary goals: - **One constructor, one applied result** — `lib.mkLocalAuthRefreshSources` takes a registry and returns the validated projection, and `lib.localAuthRefreshSources` becomes that constructor applied to this repository's registry, the same pairing `lib.mkPiCredentialContract` already has with `lib.piCredentials` and its siblings. - **The gate travels with the mapping** — the constructor asserts `lib.localAuthRefreshDiagnostics` is empty before projecting, so a fork cannot get the projection without the validation, and cannot get the validation's diagnostics text wrong by re-spelling it. - **No consumer changes** — `allod/nexus` `scripts/refresh-local-auth` keeps reading `lib.localAuthRefreshSources` with the same shape; nothing about the public registry or its projection changes. - **The export is witnessed where the mapping already is** — the existing `local-auth-refresh` check forces the projection and runs the sabotage fixtures through the same binding that is exported, so a drift between the constructor and the applied result is impossible by construction rather than pinned by a second check. ### Current state and specifics The projection is built in two private bindings in `flake.nix`: `localAuthRefreshSourcesFor` at `flake.nix:282` maps each group's entries to `{ contract; system; local_username; source_credential; secret_path; }`, and `validateLocalAuthRefresh` at `flake.nix:296` asserts `localAuthRefreshDiagnostics registry == []` and then applies it. Only the applied result is exported, at `flake.nix:378`: `lib.localAuthRefreshSources = validateLocalAuthRefresh rotationRegistry;`. The diagnostics function beside it, `lib.localAuthRefreshDiagnostics` at `flake.nix:377`, is exported as a function of a registry. The private handoff on allod/secrets#29 found the gap. A deployment's fork of this repository validates its own registry, not this one, so `lib.localAuthRefreshDiagnostics` is usable there but `lib.localAuthRefreshSources` is not: the fork has to copy the field mapping from `localAuthRefreshSourcesFor` and wrap it in its own assertion on the diagnostics. That is a fourth spelling of a mapping this issue's parent exists to keep at one, and the assertion text a consumer sees at preflight now depends on which copy it was evaluated by. The Pi credential contract already shows the shape the consumers expect. `mkPiCredentialContract` at `flake.nix:20` is a function of caller-supplied data, `piCredentialContract` at `flake.nix:22` is it applied to this repository's registry, and both are exported: the applied projections as `lib.piCredentials` and siblings, the constructor as `lib.mkPiCredentialContract` at `flake.nix:389`. A fork calls the constructor on its own data and gets the same validation and the same derived shape. Default: rename `validateLocalAuthRefresh` to `mkLocalAuthRefreshSources`, export it as `lib.mkLocalAuthRefreshSources`, and define `lib.localAuthRefreshSources = mkLocalAuthRefreshSources rotationRegistry`. The `local-auth-refresh` check already applies that binding to the public registry and to every sabotage registry, so it witnesses the export as it stands. The README export table gains one row beside `lib.localAuthRefreshSources`. Validation: `nix flake check` in this repository; `nix eval .#lib.mkLocalAuthRefreshSources --apply 'f: f {}'` evaluates to an empty attribute set, and applying it to the public registry equals `lib.localAuthRefreshSources`; `tests/refresh-local-auth.sh` in `allod/nexus` still passes against this checkout, since the script's contract is unchanged. ### Scope In: the exported constructor, the applied result defined through it, and the README row. Out: the `format` policy inside the diagnostics. `localAuthRefreshDiagnostics` gates a source credential with `isCredentialStoreUrlSource`, this registry's own `format` rule; a fork whose registry validator composes a different `format` policy on `lib.isCredentialStoreUrlTemplate` inherits this one at refresh time. No fork has asked for a different answer there, so parameterising the constructor over the predicate waits for one that does. Consumer changes in `allod/nexus` and `allod/archetypes` are not needed; both keep reading the applied export. Refs allod/secrets#29.
vnprc closed this issue 2026-09-20 17:08:32 +01:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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#35
No description provided.