Confidential riles-fleet Research + Plan only — no changes made

Migrating the Fleet off 1Password onto Infisical

Secrets-management migration plan for Zach Conner’s cortextOS fleet — 10 Claude Code agents on macOS · Prepared 2026-06-19 by Sage
Contents

1. Executive Summary

The fleet’s 1Password access runs through a single no-biometric service account (claude-automation, vault Automation). That service account is now permanently rate-limited (HTTP 429): thousands of retry calls keep resetting the rate-limit window, so it never recovers. The current mitigation — the fleet-cred-get wrapper caching creds in the macOS Keychain and serving stale values when op is capped — is a band-aid over a structural problem.

Infisical (open-source secrets platform, infisical.com) is the James-Goldbach-recommended replacement. It authenticates machines with short-lived machine-identity tokens instead of a long-lived service account, so there is no single service-account object to rate-limit into oblivion. Its infisical run -- <cmd> model injects secrets straight into a child process’s environment — the values never touch stdout, a file on disk, or an agent’s context window.

Bottom line: Infisical is a sound, future-proof move. The migration is feasible file-to-file with zero secret values ever passing through an AI agent’s chat history, by exporting from the 1Password desktop app (which does not use the throttled service-account API) and importing via the Infisical CLI. The one design choice that makes or breaks the cost story: use a single shared fleet machine identity, not one per agent — mirroring today’s single service account — which keeps the fleet inside Infisical’s free tier.

What changes

  • op item get … --revealinfisical secrets get inside the same fleet-cred-get wrapper
  • Long-lived service-account token → short-lived machine-identity token (auto-renewed)
  • Token-B 429 fallover logic → deleted (no longer needed)
  • Keychain caching + stale-serve resilience → kept as-is

What does NOT change

  • The fleet-cred-get get/refresh/warm-all CLI surface every agent calls
  • Keychain-at-rest encryption of cached creds
  • The “serve stale rather than block customer work” resilience guarantee
  • Secrets still never live in plaintext in git or in a flat file

2. What Is Infisical ✓ verified against infisical.com

Infisical self-describes as “the open-source, all-in-one platform for secrets, certificates, and privileged access management.” Confirmed spelling is Infisical (James’s transcript caption “in physical” was an auto-transcription error). Official site: infisical.com.

Core license
Open-source MIT core (self-hostable: PostgreSQL + Redis + Docker, ~30 min setup)
Cloud option
Infisical Cloud — managed, zero-ops, free tier available
Access model
Machine identities with short-lived tokens (Universal Auth, plus Kubernetes/AWS/Azure/GCP/OIDC/SPIFFE)
CLI
infisical run, infisical secrets get/set, infisical login, --watch auto-reload
Auditability
Logs secret access; retention varies by tier (see §3)
Import formats
.env (key=value) and YAML via infisical secrets set --file=. CSV not accepted for import
Export formats
dotenv, dotenv-export, json, yaml, csv

3. Pricing Breakdown ✓ from infisical.com/pricing

TierPriceIdentity capProjects / EnvsAudit retentionKey features
Free $0 5 identities total (humans + machines combined) 3 projects / 3 envs each / 10 integrations Not specified (community) CLI, API, SDKs, Infisical Agent, webhooks, 2FA, all integrations, secret scanning & sharing, community support
Pro $18 / mo per identity Per-identity billing 12 projects / 50 integrations 90 days + secret versioning, point-in-time recovery, RBAC, secret rotation, SAML SSO, IP allowlisting, priority support
Enterprise Custom Custom Custom Custom retention + dedicated infra, SCIM, LDAP, dynamic secrets, approval workflows, KMIP, 99.99% SLA, SOC2 reports
The identity cap is the whole ballgame. An “identity” on the free tier counts both human logins and machine identities, capped at 5 combined. Naively giving each of the 10 agents its own machine identity blows past the cap and forces Pro at ~10 × $18 = ~$180/mo — over the fleet’s entire <$50/mo budget. Self-hosting does not raise this cap; the MIT core still gates advanced features behind a license key. The fix is architectural, not a tier upgrade — see §4 and §8.

4. How It Works for the Fleet

One shared fleet identity, not ten

Today the entire fleet authenticates to 1Password as one service account (claude-automation). All 10 agents run on the same Mac and read from the same Automation vault. The Infisical mapping is one-to-one: create one fleet machine identity (Universal Auth client ID + client secret), grant it read access to the fleet project, and have every agent’s fleet-cred-get wrapper authenticate as that single identity. That is 1 machine identity + 1 human (Zach) = 2 of the 5 free-tier slots — comfortably free.

How a secret reaches an agent without entering its context

Two access patterns, both stdout-safe:

# Pattern A — inject into a child process (secrets never printed):
infisical run --projectId=<id> --env=prod -- ./some-fleet-script.sh
#   secrets exist ONLY as env vars inside the child; nothing echoed to terminal

# Pattern B — single value into the existing Keychain-cached wrapper:
infisical secrets get CLOUDFLARE_API_TOKEN --plain --projectId=<id> --env=prod
#   replaces `op item get ... --reveal` inside fleet-cred-get's _op_pull()

