Store multiple Pi bearer tokens and select one per process #36

Closed
opened 2026-08-22 14:41:47 +01:00 by vnprc-agent · 0 comments
Contributor

Let each managed Pi credential retain several encrypted named bearer tokens and let Pi select among them without changing providers, endpoints, or models.

Provider and token semantics

A provider remains an endpoint/API/model catalogue selected through /model. A named token is an opaque bearer accepted by every provider attached to the same logical credential—for example, ordinary and promotional quota for one service. /token never changes provider or model. Selection is per logical credential, so providers sharing that credential switch together while separate credentials remain independent.

Token names are operator-chosen identifiers matching ^[a-z][a-z0-9-]{0,62}$. Generic code must not infer quota, age, priority, or exhaustion from a name.

Startup selection

For each credential, a fresh Pi process resolves:

  1. the remembered local selection, if it still names a delivered token;
  2. the optional deployment default, if valid;
  3. the only delivered token, when exactly one exists;
  4. an interactive picker when several choices remain.

If an invalid remembered name is ignored, interactive Pi reports that it became stale. An unresolved non-interactive process fails with an actionable diagnostic. Adding a token never activates it or changes the deployment default. There is no startup token-selection flag in this scope.

Remembered state is a user-owned mode-0600 XDG state file containing only credential and token names. It is a preference, not a credential registry, and is safe to regenerate.

Live /token interface

Pi exposes only:

  • /token — show status and a picker for the current provider's logical credential;
  • /token <name> — select a provisioned named token;
  • /token cancel — cancel a pending switch.

The command never accepts bearer input. When Pi is idle, selection takes effect immediately and is remembered. While a request or queued work is active, it records a pending switch; the active request and already queued work retain the old token, and activation waits until the agent is settled with no queued messages. The latest pending choice wins. /token cancel removes it.

Activation re-registers every provider sharing the credential through Pi's supported extension provider API. It does not reach into private runtime objects or mutate extension-visible authorization headers. Concurrent Pi processes may select different tokens; changing remembered state does not retroactively alter another process's active selection.

Pi 0.84.2 supports this design: runtime API-key overlays are non-persistent, and supported registerProvider() calls recompose provider authentication immediately. Token-specific command-backed keys avoid Pi's exact-command process cache while retaining the bearer-only stdout pipe.

Lifecycle and storage

Host-side pi-provider owns named-token creation, replacement, default selection, removal, recipient reprovisioning, and listing. Pi only selects names already provisioned on the VM. Each token has its own Age ciphertext and volatile mode-0600 runtime file. Plaintext may cross only hidden lifecycle input, Age stdin, volatile delivery, and Pi's credential stdout pipe; it must not enter repositories, the Nix store, generated JSON, argv, environment variables, diagnostics, or persistent plaintext files.

Removing a provisioned token removes it on targets through the existing rebuild/reconcile flow. A stale remembered name then follows normal startup resolution. There is no separate Pi retirement registry, automatic HTTP-error failover, retry, balance probing, or provider-side revocation.

Existing singleton credentials migrate forward without bearer re-entry: their ciphertext becomes the named token default, and registry metadata declares it as the deployment default. The new implementation does not preserve downgrade compatibility with the legacy layout; rollback after migration is by roll-forward repair or by restoring both old data and old code together.

Validation

Synthetic checks must prove independent add/rotation/removal of named tokens; singleton migration; shared-provider switching; startup precedence and non-interactive refusal; picker and minimal command behavior; immediate and pending live switching; latest-wins/cancel; two concurrent processes pinned to different selections; atomic mode-0600 remembered state; stale selection handling; generated activation/rebuild/reboot/removal behavior; and bearer non-disclosure across repositories, store closures, generated artifacts, argv, environment, diagnostics, and persistent home.

Scope

In scope: the public credential registry contract, Nexus lifecycle commands, libvirt runtime delivery, Pi selection extension and command-backed authentication, forward migration, documentation, deployment composition, and synthetic generated-behavior checks across allod/secrets, allod/nexus, allod/archetypes, and allod/deploy.

Out of scope: real endpoint names, provider/model IDs, tokens, targets, or recipient keys in public history; provider-side issuance/revocation; automatic quota detection or failover; process-only startup overrides; microVM delivery; hiding delivered credentials from agents sharing Pi's Unix account; and private live-provider rollout.

