Skip to content

Credentials

Note on runtime references (2026-05-20): Some sections below reference OpenClaw config paths (e.g., .openclaw/...) and Pi-E token-sync mechanics that pre-date the 2026-05-20 OpenClaw retirement. The credential entries themselves (Bitwarden names, admin accounts, rotation cadence) remain accurate; OpenClaw paths should be read as historical artifacts — current agents Pi-E and Volt-E (and iBuild-E on launchd) run on agent-runner and use the token paths documented in agent-token-sync.md; Review-E now runs in the GCP k3s cluster (rig-agent-runtime) with a cluster-managed credential.

Credential inventory with Bitwarden entry names. NEVER store actual secrets in this file.

Admin Accounts

Account Purpose
invotekas@gmail.com Invotek admin — Cloudflare login, Tailscale login, Cloudflare Access
codiedev42@gmail.com Dev/project — Bitwarden, Xcode Cloud notifications, Google Drive
post@stigjohnny.no Personal. Was the vCluster Platform login, until Loft was removed from the Dell on 2026-10-02

Bitwarden Configuration

Setting Value
Server vault.bitwarden.eu (EU)
Email codiedev42@gmail.com
Unlock See memory MCP (get_context(project: "global", key: "bitwarden_unlock"))

Credential Inventory

GitHub

