Skip to content

Bitwarden hygiene plan — full migration (LastVet, Angel, Sovereign, last1-platform)

Date: 2026-08-10
Status: Planning only. Do not execute until Ryan approves execution phase.
Companion: BITWARDEN_ENV_AUDIT_FULL.md, BITWARDEN_NAMING_CONVENTION.md


Executive summary

Metric Count
Existing secrets (lastvet + angel + sovereign) 75
Planned renames (create new key + deprecate/delete old) 58
Planned moves (change project) 5
Planned creates (net new keys) ~79
Planned deletes (after grace + verification) ~18
Estimated Bitwarden UI/CLI actions ~210

Action estimate: each rename ≈ 2 ops (create + delete old); each move ≈ 2 ops; each create ≈ 1; note updates ≈ 15.

Ryan decisions locked in this plan

Decision Plan effect
Staging crypto = copy AWS All *_STAGING_* keys copy AWS SM / current BWS prod-oriented values where noted
last1-platform project Host LAST1_TOKEN_ENCRYPTION_KEY, ONSET_IDENTITY_ASSERTION_SECRET, shared Cloudflare ID
Imaging deferred No LastVet MinIO/ZFS → API env mapping in Phase 1
R940 migration URLs Create full LASTVET_R940_*_MIGRATION_DATABASE_URL secrets
APNS skipped for staging cutover No APNS keys in Phase 1; documented in follow-up
Angel/Sovereign pre-migration Renames may break local sync briefly; LastVet order protects critical path

Migration sequence (global order)

Phase 0  Create Bitwarden projects: last1-platform (+ optional last1-test)
Phase 1  CREATE-only (no deletes) — LastVet R940 + AWS backups + migration URLs
Phase 2  MOVE cross-product secrets to last1-platform (duplicate read paths in scripts)
Phase 3  LastVet deploy script reads new names; verify R940 staging soak (AWS unchanged)
Phase 4  RENAME LastVet deprecated keys (Railway, ambiguous crypto) — deprecate notes
Phase 5  Angel + Sovereign renames (breakage acceptable)
Phase 6  DELETE Railway keys + deprecated names (after Railway teardown + 30-day grace)
Phase 7  Manifest + script updates in repos

LastVet critical path rule: Phases 1–3 complete and R940 /health verified before Phase 4 deletes any LastVet secret still referenced by AWS parallel run.


Consolidated migration table

Legend:

  • Value: copy = same bytes as source; compose = build URL from parts; fresh = generate new; pending = operator must set
  • Seq: execution order bucket (lower first)

A — Create projects (Seq 0)

Current Project Proposed Project Action Value Seq
last1-platform (new) Create project 0
last1-test (new, optional) Create project 0

B — Cross-product moves (Seq 2)

Current Project Proposed Project Action Value Rationale Seq
LAST1_TOKEN_ENCRYPTION_KEY lastvet LAST1_PLATFORM_STAGING_TOKEN_ENCRYPTION_KEY last1-platform Move copy AWS staging Shared platform; per-env 2
LAST1_TOKEN_ENCRYPTION_KEY lastvet LAST1_PLATFORM_PROD_TOKEN_ENCRYPTION_KEY last1-platform Create copy AWS prod / current BWS Staging/prod split 2
ONSET_IDENTITY_ASSERTION_SECRET lastvet LAST1_PLATFORM_SHARED_ONSET_IDENTITY_ASSERTION_SECRET last1-platform Move copy Onset + LastVet 2
CLOUDFLARE_ACCOUNT_ID lastvet LAST1_PLATFORM_SHARED_CLOUDFLARE_ACCOUNT_ID last1-platform Move copy Shared CI 2
CLOUDFLARE_API_TOKEN angel LAST1_PLATFORM_SHARED_CLOUDFLARE_API_TOKEN last1-platform Move (if Ryan confirms) copy Optional consolidate 2

C — LastVet: R940 + AWS creates (Seq 1)