Let each managed Pi credential retain several encrypted named bearer tokens and let Pi select among them without changing providers, endpoints, or models. ### Provider and token semantics A provider remains an endpoint/API/model catalogue selected through `/model`. A named token is an opaque bearer accepted by every provider attached to the same logical credential—for example, ordinary and promotional quota for one service. `/token` never changes provider or model. Selection is per logical credential, so providers sharing that credential switch together while separate credentials remain independent. Token names are operator-chosen identifiers matching `^[a-z][a-z0-9-]{0,62}$`. Generic code must not infer quota, age, priority, or exhaustion from a name. ### Startup selection For each credential, a fresh Pi process resolves: 1. the remembered local selection, if it still names a delivered token; 2. the optional deployment default, if valid; 3. the only delivered token, when exactly one exists; 4. an interactive picker when several choices remain. If an invalid remembered name is ignored, interactive Pi reports that it became stale. An unresolved non-interactive process fails with an actionable diagnostic. Adding a token never activates it or changes the deployment default. There is no startup token-selection flag in this scope. Remembered state is a user-owned mode-`0600` XDG state file containing only credential and token names. It is a preference, not a credential registry, and is safe to regenerate. ### Live `/token` interface Pi exposes only: - `/token` — show status and a picker for the current provider's logical credential; - `/token <name>` — select a provisioned named token; - `/token cancel` — cancel a pending switch. The command never accepts bearer input. When Pi is idle, selection takes effect immediately and is remembered. While a request or queued work is active, it records a pending switch; the active request and already queued work retain the old token, and activation waits until the agent is settled with no queued messages. The latest pending choice wins. `/token cancel` removes it. Activation re-registers every provider sharing the credential through Pi's supported extension provider API. It does not reach into private runtime objects or mutate extension-visible authorization headers. Concurrent Pi processes may select different tokens; changing remembered state does not retroactively alter another process's active selection. Pi 0.84.2 supports this design: runtime API-key overlays are non-persistent, and supported `registerProvider()` calls recompose provider authentication immediately. Token-specific command-backed keys avoid Pi's exact-command process cache while retaining the bearer-only stdout pipe. ### Lifecycle and storage Host-side `pi-provider` owns named-token creation, replacement, default selection, removal, recipient reprovisioning, and listing. Pi only selects names already provisioned on the VM. Each token has its own Age ciphertext and volatile mode-`0600` runtime file. Plaintext may cross only hidden lifecycle input, Age stdin, volatile delivery, and Pi's credential stdout pipe; it must not enter repositories, the Nix store, generated JSON, argv, environment variables, diagnostics, or persistent plaintext files. Removing a provisioned token removes it on targets through the existing rebuild/reconcile flow. A stale remembered name then follows normal startup resolution. There is no separate Pi retirement registry, automatic HTTP-error failover, retry, balance probing, or provider-side revocation. Existing singleton credentials migrate forward without bearer re-entry: their ciphertext becomes the named token `default`, and registry metadata declares it as the deployment default. The new implementation does not preserve downgrade compatibility with the legacy layout; rollback after migration is by roll-forward repair or by restoring both old data and old code together. ### Validation Synthetic checks must prove independent add/rotation/removal of named tokens; singleton migration; shared-provider switching; startup precedence and non-interactive refusal; picker and minimal command behavior; immediate and pending live switching; latest-wins/cancel; two concurrent processes pinned to different selections; atomic mode-`0600` remembered state; stale selection handling; generated activation/rebuild/reboot/removal behavior; and bearer non-disclosure across repositories, store closures, generated artifacts, argv, environment, diagnostics, and persistent home. ### Scope In scope: the public credential registry contract, Nexus lifecycle commands, libvirt runtime delivery, Pi selection extension and command-backed authentication, forward migration, documentation, deployment composition, and synthetic generated-behavior checks across `allod/secrets`, `allod/nexus`, `allod/archetypes`, and `allod/deploy`. Out of scope: real endpoint names, provider/model IDs, tokens, targets, or recipient keys in public history; provider-side issuance/revocation; automatic quota detection or failover; process-only startup overrides; microVM delivery; hiding delivered credentials from agents sharing Pi's Unix account; and private live-provider rollout.
vnprc closed this issue 2026-08-24 02:42:53 +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/strategy#36
No description provided.