The machine-identity token is obtained once per session and held in an env var:

export INFISICAL_TOKEN=$(infisical login --method=universal-auth \
  --client-id=<id> --client-secret=<secret> --silent --plain)
Token TTL (default)
7200 s (2 hours), configurable
Renewal
Renewable up to a Max TTL; periodic tokens renew indefinitely without re-issuing the client secret — ideal for a 24/7 fleet
Secret “zero”
Only the client secret is long-lived; store it in the launchd-seeded secure env (same slot pattern as today’s OP_SERVICE_ACCOUNT_TOKEN via op-token-ensure)

5. Why It Solves the 1Password Rate-Limit Problem

Dimension1Password service account (now)Infisical machine identity
Auth objectOne long-lived service-account token, globally rate-limitedShort-lived token minted from a client secret; the identity itself isn’t a throttle target the same way
Failure mode under loadRetry storms reset the 429 window → permanent lockoutReads are normal API calls against your own project; no shared service-account quota choke point
RecoveryStuck until call volume drops for a full window (didn’t happen)Token renew/re-mint is cheap and local; no global cooldown to wait out
Secret-on-diskAvoided via Keychain cache (our wrapper)Native: infisical run never writes secrets to disk
AuditLimitedSecret access logged (retention by tier)
Keep the fleet-cred-get Keychain cache even after migrating. It’s defense-in-depth: if Infisical Cloud has an outage, the fleet still serves recently-cached creds instead of blocking customer-facing work — exactly the resilience property it provides today.

6. Migration Plan

Every step below is designed so no secret value ever passes through an AI agent’s chat/context. The migration is file-to-file: export → transform → import, with secret values redacted in all logging and never echoed to a stdout an agent reads.

1

Export both 1Password vaults — via the DESKTOP app, not the API

Key fact (verified): 1Password’s desktop export (File > Export) is a local operation gated by the account password. It does not use the rate-limited service-account API — so it works even while the service account is throttled. Zach (human, at the GUI) runs this for both the Codex/personal vault and the Automation vault.

Choose .1pux, not CSV. Correction to a common assumption: .1pux is not encrypted — it is a plaintext JSON-in-ZIP archive. But it is the right choice because CSV silently drops everything that isn’t a Login/Password item (API keys stored as custom fields, secure notes, OTP secrets, multi-field items like the fleet’s VE Stats Supabase with URL + Service Role Key + DB Password). .1pux preserves all item types, all custom fields, and vault structure. Since .1pux is plaintext, treat it like a live secret from the moment it lands (§7).

Output: codex-vault.1pux and automation-vault.1pux written to a tmpfs / RAM-disk path if possible, otherwise a chmod 700 directory that is shredded at the end (§9).

2

Transform export → Infisical import format, file-to-file, secrets never on stdout

A converter script reads the .1pux ZIP, walks export.data JSON, and emits one .env file per logical environment/project (e.g. fleet-prod.env, codex.env). Infisical bulk import accepts .env (KEY=value) and YAML — not CSV — so .env is the target format.

Approach (not final code — this is the design + the safeguards that matter):

  • Pure file-to-file: read .1pux → write .env. Secret values move buffer-to-buffer in the script’s memory and into the output file. They are never print/echo/console.log-ed.
  • Redacted logging only: the script logs keys and counts (“wrote 24 secrets to fleet-prod.env”), never values. A sentinel test (a fake secret) verifies the redaction path BEFORE real data flows — per the fleet’s “redact secrets before output” rule.
  • Key normalization: map 1Password item::field pairs to Infisical key names. The fleet’s 24 warm-all entries (Cloudflare::API Token, VE Stats Supabase::Service Role Key, …) get a deterministic naming convention so the wrapper can still look them up.
  • An agent orchestrates the script but never sees its data: the agent (or Zach) invokes the converter; the converter’s stdout is keys/counts only. Output .env files are written with 0600 perms to the shredded directory.
  • No agent reads the .env: the very next step (import) consumes it; no cat/Read of the file by any agent at any point.

Keep a separate, committed-to-repo key manifest (key names only, zero values) so the mapping is reviewable and reproducible without exposing secrets.

3

Load secrets into Infisical — CLI bulk import, file-to-file

Authenticate the human (Zach) interactively once for the import, then bulk-import each .env straight from file:

infisical login                                  # Zach, interactive, one time
infisical secrets set --file="./fleet-prod.env" --env=prod --projectId=<id>
infisical secrets set --file="./codex.env"      --env=prod --projectId=<codexId>

infisical secrets set --file= reads the file directly — values go file → API over TLS and are never echoed. Note: infisical secrets import is a different command (it links secrets between Infisical paths, not from a file) — do not confuse the two. There is no official 1Password→Infisical tool, so the Step-2 converter is custom work the fleet owns.

4

Rewrite the fleet’s credential wrapper to read from Infisical