Current Project Proposed Project Action Value Rationale Seq
LASTVET_R940_STAGING_MIGRATION_DATABASE_URL lastvet Create compose Ryan decision: full URL, master role 1
LASTVET_R940_PROD_MIGRATION_DATABASE_URL lastvet Create compose Same 1
LASTVET_AWS_STAGING_{KEY} ×40 lastvet Create copy AWS SM Operator backup; see ECS app_secret_keys 1
LASTVET_AWS_PROD_{KEY} ×40 lastvet Create copy AWS SM Same 1
LASTVET_R940_STAGING_{KEY} for crypto JWT/HMAC ×8 lastvet Create copy AWS staging Explicit staging entries 1
LASTVET_R940_PROD_{KEY} for crypto ×8 lastvet Create copy AWS prod Explicit prod entries 1
LASTVET_R940_SHARED_OLLAMA_BASE_URL lastvet Create pending → http://inference-01.tailc3bbdc.ts.net:11434 Reserved; API does not read yet 1

D — LastVet: renames (Seq 3–4)

Current Project Proposed Project Action Value Rationale Seq
DATABASE_APP_URL_R940_STAGING lastvet LASTVET_R940_STAGING_DATABASE_APP_URL lastvet Rename copy Convention 3
DATABASE_APP_URL_R940_PROD lastvet LASTVET_R940_PROD_DATABASE_APP_URL lastvet Rename copy Convention 3
DATABASE_APP_URL_STAGING lastvet LASTVET_RAILWAY_STAGING_DATABASE_APP_URL lastvet Rename copy Legacy marker 4
DATABASE_APP_URL_PRODUCTION lastvet LASTVET_RAILWAY_PROD_DATABASE_APP_URL lastvet Rename copy Legacy marker 4
MIGRATION_DATABASE_URL_STAGING lastvet LASTVET_RAILWAY_STAGING_MIGRATION_DATABASE_URL lastvet Rename copy Legacy marker 4
MIGRATION_DATABASE_URL_PRODUCTION lastvet LASTVET_RAILWAY_PROD_MIGRATION_DATABASE_URL lastvet Rename copy Legacy marker 4
LASTVET_APP_PASSWORD_STAGING lastvet LASTVET_RAILWAY_STAGING_POSTGRES_APP_PASSWORD lastvet Rename copy Target explicit 4
LASTVET_APP_PASSWORD_PRODUCTION lastvet LASTVET_RAILWAY_PROD_POSTGRES_APP_PASSWORD lastvet Rename copy Target explicit 4
LASTVET_R940_CLOUDFLARED_TUNNEL_TOKEN lastvet LASTVET_EDGE_CLOUDFLARED_TUNNEL_TOKEN lastvet Rename copy EDGE not R940 3
LASTVET_R940_CLOUDFLARED_ORIGIN_CERT lastvet LASTVET_EDGE_CLOUDFLARED_ORIGIN_CERT lastvet Rename copy EDGE not R940 3
VETERAN_JWT_SECRET lastvet LASTVET_AWS_PROD_VETERAN_JWT_SECRET lastvet Rename copy Was prod-oriented 4
SECURE_LINK_HMAC_SECRET lastvet LASTVET_AWS_PROD_SECURE_LINK_HMAC_SECRET lastvet Rename copy Same 4
OAUTH_JWT_SECRET lastvet LASTVET_AWS_PROD_OAUTH_JWT_SECRET lastvet Rename + fix note copy Remove stale fallback note 4
PROVIDER_JWT_SECRET lastvet LASTVET_AWS_PROD_PROVIDER_JWT_SECRET lastvet Rename copy Env explicit 4
LAST1_WEBHOOK_SECRET lastvet LASTVET_AWS_PROD_LAST1_WEBHOOK_SECRET lastvet Rename copy Env explicit 4
LAST1_PROOF_DRAINER_SECRET lastvet LASTVET_AWS_PROD_LAST1_PROOF_DRAINER_SECRET lastvet Rename copy Env explicit 4
LASTVET_INTERNAL_ADMIN_SECRET lastvet LASTVET_AWS_PROD_INTERNAL_ADMIN_SECRET lastvet Rename copy Env explicit 4
LASTVET_APP_SECRET lastvet LASTVET_AWS_PROD_APP_SECRET lastvet Rename copy Env explicit 4
VETERAN_SESSION_EXCHANGE_SECRET lastvet LASTVET_IOS_SHARED_VETERAN_SESSION_EXCHANGE_SECRET lastvet Rename copy iOS + API 3
VA_OPEN_DATA_API_KEY lastvet LASTVET_IOS_SHARED_VA_OPEN_DATA_API_KEY lastvet Rename copy iOS only 3
E2E_OTP_IMAP_HOST lastvet LASTVET_TEST_E2E_OTP_IMAP_HOST lastvet or last1-test Rename copy Harness scripts 4
E2E_OTP_IMAP_USER lastvet LASTVET_TEST_E2E_OTP_IMAP_USER lastvet or last1-test Rename copy Harness 4
E2E_OTP_IMAP_PASSWORD lastvet LASTVET_TEST_E2E_OTP_IMAP_PASSWORD lastvet or last1-test Rename copy Harness 4

