Add managed Pi provider reconciliation #44

Merged
vnprc merged 4 commits from agent/pi-provider-lifecycle into master 2026-08-22 04:16:36 +01:00
Member

Every dev VM now reconciles declared custom Pi providers, including retirement to an empty desired set.
Libvirt VMs receive user-owned 0600 Age secrets in volatile storage; microVM credential projections fail closed.
Pi auth stores only a command-backed helper reference, never bearer bytes.
An ownership manifest limits removal to previously managed provider IDs and unchanged managed links.
Unrelated Pi configuration and operator-replaced stale links remain intact.
The empty public catalogs create no Pi credential/provider artifacts on a never-configured VM.
A generated lifecycle check exercises both OpenAI adapters, rotation caching, removal, locking, rollback, and leak boundaries.
If this is wrong, leave the PR unmerged; the public empty state has no deployment migration to undo.

Dependencies

  • Depends on allod/profiles#6 (lib.piProviders).
  • Depends on allod/secrets#13 (credential projections and provider-reference validation).
  • Depends on allod/nexus#26 (reviewed pi-provider operator lifecycle command).
  • Deploy must follow one profiles, secrets, and inventory graph; this PR exports the pure four-edge validator it can sabotage independently.

Risk

R3 High. This changes Home Manager activation, Age projection, auth configuration, removal behavior, and the Nexus operator-tool pin. Review should focus on ownership boundaries, proper-lockfile interoperability, partial-failure restoration, empty desired state, the shared-credential join, and realized cross-repo packages.

Validation

  • ./check.sh — all 25 current machine/check/passive-output steps passed after the master refresh.
  • ./check-selftest.sh — gate negative fixtures passed.
  • nix build .#checks.x86_64-linux.pi-provider-lifecycle --no-link -L
  • nix build .#checks.x86_64-linux.pi-integration .#checks.x86_64-linux.composed-layer-smoke --no-link -L
  • nix build .#nixosConfigurations.allod-dev.config.system.build.toplevel --no-link -L
  • nix build .#nixosConfigurations.nexus.config.system.build.toplevel .#checks.x86_64-linux.composed-layer-smoke --no-link -L
  • Exact lock assertion pins Nexus to 18c64d059688928694e8ff71c25cd17656efd230; the realized nexus-provisioning-scripts has executable bin/pi-provider with add/retarget/rotate/retire/recover help.
  • Pure validateComposedLayerSources tryEval: matching graph succeeds; a changed observed profiles source fails.
  • git diff --check

Refs allod/strategy#34

Every dev VM now reconciles declared custom Pi providers, including retirement to an empty desired set. Libvirt VMs receive user-owned `0600` Age secrets in volatile storage; microVM credential projections fail closed. Pi auth stores only a command-backed helper reference, never bearer bytes. An ownership manifest limits removal to previously managed provider IDs and unchanged managed links. Unrelated Pi configuration and operator-replaced stale links remain intact. The empty public catalogs create no Pi credential/provider artifacts on a never-configured VM. A generated lifecycle check exercises both OpenAI adapters, rotation caching, removal, locking, rollback, and leak boundaries. If this is wrong, leave the PR unmerged; the public empty state has no deployment migration to undo. ## Dependencies - Depends on allod/profiles#6 (`lib.piProviders`). - Depends on allod/secrets#13 (credential projections and provider-reference validation). - Depends on allod/nexus#26 (reviewed `pi-provider` operator lifecycle command). - Deploy must follow one profiles, secrets, and inventory graph; this PR exports the pure four-edge validator it can sabotage independently. ## Risk R3 High. This changes Home Manager activation, Age projection, auth configuration, removal behavior, and the Nexus operator-tool pin. Review should focus on ownership boundaries, proper-lockfile interoperability, partial-failure restoration, empty desired state, the shared-credential join, and realized cross-repo packages. ## Validation - `./check.sh` — all 25 current machine/check/passive-output steps passed after the master refresh. - `./check-selftest.sh` — gate negative fixtures passed. - `nix build .#checks.x86_64-linux.pi-provider-lifecycle --no-link -L` - `nix build .#checks.x86_64-linux.pi-integration .#checks.x86_64-linux.composed-layer-smoke --no-link -L` - `nix build .#nixosConfigurations.allod-dev.config.system.build.toplevel --no-link -L` - `nix build .#nixosConfigurations.nexus.config.system.build.toplevel .#checks.x86_64-linux.composed-layer-smoke --no-link -L` - Exact lock assertion pins Nexus to `18c64d059688928694e8ff71c25cd17656efd230`; the realized `nexus-provisioning-scripts` has executable `bin/pi-provider` with add/retarget/rotate/retire/recover help. - Pure `validateComposedLayerSources` `tryEval`: matching graph succeeds; a changed observed profiles source fails. - `git diff --check` Refs allod/strategy#34
Author
Member