The fleet reads every credential through one script: orgs/riles-fleet/shared/scripts/fleet-cred-get (435 lines; ~18 files / 10 agents call it or call op via skills). The rewrite is surgical — the public get / refresh / warm-all / clear / list interface stays identical so no caller changes:

  • Replace _op_pull() (line 176, op item get "$item" --vault "$vault" --fields "$field" --reveal) with infisical secrets get "$key" --plain --projectId="$pid" --env="$env".
  • Delete the token-B 429 fallover machinery (_op_pull_with_fallover, OP_RATE_LIMIT_RC, OP_SERVICE_ACCOUNT_TOKEN_B) — there is no service-account rate limit to fall over from.
  • Keep the macOS Keychain cache, the 24-h TTL, and the stale-serve-on-outage logic verbatim — it now guards against Infisical Cloud outages instead of 429s.
  • Swap token seeding: op-token-ensure (seeds OP_SERVICE_ACCOUNT_TOKEN) → an infisical-token-ensure that mints/renews INFISICAL_TOKEN from the launchd-held client secret (same secure-env pattern, no GUI popups).
  • Update the warm-all manifest: the 24 item::field::vault tuples become Infisical key names; warm-all pre-fetches them into the Keychain exactly as today.
  • Update skill references: the ~18 skills/scripts that call op item get directly (ttc-wave-send, gws-auth-recovery, monarch-puller, sked-api, etc.) route through fleet-cred-get or the new infisical call. Prefer routing through the wrapper so caching/resilience is centralized.
5

Verify, then decommission 1Password after a backup window

  • Parity check: for each of the 24 warm-all creds, confirm fleet-cred-get get returns a working value from Infisical (validate by using it — e.g. a Cloudflare API ping — never by printing it).
  • Run both in parallel for N days (recommend 14 days): keep 1Password as a read-only backup. Do NOT delete vaults yet.
  • Watch the fallover log and agent error rates; confirm zero op calls remain in live paths (grep for op item get across agent scripts — should be migrated or dead).
  • Then follow the Decommission Checklist (§9).

7. Security Safeguards

Where a secret could leakHow the plan prevents it
The .1pux export file (plaintext JSON-in-ZIP)Written to a RAM-disk / chmod 700 dir; treated as live secret; rm -P/shred immediately after import (§9). Never cat/Read by an agent.
Converter script printing values to stdoutScript logs keys + counts only; a sentinel-secret test verifies the redaction path before real data flows.
Generated .env files0600 perms, written to the shredded dir, consumed immediately by import, never read by an agent.
Import command echoing secretsinfisical secrets set --file= reads file directly → TLS; nothing echoed.
Runtime: secrets in an agent’s contextinfisical run injects into the child process env only; infisical secrets get --plain output is captured by the wrapper into Keychain, never surfaced to the agent transcript.
Secret “zero” (the client secret)Held in launchd-seeded secure env (same model as today’s OP token); short-lived INFISICAL_TOKEN minted from it; consider a periodic token to avoid re-handling the client secret.
GitOnly a key manifest (names, no values) is committed. .env and .1pux are never in the repo; add to .gitignore defensively.
Single most important safeguard: the .1pux export is plaintext and contains every secret from both vaults at once. From the instant it is created until it is securely shredded, it is the highest-value target in the entire migration. Keep it on a RAM-disk or chmod 700 path, never let any AI agent cat/Read it, and shred/rm -P it the moment import verifies — before anything else.

8. Recommendation

Infisical Cloud, Free tier, with a single shared fleet machine identity.
Verify before committing money: Free-tier secret-count and per-project limits weren’t explicitly published on the pricing page at research time. Before import, confirm the fleet’s total secret count fits the free tier (the 24 warm-all creds plus the long tail across both vaults). If it doesn’t, that — not identity count — becomes the gating factor.

9. Decommission Checklist

  1. Day 0: Both vaults exported to .1pux via desktop app; converter run; secrets imported to Infisical; key manifest committed (names only).
  2. Day 0: shred -u / rm -P the .1pux exports and all generated .env files. Confirm the shredded directory is empty.
  3. Day 0: fleet-cred-get rewritten + deployed; infisical-token-ensure seeding the client secret in launchd env; warm-all succeeds against Infisical.
  4. Day 0–14: Parallel run. 1Password left intact as read-only backup. Monitor agent error rates and the fleet-cred fallover log.
  5. Day 0–14: grep -r "op item get" across all agent scripts/skills → every hit migrated or confirmed dead. Zero live op calls remain.
  6. ~Day 14: Parity confirmed for all 24 warm-all creds (validated by use, not by printing).
  7. ~Day 14: Revoke/disable the claude-automation service account in 1Password. Remove OP_SERVICE_ACCOUNT_TOKEN / token-B from launchd env and the ~/.secrets keychain entries.
  8. ~Day 14: Remove op-token-ensure sourcing and any residual op wrappers; delete dead 1Password skills (op-daemon-socket-reset, op-fresh-inode-workaround).
  9. ~Day 21 (only after a clean week post-cutover): Delete the 1Password Automation vault contents (or archive the whole vault). Keep Zach’s personal/Codex vault if he still uses 1Password personally; only remove the fleet-relevant items.
  10. ☐ Update ~/.claude/CLAUDE.md & fleet knowledge.md: secrets-management section now points to Infisical, not 1Password.