E — LastVet: keep as-is (Seq 3)

Current Action Notes
LASTVET_R940_POSTGRES_APP_PASSWORD Keep Link note to app URL secret
LASTVET_R940_POSTGRES_MASTER_PASSWORD Keep Used to compose migration URLs
LASTVET_R940_MINIO_ROOT_USER Keep Task 2 infra
LASTVET_R940_MINIO_ROOT_PASSWORD Keep Task 2 infra
LASTVET_R940_ZFS_IMAGING_KEY Keep ZFS recovery doc

F — LastVet: deletes (Seq 6)

Current Action When
VA_FORMS_API_KEY Delete After confirming iOS still unused
All LASTVET_RAILWAY_* (6 keys) Delete After Railway teardown + 30-day grace
Deprecated names after renames (×~22) Delete After scripts verified
LAST1_TOKEN_ENCRYPTION_KEY, ONSET_*, CLOUDFLARE_ACCOUNT_ID in lastvet Delete After last1-platform copies + deploy scripts updated

G — Angel: renames (Seq 5)

Current Project Proposed Action Value Seq
DATABASE_URL angel ANGEL_CLOUDFLARE_PROD_DATABASE_URL Rename copy (neon) 5
SESSION_SECRET angel ANGEL_CLOUDFLARE_PROD_SESSION_SECRET Rename copy 5
R2_ACCESS_KEY_ID angel ANGEL_CLOUDFLARE_PROD_R2_ACCESS_KEY_ID Rename copy 5
R2_SECRET_ACCESS_KEY angel ANGEL_CLOUDFLARE_PROD_R2_SECRET_ACCESS_KEY Rename copy 5
R2_ENDPOINT angel ANGEL_CLOUDFLARE_PROD_R2_ENDPOINT Rename copy 5
R2_API_TOKEN angel ANGEL_CLOUDFLARE_PROD_R2_API_TOKEN Rename copy 5
S3_BUCKET angel ANGEL_CLOUDFLARE_PROD_S3_BUCKET Rename copy 5
OLLAMA_BASE_URL angel ANGEL_R940_SHARED_OLLAMA_BASE_URL Rename set inference-01 URL 5
OLLAMA_MODEL angel ANGEL_R940_SHARED_OLLAMA_MODEL Rename copy 5
OLLAMA_TIMEOUT_MS angel ANGEL_R940_SHARED_OLLAMA_TIMEOUT_MS Rename copy 5
CF_ACCESS_CLIENT_ID angel ANGEL_R940_SHARED_CF_ACCESS_CLIENT_ID Rename copy or empty 5
CF_ACCESS_CLIENT_SECRET angel ANGEL_R940_SHARED_CF_ACCESS_CLIENT_SECRET Rename copy or empty 5
ANTHROPIC_API_KEY angel ANGEL_CLOUDFLARE_PROD_ANTHROPIC_API_KEY Rename copy 5
NCBI_API_KEY angel ANGEL_CLOUDFLARE_PROD_NCBI_API_KEY Rename copy 5
SEMANTIC_SCHOLAR_API_KEY angel ANGEL_CLOUDFLARE_PROD_SEMANTIC_SCHOLAR_API_KEY Rename copy 5
SENDGRID_API_KEY angel ANGEL_CLOUDFLARE_PROD_SENDGRID_API_KEY Rename copy 5
STRIPE_SECRET_KEY angel ANGEL_CLOUDFLARE_PROD_STRIPE_SECRET_KEY Rename copy 5
STRIPE_WEBHOOK_SECRET angel ANGEL_CLOUDFLARE_PROD_STRIPE_WEBHOOK_SECRET Rename copy 5
STRIPE_PRICE_ID angel ANGEL_CLOUDFLARE_PROD_STRIPE_PRICE_ID Rename copy 5
GOOGLE_* (×3) angel ANGEL_CLOUDFLARE_PROD_GOOGLE_* Rename copy 5
APPLE_* (×5) angel ANGEL_CLOUDFLARE_PROD_APPLE_* Rename copy 5
WEB_ORIGIN angel ANGEL_CLOUDFLARE_PROD_WEB_ORIGIN Rename copy 5
WEBAUTHN_RP_ID angel ANGEL_CLOUDFLARE_PROD_WEBAUTHN_RP_ID Rename copy 5
ANGEL_INTERNAL_ADMIN_SECRET angel ANGEL_CLOUDFLARE_PROD_INTERNAL_ADMIN_SECRET Rename copy 5
NEXT_PUBLIC_API_URL angel ANGEL_CLOUDFLARE_PROD_NEXT_PUBLIC_API_URL Rename copy 5
CF_ACCOUNT_ID angel (delete after move) Delete 6