Bitwarden Entry Purpose Used By Expiry
GitHub PAT (Claude) Legacy PAT — EXPIRED Was used by Docker runners Expired
GitHub OAuth Token OAuth token for gh CLI Docker runners (.env as GITHUB_TOKEN) —
RELEASE_PAT Review-E PAT for release PR auto-approval dashecorp dashe-* + dashe-cutie (via OpenTofu in dashecorp/infra) 2026-05-30 (EXPIRED — rotation pending, #23)
— (rig-gitops credential-ops environment secret GHCR_READ_TOKEN, plus environment variable GHCR_READER_USER, the operator's login) GHCR read PAT: the operator's own classic PAT, read:packages only (rig-gitops#896 D1), because no machine user exists. Set 2026-10-06 Only C2 (c2-ghcr-pull-secrets.yml, which encrypts it into the GHCR pull secrets), and C8, which rotates it by re-running C2 (CI/CD) No expiry
— (rig-gitops credential-ops environment secret DELL_TUNNEL_TOKEN; none at repo level) The Dell tunnel dashecorp-dell-v2's connector token. Written by OpenTofu (infra#658, from cloudflare/dashecorp.com state, no value printed). Set 2026-10-15 Only C4 (c4-c5-cloudflare-secrets.yml), after the operator approves each run; it encrypts it into apps/cloudflared/dell/tunnel-token.sops.yaml (CI/CD) —
— (rig-gitops credential-ops environment secrets DELL_TENANT_MAP_CLIENT_ID, DELL_TENANT_MAP_CLIENT_SECRET; none at repo level) The Cloudflare Access service token the Dell's conductor uses to read the tenant map. Written by OpenTofu (infra#658, from cloudflare/dashecorp.com state). Set 2026-10-15. The secret may be 64 hex or, for tokens made on or after 2026-08-26, cfast_[40 alphanumeric][8-character checksum] Only C5 (c4-c5-cloudflare-secrets.yml), after the operator approves each run 1 year by default (Access service-token default, infra#660)
— (rig-gitops credential-ops environment secrets GCP_TENANT_MAP_CLIENT_ID, GCP_TENANT_MAP_CLIENT_SECRET; none at repo level) GCP's own Cloudflare Access service token for its conductor's tenant-map reads. Written by OpenTofu (infra#668, second apply 18:14:43Z). Set 2026-10-15 Only the c5-gcp step (c4-c5-cloudflare-secrets.yml, rig-gitops#1025; first run 38784866311), which fenced GCP's conductor (rig-gitops#1027) 1 year by default (infra#660)
— (rig-gitops credential-ops environment secret GCP_TUNNEL_TOKEN; none at repo level) The connector token of GCP's rotated tunnel dashecorp-gcp-v2, whose secret OpenTofu never prints (random_bytes). It replaced dashecorp-gcp, whose secret old CI logs printed (infra#654), and which was deleted on 2026-10-16 (infra#684). Written by OpenTofu (infra#678; created by the second run, 2026-10-15 23:27:55Z) Only the c4-gcp step (rig-gitops#1034), after the operator approves each run. It encrypts the token into apps/cloudflared/gcp/tunnel-token-v2.sops.yaml; first run #16, 2026-10-16. Since rig-gitops#1039 the step refuses the deleted tunnel's fixed ID and requires main's apps/cloudflared/gcp/tunnel-id Rotated by replacing random_bytes.tunnel_secret_gcp_v2 (infra docs/2026-10-15-gcp-tunnel-rotation.md)
— (rig-cockpit-worker secret TENANT_MAP_READERS) The worker's tenant-map reader set: [dell, gcp] since infra#668 (2026-10-15). Under infra#658 it was [] when var.dell_tenant_map_reader_enabled is false. Written by OpenTofu (infra#658, from cloudflare/dashecorp.com state). Set 2026-10-15 rig-cockpit-worker deploy.yml, which puts it on the Worker (first: run 38718557371; [dell, gcp] live 18:17:52Z) Each reader's Access token: 1 year by default (infra#660)
— (rig-cockpit-worker secret ROUTER_WEBHOOK_SECRET) The webhook router's HMAC secret: the same shared value as infra's WEBHOOK_SECRET and the conductor's GITHUB_WEBHOOK_SECRET. Written by OpenTofu (infra#664, from var.webhook_secret; GitHub refuses Actions secret names that start with GITHUB_). Set 2026-10-15 rig-cockpit-worker deploy.yml, which puts it on the Worker as GITHUB_WEBHOOK_SECRET on every deploy (rig-cockpit-worker#302) Rotates with WEBHOOK_SECRET: after the apply, deploy the worker, or the router (once live) refuses deliveries with 401
— (rig-cockpit-worker repo secret INBOX_COMMENT_TOKEN) Fine-grained token "Inbox comments": dashecorp/operator-inbox only, Issues read and write. Lets the Worker post a Done as an issue comment (Operator inbox). Set 2026-10-06 rig-cockpit-worker 2027-10-07: the dashecorp enterprise caps fine-grained tokens at 366 days. Renewal is tracked in the deadline tracker (#45)
— (rig-cockpit-worker repo secret INFRA_DISPATCH_TOKEN) Fine-grained token "rig-cockpit INFRA_DISPATCH_TOKEN": dashecorp/infra only, Actions read and write (plus the mandatory Metadata read). Lets the Worker dispatch infra's rig-location.yml with gcp=running, the GCP automatic start (rig-gitops#1021 step 7). It has no Deployments permission, so it cannot approve rig-standby: it can never stop GCP or point the stable host at the Dell. Made by the operator, 2026-10-16 08:47:37Z rig-cockpit-worker. Unused until rig-cockpit-worker#315 adds it to deploy.yml's synced secrets 2027-10-17 (the 366-day enterprise cap). Renewal is tracked in the deadline tracker (#47)
codi-e-bot App PEM (ORCHESTRATOR_BOT_PEM) Read-only GitHub App (ID 4127706, @dashecorp) for orchestrator cross-org reads; replaced the expired GH_PAT claude-3 monitor-infra.yml + build-unified-docs.yml (mint per-org tokens via actions/create-github-app-token) App key (no expiry) — must be PKCS#8; re-set as PKCS#8 2026-09-30 (#765)

GitHub App PEMs: Stored locally on each agent host, not in Bitwarden. See .claude/skills/github-apps/SKILL.md for paths. Exception: the codi-e-bot App PEM is also held as the ORCHESTRATOR_BOT_PEM repo secret on Stig-Johnny/claude-3 (PKCS#8), and its source copy is in the password field of the Bitwarden Login item codi-e-bot (PKCS#1; the item has no notes or attachments). actions/create-github-app-token requires PKCS#8, so convert before upload — runbook docs/runbooks/ops-e-reset-orchestrator-bot-pem.md.

The GHCR read PAT is single-purpose. Reuse an existing PAT only if its scope is exactly read:packages. The legacy agent PATs are never used for it.

Credential automation

The direction the operator approved in claude-3#1027: credentials stop passing through agents and chat. - From state to SOPS: secrets flow from OpenTofu state to SOPS through CI. #897 P2 (GitHub Actions secrets from remote state) is the next step. - Phone-approved workflows: pre-reviewed workflows behind the credential-ops environment, approved by the operator in GitHub Mobile (CI/CD). - GitHub Mobile shows no Approve for runs triggered under the operator's own login (no notification is sent), so the operator approves those in Safari (run 38784866311). - The approve-rule (claude-3#1191, merged 2026-10-15, CLAUDE.md): the orchestrator may approve a waiting rig-gitops credential-ops run (C2, C4/C5) only if it dispatched the run itself from main, and the operator replied with a literal yes naming that step after being told the run number, step and target branch. It approves through POST /repos/dashecorp/rig-gitops/actions/runs/<id>/pending_deployments with a comment quoting the yes and its timestamp. Never a rejection, never another environment. - Later: approvals as Rig Cockpit push notifications, and phase 2, a Cockpit credential-intake page (operator decision pending). - Asking the operator: credential steps are filed in dashecorp/operator-inbox, never asking for the secret itself (Operator inbox).

Rig Cockpit approvals and push

  • The approvals webhook secret signs both Cockpit GitHub webhooks (/v1/github/waiting and /v1/github/inbox, see Networking). It flows OpenTofu random_password → rig-cockpit-worker Actions secret APPROVALS_WEBHOOK_SECRET → deploy.yml → the Worker, with no manual step (dashecorp/infra#582, #585).
  • APNs, not set yet: the Worker needs four rig-cockpit-worker repo secrets, requested in operator-inbox #5: APNS_KEY_ID, APNS_TEAM_ID, APNS_AUTH_KEY (the full .p8) and APNS_BUNDLE_ID (no.invotek.rig-cockpit). Once they exist, check the names with gh secret list --repo dashecorp/rig-cockpit-worker, run deploy.yml, and have the operator tap Send Test Push in More → Settings. Until then the app says "Push is not configured", and pushes fall back to Discord #admin.

Apple / App Store Connect

Bitwarden Entry Purpose Used By
App Store Connect API Key ASC API (issuer ID, key ID, private key) appstoreconnect-mcp
Apple ID (u7232055051@gmail.com) ASC portal login, Xcode signing Browser automation

Cloud Services

Bitwarden Entry Purpose Used By
Cloudflare API Token Cloudflare API access cloudflare MCP, GitHub Actions
OpenAI API Key (Claude) DALL-E image generation, Nutri-E workers Blog images, Nutri-E
Google Service Account Key Play Store deployment dashe-reward-e Android

Cloudflare account facts (non-secret): account ID 59710bf016d417f860051f1f00b00258 (dashboard identifier). The local wrangler OAuth token lives at ~/.config/.wrangler/config/default.toml on the operator machine (not in Bitwarden); the Cloudflare API Token above is what the cloudflare MCP + CI use.

Messaging / Notifications

Bitwarden Entry Purpose Used By
Discord Bot Token (Codi-E) Codi-E Discord bot discord MCP
Discord Bot Token (Pi-E) Pi-E Discord bot Pi-E agent-runner
Discord Bot Token (Volt-E) Volt-E Discord bot Volt-E agent-runner
Discord Bot Token (Review-E) Review-E Discord bot Review-E agent-runner
~~Discord Bot Token (ATL-E)~~ ~~ATL-E Agent bot~~ — retired 2026-05-20 (ATL-E replaced by rig-conductor + rig-planner) —
Telegram Bot Token Telegram notifications (legacy) telegram-notifications-mcp
Pushbullet API Key SMS forwarding pushbullet-sms-mcp

Revenue / Subscriptions

Bitwarden Entry Purpose Used By
RevenueCat API Key Subscription management revenuecat MCP

GCP

Resource Value
Infra Project invotek-github-infra
Service Account github-actions@invotek-github-infra.iam.gserviceaccount.com
Auth Workload Identity Federation (keyless, no secret needed)
OpenTofu state encryption Since 2026-09-30, dashecorp/infra state in gs://tablez-terraform-state is encrypted client-side (AES-GCM) with the KMS key tofu-state (europe-north1), so a bucket read alone reveals no secrets. The CI SA has cryptoKeyEncrypterDecrypter on the key and project roles/cloudkms.admin (infra BOOTSTRAP step 22). All ten root modules are encrypted since 2026-10-01 (dashecorp/infra#492 closed). Nine reject plaintext state. k8s/dell/k3s was encrypted from its first apply by GitHub Actions (#503, no fallback), and the two never-applied mac-mini/* modules were retired (#512). The bucket's old plaintext versions (1,069 from before encryption) and four orphaned plaintext prefixes were purged on 2026-10-02 by OpenTofu lifecycle rules (infra#537, #521). The bucket is now google_storage_bucket.tofu_state in gcp/dashecorp, with prevent_destroy, and it keeps 30 days of stale (encrypted) versions. Check a module by printing only the state object's top-level keys: encrypted state shows encrypted_data, encryption_version, lineage, meta and serial
Dell CI SSH key ED25519 key generated by OpenTofu (tls_private_key.hosts_ci, github/dashecorp). The private half lives in KMS-encrypted state and the dashecorp/infra hosts environment secret HOSTS_SSH_PRIVATE_KEY, usable from main only. The public half is the repo variable HOSTS_SSH_PUBLIC_KEY. It is not in Bitwarden, and no person handles it. Only claude on the Dell trusts it, from tailnet addresses (infra BOOTSTRAP §23). To rotate: rename the resource in a PR, then swap the Dell's line
GCP default Compute account (1090472687120-compute@, the rig VMs) No project Editor since 2026-10-02. Its project roles are exactly roles/compute.storageAdmin plus the custom role gcePdCsiAttach, and it can act as itself (BOOTSTRAP §24). gcp/dashecorp/node-sa.tf fails the plan if those roles drift or anyone gets Editor (infra#539). Agent namespaces cannot reach the metadata server (rig-gitops#887)
Billing Account 015BBC-422A59-EB7AF4, which also pays for cuti-e, claude-memory-mcp, reward-e-app and dev-entropy-477313-v5
Human owner stig@dashecorp.com since 2026-09-29, also of claude-memory-mcp, cuti-e, nutri-e-app, reward-e-play-store, reward-e-app-dev and dev-entropy-477313-v5. post@nutrieapp.com owned them until then; its domain was to lapse on 2026-10-05 (claude-3#975), but Domeneshop auto-renewed it on 2026-10-06 until 2027-10-05; the operator is asking for the renewal to be cancelled and refunded (deadline #4)
Billing administrators codiedev42@gmail.com and stig@dashecorp.com. post@nutrieapp.com stays until it is retired by 2026-11-04 (deadline #36)
Dashecorp billing Dashecorp Billing 0151A6-EDAFD0-29E42A, managed by stig@dashecorp.com. No project is on it yet
Other accounts codiedev42@gmail.com owns reward-e-app. invotekas@gmail.com has no access to the rig's project or its billing account (claude-3#901)

Rig (k3s) — SOPS-managed secrets (dashecorp/rig-gitops)

Names, keys and pairings only — the values live encrypted in git and decrypted only inside the cluster.

Secret (namespace/name) Keys Consumer / notes
rig-conductor/rig-conductor-secrets 10 pre-existing keys + runpod-api-key (added rig-gitops#727, re-provisioned with the real rpa_ key in rig-gitops#731, both 2026-09-08) rig-conductor. runpod-api-key → RUNPOD_API_KEY (optional secretKeyRef: the conductor boots without it and the RunPod idle monitor simply exits). RunPod key = Restricted: api.runpod.io/graphql Read/Write, api.runpod.ai None; drives the conductor's volume-first wake / stop / idle auto-stop of the operator's vLLM pod (rig-conductor#2008).
dashecorp/rig-e-secrets chat-api-token, openai-api-key, anthropic-api-key, database-url Rig-E. chat-api-token must be byte-identical to the Cloudflare Worker secret below — the pair IS the auth gate; a mismatch is a 401 that the cockpit SPA renders as a logout. openai-api-key = the vLLM key of the RunPod pod (rig-gitops#723). anthropic-api-key is misnamed: it holds the Claude subscription OAuth token (CLAUDE_CODE_OAUTH_TOKEN), not an API key — rename tracked in rig-agent-runtime#664.
dashecorp/{review-e,dev-e,planner}-secrets, dashecorp/ar-pull-secret tenant copies of the base fleet secrets + Artifact Registry pull cred dashecorp tenant fleet (rig-gitops apps/dashecorp/kustomization.yaml header documents each).
CF Worker rig-cockpit-worker → AGENT_CHAT_TOKEN_RIG_E (Worker secret) Set with wrangler secret put AGENT_CHAT_TOKEN_RIG_E --name rig-cockpit-worker in the CF account 59710bf0…. Not in deploy.yml's sync list, so it survives deploys but is not reproducible from git. AGENT_CHAT_ROUTES next to it is a plain [vars] entry in wrangler.toml, not a secret.

Finding (2026-10-03): the conductor's GitHub token is the operator's PAT. rig-conductor-secrets/github-token holds the operator's PAT, so MergeGate merges as the operator. It should be narrowed. That is operator work, and it isn't filed yet: it needs prepared steps first.

Encryption facts (non-secret): age recipient (public) age1n06gnyjdll2hpl0t2ufupww2v0denz73cz02vpg0k3qcgxhhfflsyu8dlq; the cluster decrypts with flux-system/sops-age; Flux Kustomization dashecorp-bootstrap (10 min interval). The rig age private key is not on the operator Mac and is not needed: to add a key to an existing secret, rebuild it — read the current values from the live cluster secret (kubectl get secret … -o json over gcloud compute ssh invotek-k3s --account stig@dashecorp.com; .data is base64), add the new key from a hidden prompt, re-encrypt with the public recipient. Gate before staging: ENC[ count = 5 structural + N values + 1 MAC (17 for the 11-key conductor secret), key count inside the stringData: block only, no plaintext. main is linear with no force-push, so a plaintext commit there is unscrubbable — the gate is the last line.

Operator-held, not in Bitwarden: the vLLM key and the RunPod API key were minted by the operator and are kept in his personal manager (Enpass), not Bitwarden — the rig docs/CLAUDE.md still say Bitwarden; reconcile when the RunPod account gets MFA (it has none today).

Agent Authentication Methods

All agents share ONE Claude Max subscription: invotekas@gmail.com (max tier).

Agent Claude Auth GitHub Auth How Token Stays Alive
Codi-E / Claude-4/5/6 Local Claude Max User's gh CLI Manual claude auth login
Pi-E OAuth (invotekas@gmail.com) pie-agent-bot GitHub App Claude cron job (3-day keepalive)
Volt-E OAuth (invotekas@gmail.com) volt-e-agent-bot GitHub App Claude cron job (3-day keepalive)
Review-E OAuth (invotekas@gmail.com) review-e-bot GitHub App Cluster-managed (GCP k3s rig-agent-runtime)
iBuild-E Local Claude Max ibuild-e-bot GitHub App Local LaunchAgent

Token Lifecycle

  1. Primary: Each Pi/VPS remote agent (Pi-E, Volt-E) runs a Claude cron job that keeps the OAuth token alive for 3 days by refreshing it automatically. (Review-E runs in GCP k3s and uses a cluster-managed credential, not this cron path.)
  2. Bootstrap/Fallback: M4 MacBook pushes tokens via push-claude-creds.sh (LaunchAgent, every 5 min). Only pushes when local token is newer than remote. Used to bootstrap new agents or recover from expiry.
  3. Emergency: VNC into agent host, run claude login to get a fresh token directly.

Token Sync Infrastructure

LaunchAgents on MacBook Air M4: - com.invotek.push-claude-creds — pushes OAuth to Pi-E only on credential change + 5-min interval (Volt-E disabled — it uses interactive claude login + VNC; Review-E migrated to GCP k3s and is no longer a push target) - com.invotek.refresh-github-tokens — generates fresh ghs_ GitHub App tokens every 30 min - ~~ai.invotek.claude3.token-sync — additional token sync~~ (DISABLED 2026-03-26) - ai.invotek.claude3.heartbeat — heartbeat check

Push targets (from script): - Pi-E: claude@100.107.48.17 (key: rpi-claude), path: /home/claude/.openclaw/agents/main/agent/auth-profiles.json - Volt-E: root@100.111.142.118 (key: vps-srv1099021), path: ~/.claude/credentials.json (agent-runner) - Review-E: migrated to GCP k3s (rig-agent-runtime) — credential is cluster-managed, no longer pushed to Pi4-02

Note: iBuild-E manages its own token via environment variable in ~/.zshrc (disabled com.invotek.ibuild-e.token-sync), not through the push script. The ai.invotek.mac-executor.plist LaunchAgent that also carried it was retired on 2026-09-25 with the rest of claude's LaunchAgents (see hosts.md).

Pi-E Token Watchdog: Cron every 5 min, Discord alert 1hr before OAuth expiry.

Rotation Schedule

Credential Rotation Next Due
RELEASE_PAT Manual (Review-E PAT) 2026-05-30 (EXPIRED — rotation pending, #23)
GitHub App tokens Auto (1hr lifetime, refreshed every 30min) —
OAuth tokens Auto (refreshed by push script / VNC login) —
Cloudflare API Token Manual Check Bitwarden
ASC API Key Manual Check Bitwarden

Expired / Deprecated

Entry Status Notes
GitHub PAT (Claude) (ghp_*) EXPIRED Replaced by OAuth + GitHub Apps
Telegram Bot Token Deprecated Discord preferred, Telegram still configured