Plan authenticated Pi provider discovery #41

Merged
vnprc merged 9 commits from agent/pi-provider-discovery-plan into master 2026-08-25 02:41:58 +01:00
Member

Summary

  • define secure authenticated /models discovery and refresh contracts
  • pin translation, double-snapshot removal, transaction, and rollback behavior
  • require synthetic black-box bearer, HTTP, interruption, and recovery witnesses
  • record independent implementation review and different-model scoped verification

Validation

  • two-pass R3 plan review (claude-opus-5, gpt-5.6-terra)
  • implementation review (claude-opus-4-8): one diagnostic truncation gap found and fixed in Nexus
  • scoped fix verification (gpt-5.6-terra): no remaining findings
  • clean strategy worktree

Refs allod/nexus#32

## Summary - define secure authenticated `/models` discovery and refresh contracts - pin translation, double-snapshot removal, transaction, and rollback behavior - require synthetic black-box bearer, HTTP, interruption, and recovery witnesses - record independent implementation review and different-model scoped verification ## Validation - two-pass R3 plan review (`claude-opus-5`, `gpt-5.6-terra`) - implementation review (`claude-opus-4-8`): one diagnostic truncation gap found and fixed in Nexus - scoped fix verification (`gpt-5.6-terra`): no remaining findings - clean strategy worktree Refs allod/nexus#32
Refs allod/nexus#32

Model: gpt-5.6-sol
Bearer resolution assumed an Age identity the command never had; pin it to the
hypervisor identity rotate-token already uses and keep plaintext out of files.
Discovery also reads a bearer for an existing credential, so the operator sees
the provider, credential, resolved targets, and request URL first.
Manual add accepts any https URL, so refresh could send a decrypted bearer to a
URL carrying userinfo, a query, or another path; refresh now applies the
discovery URL rule before it reads a bearer. The size bound is on bytes read,
because a Content-Length check is no bound at all on a chunked response.
Only removals were confirmed, so refreshing a manually added provider would
silently replace operator-written model JSON with allowlisted fields. Refresh
now refuses that provider instead of destroying data it cannot regenerate.
Four speculative endpoint-flag spellings, a chat-wins precedence rule, and an
all-models aggregate bought nothing Pi cannot express per model, and refresh
would have clobbered an explicit --api with a discovered one. Model-level
api: openai-responses overrides the provider value, so the provider-level
adapter stays an add-time operator choice.
Findings new to this pass: 0 BLOCKER, 5 GAP, 1 SIMPLIFY. All six are
original-plan defects; this is the first pass, so nothing was review-introduced.

1. [GAP] Decryption identity unnamed. Bearer resolution assumed an Age identity
   pi-provider never had, and never said which one; pinned to AGE_IDENTITY,
   default $HOME/.ssh/host, as rotate-token already uses, with plaintext held in
   a variable rather than a file. Fixed in 0e075f6.
2. [GAP] Bearer requested before blast radius shown. discover-add defaults to
   every libvirt dev VM, so the operator would paste a token before seeing the
   targets it gets encrypted to; both commands now print provider, credential,
   resolved targets, and request URL before any prompt or decryption, and the
   "an existing credential prompts for nothing" promise is narrowed to manual
   add. Fixed in 0e075f6.
3. [GAP] Stored base URL never revalidated. Manual add is open-world and the
   registry only checks the https:// prefix, so refresh could send a decrypted
   bearer to a stored URL carrying userinfo, a query, or another path; refresh
   now applies the discovery URL rule and refuses before reading a bearer.
   Fixed in a648e1d.
4. [GAP] Size bound was not a bound. A 1 MiB limit checked against an advertised
   Content-Length does nothing to a chunked response; the bound is now on bytes
   actually received. Fixed in a648e1d.
5. [GAP] Refresh could destroy hand-authored model JSON. Only removals were
   confirmed, so refreshing a manually added provider would silently replace
   operator-written fields with allowlisted ones; refresh now refuses a provider
   whose stored models carry non-allowlisted fields. Fixed in 194284a.
6. [SIMPLIFY] Discovered provider-level adapter flip deleted. Four speculative
   endpoint-flag spellings, a chat-wins precedence rule, and an all-models
   aggregate bought nothing Pi cannot express per model, and refresh would have
   clobbered an explicit --api; model-level api: openai-responses now carries
   the case and the provider-level adapter stays an add-time operator choice.
   Fixed in 68898ef.

SIMPLIFY sweep considered: the whole translation allowlist (the limit and
capability spellings are ungrounded guesses, but each is one jq branch and a
wrong guess degrades to Pi's own default, so only the endpoint matrix was cut);
the second removal snapshot (kept, it is the issue's stated safety requirement);
the --prompt-token flag (kept, it is the only escape from a stale default
token); and the dry-run network read (kept, the issue requires it).

Tracking issue: allod/nexus#32

Model: claude-opus-5
Require a validated single bearer resolution, suppress xtrace while it exists, and make curl ignore user configuration and proxies so its authenticated request remains confined to the fixed endpoint. Pin removal confirmation after successful tmpfs preflight and recheck clean repositories before the second snapshot and journal.

The production implementation must witness these contracts with its synthetic client, Age, and transaction fixtures.
Findings new to this terminal pass: 0 BLOCKER, 3 GAP, 0 SIMPLIFY. All three were original-plan gaps surfaced by the full review of the first-pass range.

1. [GAP] curl configuration and proxy environment could escape the fixed bearer-bearing request; fixed in 694104f.
2. [GAP] decrypted defaults lacked validated one-resolution and xtrace-safe handling; fixed in 694104f.
3. [GAP] removal confirmation lacked a pre-journal cleanliness sequence; fixed in 694104f.

SIMPLIFY sweep retained each added requirement because it closes the authenticated client or two-repository source-loss boundary. No third prose review: the remaining work is the named Nexus implementation witnesses.

Tracking issue: allod/nexus#32

Model: gpt-5.6-terra
vnprc force-pushed agent/pi-provider-discovery-plan from 0bd5c23c40 to 5a598abe66 2026-08-25 02:37:53 +01:00 Compare
vnprc approved these changes 2026-08-25 02:41:52 +01:00
vnprc merged commit 5a598abe66 into master 2026-08-25 02:41:58 +01:00
vnprc deleted branch agent/pi-provider-discovery-plan 2026-08-25 02:41:58 +01:00
Sign in to join this conversation.
No description provided.