H — Angel: local dev keys (Seq 5–6)

Current Proposed Action Rationale
POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB ANGEL_LOCAL_DEV_POSTGRES_* or remove from BWS Rename or delete Not Worker secrets
MINIO_ROOT_USER, MINIO_ROOT_PASSWORD Remove from BWS Delete Local compose only
REDIS_URL, PORT Remove from BWS Delete Local only
S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY Remove from BWS Delete Duplicates MinIO dev

I — Angel: creates (Seq 5)

Proposed Action Value
ANGEL_CLOUDFLARE_PROD_COUNCIL_PANEL_JSON Create optional JSON
ANGEL_CLOUDFLARE_PROD_APPLE_IOS_CLIENT_ID Create if non-default

J — Sovereign: creates (Seq 5)

Proposed Project Action Value Maps to
SOVEREIGN_R940_SHARED_ENGINE_URL sovereign Create pending (Ryan: hostname) VITE_ENGINE_URL
SOVEREIGN_R940_SHARED_ENGINE_URL_LOCAL sovereign Create pending VITE_ENGINE_URL_LOCAL
SOVEREIGN_R940_SHARED_OLLAMA_BASE_URL sovereign Create inference-01:11434 future use

Update Sovereign/.env.*.example to reference BWS pull script (future PR).

K — Meridian: creates (Seq 5)

Proposed Project Action Value Maps to
MERIDIAN_R940_SHARED_OLLAMA_BASE_URL meridian (new project) Create http://inference-01.tailc3bbdc.ts.net:11434 OLLAMA_BASE_URL
MERIDIAN_R940_SHARED_OLLAMA_MODEL meridian Create model tag OLLAMA_MODEL
MERIDIAN_R940_DEV_API_BASE meridian Create local shim URL MERIDIAN_API_BASE
MERIDIAN_R940_DEV_API_KEY meridian Create shim key MERIDIAN_API_KEY
MERIDIAN_TEST_VA_SANDBOX_ACCESS_TOKEN meridian Create copy operator PAT VA_SANDBOX_ACCESS_TOKEN
MERIDIAN_TEST_TELEMETRY_URL meridian Create optional MERIDIAN_TELEMETRY_URL
MERIDIAN_TEST_TELEMETRY_TOKEN meridian Create optional MERIDIAN_TELEMETRY_TOKEN

Sub-task G — Cross-repo rename layer (what must change, where)

Bitwarden renames do not change application runtime var names. Each repo needs a mapping layer (or manifest key update) so deploy/sync scripts read new BWS keys and export canonical env names.

Pattern summary by repo