Independent R3 cross-review found one concrete rollback safety defect:

  • modules/pi-provider-reconcile.sh: failure cleanup unconditionally ran rm -rf on every recorded credential-link path. A non-cooperating writer could replace a checked link with a directory before a later activation failure, causing rollback to recursively delete operator data. Rollback must never recursively remove these paths; it should unlink a newly created link only while it still has the exact desired target, restore a preexisting managed link only from absence, and otherwise warn and preserve the concurrent replacement. The lifecycle check also needs a changed/non-link rollback sabotage witness.
Independent R3 cross-review found one concrete rollback safety defect: - `modules/pi-provider-reconcile.sh`: failure cleanup unconditionally ran `rm -rf` on every recorded credential-link path. A non-cooperating writer could replace a checked link with a directory before a later activation failure, causing rollback to recursively delete operator data. Rollback must never recursively remove these paths; it should unlink a newly created link only while it still has the exact desired target, restore a preexisting managed link only from absence, and otherwise warn and preserve the concurrent replacement. The lifecycle check also needs a changed/non-link rollback sabotage witness.
Author
Member

Independent gpt-5.5 / xhigh R3 review found four additional actionable defects:

  • High — source-of-truth override: flake.nix lets production profileData / inventory profile settings override piProviders, piCredentials, piCatalog, and piCredentialMap after the secrets join. Production composition must force the secrets/profile-derived values while retaining direct mkDevVm injection only for generated checks.
  • High — link creation race: modules/pi-provider-reconcile.sh creates a temporary symlink and force-moves it over the managed path after the pre-mutation recheck. A non-cooperating writer can create a file/link in that gap and be overwritten. Initially absent links need an atomic no-clobber create; changed existing paths must fail and be preserved.
  • Medium — shared profiles contract: profiles PR 6 accepts model.name = "", including in its positive fixture, while the pinned Pi schema requires a non-empty model name and rejects the resulting models.json. This belongs in the profiles catalog validator and archetypes must refresh its pin after that dependency fix.
  • Low — inert sabotage: the lifecycle sabotage only proves that jq del removed a key from its own output; it never feeds the damaged artifact to a real expected-state comparator. The witness must require the generated lifecycle assertion to fail.

The reviewer also confirmed the earlier rollback fix’s conditional removal/restoration design and hostile non-link preservation witness. It could not run Nix builds in its read-only sandbox; the owner-side post-fix lifecycle build and full 23-step gate are separate validation evidence.

Independent `gpt-5.5` / `xhigh` R3 review found four additional actionable defects: - **High — source-of-truth override:** `flake.nix` lets production `profileData` / inventory profile settings override `piProviders`, `piCredentials`, `piCatalog`, and `piCredentialMap` after the secrets join. Production composition must force the secrets/profile-derived values while retaining direct `mkDevVm` injection only for generated checks. - **High — link creation race:** `modules/pi-provider-reconcile.sh` creates a temporary symlink and force-moves it over the managed path after the pre-mutation recheck. A non-cooperating writer can create a file/link in that gap and be overwritten. Initially absent links need an atomic no-clobber create; changed existing paths must fail and be preserved. - **Medium — shared profiles contract:** profiles PR 6 accepts `model.name = ""`, including in its positive fixture, while the pinned Pi schema requires a non-empty model name and rejects the resulting `models.json`. This belongs in the profiles catalog validator and archetypes must refresh its pin after that dependency fix. - **Low — inert sabotage:** the lifecycle sabotage only proves that `jq del` removed a key from its own output; it never feeds the damaged artifact to a real expected-state comparator. The witness must require the generated lifecycle assertion to fail. The reviewer also confirmed the earlier rollback fix’s conditional removal/restoration design and hostile non-link preservation witness. It could not run Nix builds in its read-only sandbox; the owner-side post-fix lifecycle build and full 23-step gate are separate validation evidence.
Author
Member

Resolved in follow-up commit f7f37dae04607cc2c736df050b9d6d04ec78547f.

  • Production composition now forces the joined Pi contract after profile/inventory merges, while direct fixture injection remains available only through mkDevVm.
  • Absent credential links use an atomic, no-clobber symlink(2) create; successful and failed activation paths preserve concurrent files, directories, and changed links.
  • Rollback removes only the exact link it created and restores a retired managed link only from absence.
  • The lifecycle sabotage now runs the real reconciler and requires the normal managed-state comparator to reject the lost provider.
  • Profiles PR 6 now rejects empty model names and archetypes pins its fixed head.

Post-fix validation passed: full 23-step ./check.sh, self-test, focused lifecycle/integration/composed builds, the allod-dev toplevel build, and diff-check. A second scoped read-only review of all five repaired areas reported no remaining actionable regressions.

Resolved in follow-up commit `f7f37dae04607cc2c736df050b9d6d04ec78547f`. - Production composition now forces the joined Pi contract after profile/inventory merges, while direct fixture injection remains available only through `mkDevVm`. - Absent credential links use an atomic, no-clobber `symlink(2)` create; successful and failed activation paths preserve concurrent files, directories, and changed links. - Rollback removes only the exact link it created and restores a retired managed link only from absence. - The lifecycle sabotage now runs the real reconciler and requires the normal managed-state comparator to reject the lost provider. - Profiles PR 6 now rejects empty model names and archetypes pins its fixed head. Post-fix validation passed: full 23-step `./check.sh`, self-test, focused lifecycle/integration/composed builds, the `allod-dev` toplevel build, and diff-check. A second scoped read-only review of all five repaired areas reported no remaining actionable regressions.
Author
Member