Repo Canonical env doc BWS consumption today Mapping layer today Recommended mapping layer owner Files to update when BWS keys rename
last.vet-api .env.example, environment.ts lastvet-gates/scripts/sync-bitwarden-secrets.sh.env.local (1:1 keys) None on R940 VM yet lastvet-gates/scripts/restore-lastvet-api-env-from-bws.sh (new) on lastvet-api VM; systemd EnvironmentFile lastvet-gates/bitwarden/secrets-manifest.json, sync-bitwarden-secrets.sh, new restore script, LASTVET_MIGRATION_PLAN.md unit example
last.vet-ios Secrets.xcconfig (gitignored) sync-bitwarden-secrets.sh ios — manifest key → xcconfig 1:1 via manifest Manifest key names only (no runtime rename in Swift) lastvet-gates/bitwarden/secrets-manifest.json, sync-bitwarden-secrets.sh
last.vet-web .env.local sync-bitwarden-secrets.sh web 1:1 partial manifest Manifest + optional Cloudflare Pages dashboard secrets-manifest.json, sync-bitwarden-secrets.sh
last.vet-provider .env.staging.example Cloudflare Pages env (not BWS-driven) None Out of BWS migration Phase 1 — URLs are public N/A for BWS rename
lastvet-gates (infra) scripts Direct bws secret list key lookup Hardcoded key names Update script constants when edge keys rename scripts/restore-cloudflared-from-bws.sh (LASTVET_R940_CLOUDFLARED_*LASTVET_EDGE_*)
angel .env.example, config/secrets.manifest.yaml secrets-pull.sh dumps BWS 1:1 to .env; secrets-run.sh uses bws run; secrets-push-wrangler.sh allowlist Partial — wrangler push uses same names as Worker angel/scripts/bws-to-runtime.env.sh (new shared library) sourced by pull, run, and push scripts secrets-pull.sh, secrets-run.sh, secrets-push-wrangler.sh, secrets-sync.sh, deploy-web-pages.sh, config/secrets.manifest.yaml
Sovereign (client) .env.mac.example, .env.ios.example Manual copy to .env None Sovereign/scripts/secrets-pull.sh (new) + scripts/build-tauri.sh exports VITE_* from mapped BWS keys New scripts; .env.*.example; CI if added
Odysseus (repo TBD) odysseus/.env (per UI copy) Planned: bws run in systemd None /etc/sovereign/odysseus.env generator on sovereign-app from BWS Odysseus repo systemd unit + generator script (when repo on agent-01)
MemPalace (repo TBD) import scripts Planned: SOVEREIGN_DB_URL via bws None sovereign-mem backup/import scripts read BWS directly Import tooling in odysseus/sovereign-import package
last1-meridian .env.example python-dotenv loads .env with runtime key names None last1-meridian/scripts/secrets-run.sh (bws run or map MERIDIAN_* → runtime) New script + .env.example comments only
last1-platform N/A Read by other products' mapping layers N/A Each consumer's mapping script pulls platform project by ID All products that read LAST1_PLATFORM_*
Bitwarden (storage names)          Mapping layer (per repo)           Runtime (code)
─────────────────────────          ────────────────────────           ────────────────
LASTVET_R940_STAGING_DATABASE_APP_URL  ──► restore-lastvet-api-env.sh ──► DATABASE_APP_URL
LAST1_PLATFORM_STAGING_TOKEN_ENCRYPTION_KEY ──► same script ──► LAST1_TOKEN_ENCRYPTION_KEY
ANGEL_CLOUDFLARE_PROD_DATABASE_URL  ──► secrets-push-wrangler.sh ──► DATABASE_URL (Wrangler)
ANGEL_R940_SHARED_OLLAMA_BASE_URL  ──► secrets-run.sh + local.env ──► OLLAMA_BASE_URL
SOVEREIGN_R940_SHARED_ENGINE_URL   ──► Sovereign build script ──► VITE_ENGINE_URL
MERIDIAN_R940_SHARED_OLLAMA_BASE_URL ──► meridian secrets-run.sh ──► OLLAMA_BASE_URL

Do not build one mega shared script in lastvet-gates for Angel/Sovereign/Meridian. Each product owns its mapper; last1-platform secrets are read by ID from whichever mappers need them.

Per-repo change checklist (no code changes in this task)

LastVet (critical path — do first)

  1. Add lastvet-gates/scripts/restore-lastvet-api-env-from-bws.sh with explicit BWS_KEY → export RUNTIME_VAR table.
  2. Update bitwarden/secrets-manifest.json to new storage key names (or add indirection table in manifest).
  3. Update sync-bitwarden-secrets.sh to resolve new keys for ios/api/web targets.
  4. Update restore-cloudflared-from-bws.sh for LASTVET_EDGE_* names.
  5. Point systemd on lastvet-api at restore script output (/etc/lastvet/api.env).
  6. Do not change last.vet-api/src/config/environment.ts.

Angel (breakage OK)

  1. Add mapping function in new scripts/lib/bws-env-map.sh.
  2. Change secrets-pull.sh to write runtime names to .env after map (or write mapped file consumed by run).
  3. Change secrets-push-wrangler.sh to read ANGEL_CLOUDFLARE_PROD_* and push as Worker secret names unchanged.
  4. Update deploy-web-pages.sh read_secret calls for renamed keys.
  5. Optional: iOS AngelConfig.swift stays hardcoded unless adding BWS-backed xcconfig later.

Sovereign client

  1. Create sovereign BWS keys for engine URLs.
  2. Add scripts/secrets-pull.sh emitting .env with VITE_* runtime names.
  3. Document Tauri build: source .env && npm run tauri build.

Meridian

  1. Create Bitwarden project meridian.
  2. Add scripts/secrets-run.sh mapping MERIDIAN_R940_* → runtime for meridian run --provider ollama.
  3. Keep CI on mock provider without BWS.

Cross-product (last1-platform)

  1. Create project and staging/prod token encryption keys.
  2. Update LastVet restore script to read from two project IDs (lastvet + last1-platform).
  3. Document rotation: platform keys rotate once; all mappers re-copy on deploy.

Test coverage after rename (manual / script verification)

Repo Verify command / signal
last.vet-api curl localhost:3000/health on R940; AWS ECS unchanged
last.vet-ios sync-bitwarden-secrets.sh ios regenerates xcconfig; build succeeds
last.vet-api E2E e2e-roi-gate0-full.sh with renamed LASTVET_TEST_E2E_OTP_IMAP_*
angel ./scripts/secrets-run.sh pnpm exec turbo run dev; ./scripts/secrets-push-wrangler.sh; digest cron dry run
Sovereign Tauri build with pulled .env; engine chip connects to new MagicDNS host
last1-meridian secrets-run.sh meridian run --provider ollama against inference-01
lastvet-gates restore-cloudflared-from-bws.sh on edge after LASTVET_EDGE_* rename

Deploy-time mapping layer (LastVet R940)

Example /usr/local/bin/restore-lastvet-api-env-from-bws.sh (to be implemented Week 2):

#!/usr/bin/env bash
set -euo pipefail
ENV_LANE="${1:-staging}"   # staging | prod
TARGET="${2:-R940}"        # R940 | AWS

bws_get() { bws secret list | jq -r --arg k "$1" '.[] | select(.key==$k) | .value'; }

export DATABASE_APP_URL="$(bws_get "LASTVET_${TARGET}_${ENV_LANE}_DATABASE_APP_URL")"
export MIGRATION_DATABASE_URL="$(bws_get "LASTVET_${TARGET}_${ENV_LANE}_MIGRATION_DATABASE_URL")"
export LAST1_TOKEN_ENCRYPTION_KEY="$(bws_get "LAST1_PLATFORM_${ENV_LANE}_TOKEN_ENCRYPTION_KEY")"
export ONSET_IDENTITY_ASSERTION_SECRET="$(bws_get "LAST1_PLATFORM_SHARED_ONSET_IDENTITY_ASSERTION_SECRET")"
# ... remaining LASTVET_${TARGET}_${ENV_LANE}_* → canonical names

Angel secrets-push-wrangler.sh must map ANGEL_CLOUDFLARE_PROD_* → Worker secret names.


Follow-up list (not Phase 1)

Item Owner Trigger
APNS secrets for LastVet iOS push Ryan scopes After staging cutover stable
LastVet imaging MinIO/ZFS env adapter Task 2 Post-staging cutover
bitwarden/secrets-manifest.json regeneration Agent After Phase 4
Sovereign unicorn doc purge Agent With Sovereign BWS pull script
Merge PR #8 lastvet-only docs into docs/architecture/ or archive Ryan Optional dedup

Action count detail

Operation type Count CLI ops each Subtotal
Create project 1–2 1 2
Create new secrets ~79 1 79
Rename (create + delete old) 58 2 116
Move (create + delete old) 5 2 10
Delete (standalone) ~18 1 18
Note / metadata updates ~15 1 15
Total estimated ~240

Report rounded figure: ~220 (some creates coalesce with renames in single session).


Verification checklist (before any delete)

  • [ ] restore-lastvet-api-env-from-bws.sh staging R940 produces valid /health
  • [ ] AWS ECS staging still runs unchanged (no BWS blind-sync into SM)
  • [ ] secrets-push-wrangler.sh succeeds with new Angel key names
  • [ ] last.vet-api/scripts/e2e-roi-gate0-full.sh reads renamed LASTVET_TEST_E2E_OTP_IMAP_*
  • [ ] iOS sync script reads LASTVET_IOS_SHARED_*
  • [ ] rg 'LAST1_TOKEN_ENCRYPTION_KEY' in lastvet project returns only last1-platform copies
  • [ ] Meridian secrets-run.sh maps MERIDIAN_R940_* → runtime for ollama provider
  • [ ] No secret names or notes contain PHI/PII (re-audit)

References