Deploy PR 4’s independent review found a cross-repo dependency blocker: this branch still pins the Nexus input at stale master a66ea960, so the realized nixosConfigurations.nexus package set does not contain bin/pi-provider even though deploy composes the new public interface.

Cross-link: allod/deploy#4 (comment)

Resolution belongs here: repin only the Nexus input to reviewed PR 26 head dc503fa27374711bb8d0321140e328f48e503a49, then verify the exact lock revision and inspect the realized Nexus package for bin/pi-provider before rerunning the affected and full gates.

Deploy PR 4’s independent review found a cross-repo dependency blocker: this branch still pins the Nexus input at stale master `a66ea960`, so the realized `nixosConfigurations.nexus` package set does not contain `bin/pi-provider` even though deploy composes the new public interface. Cross-link: https://forge.anarch.diy/allod/deploy/pulls/4#issuecomment-5210 Resolution belongs here: repin only the Nexus input to reviewed PR 26 head `dc503fa27374711bb8d0321140e328f48e503a49`, then verify the exact lock revision and inspect the realized Nexus package for `bin/pi-provider` before rerunning the affected and full gates.
Author
Member

Resolved the stale Nexus pin finding in follow-up commit 3811b3e.

  • flake.lock now pins only the Nexus input to reviewed PR 26 head dc503fa27374711bb8d0321140e328f48e503a49; profiles and secrets remain on their reviewed feature heads.
  • The realized Nexus Home Manager package is /nix/store/lhrx16j85z6qv75nyiamvlx0dny686a9-nexus-provisioning-scripts, and executable bin/pi-provider --help exposes add, retarget, rotate, retire, and recover.
  • The Nexus toplevel plus composed-layer focused build passed.
  • The full ./check.sh gate passed all 23 steps after the repin.
  • Exact-pin assertions and git diff --check passed.

PR 44’s dependency and validation sections now name Nexus PR 26 and the realized-package witness.

Resolved the [stale Nexus pin finding](https://forge.anarch.diy/allod/archetypes/pulls/44#issuecomment-5212) in follow-up commit `3811b3e`. - `flake.lock` now pins only the Nexus input to reviewed PR 26 head `dc503fa27374711bb8d0321140e328f48e503a49`; profiles and secrets remain on their reviewed feature heads. - The realized Nexus Home Manager package is `/nix/store/lhrx16j85z6qv75nyiamvlx0dny686a9-nexus-provisioning-scripts`, and executable `bin/pi-provider --help` exposes add, retarget, rotate, retire, and recover. - The Nexus toplevel plus composed-layer focused build passed. - The full `./check.sh` gate passed all 23 steps after the repin. - Exact-pin assertions and `git diff --check` passed. PR 44’s dependency and validation sections now name Nexus PR 26 and the realized-package witness.
Author
Member

The owner requested that this PR be rebased after a batch of changes landed on master. This branch is ten base commits behind. I will rebase it after Nexus PR 26, refresh the exact Nexus feature pin to that rebased head, and rerun the full archetypes gate before deploy is updated.

The owner requested that this PR be rebased after a batch of changes landed on `master`. This branch is ten base commits behind. I will rebase it after Nexus PR 26, refresh the exact Nexus feature pin to that rebased head, and rerun the full archetypes gate before deploy is updated.
Author
Member

The branch is refreshed against current master without rewriting its public history. Merge head: ddfccf0a0b0bffa7294a44dcada76de2911d5cfb; exact Nexus pin: 18c64d059688928694e8ff71c25cd17656efd230.

The only merge conflict was the expected Nexus lock node. It was resolved by retaining the new upstream lock graph and advancing the feature pin. The expanded full gate now passes all 25 steps, check-selftest.sh passes, the Pi lifecycle/integration checks are included, and diff/status checks are clean. The PR validation text has been updated from 23 to 25 steps and to the refreshed Nexus head.

The branch is refreshed against current `master` without rewriting its public history. Merge head: `ddfccf0a0b0bffa7294a44dcada76de2911d5cfb`; exact Nexus pin: `18c64d059688928694e8ff71c25cd17656efd230`. The only merge conflict was the expected Nexus lock node. It was resolved by retaining the new upstream lock graph and advancing the feature pin. The expanded full gate now passes all 25 steps, `check-selftest.sh` passes, the Pi lifecycle/integration checks are included, and diff/status checks are clean. The PR validation text has been updated from 23 to 25 steps and to the refreshed Nexus head.
vnprc approved these changes 2026-08-22 04:16:31 +01:00
vnprc merged commit ddfccf0a0b into master 2026-08-22 04:16:36 +01:00
vnprc deleted branch agent/pi-provider-lifecycle 2026-08-22 04:16:42 +01:00
Sign in to join this conversation.
No description provided.