Skip to content

AIN-289 B2 · rotation grace migration (0034) + auth-additive + grace mint script#96

Merged
hizrianraz merged 2 commits into
mainfrom
hizrianraz/ain-289-b2-0034-api-key-hash-pending
May 28, 2026
Merged

AIN-289 B2 · rotation grace migration (0034) + auth-additive + grace mint script#96
hizrianraz merged 2 commits into
mainfrom
hizrianraz/ain-289-b2-0034-api-key-hash-pending

Conversation

@hizrianraz
Copy link
Copy Markdown
Contributor

@hizrianraz hizrianraz commented May 28, 2026

Charter v2 B2 fix for the single-column finding on tenants.api_key_hash.

  • alembic 0034: ADD COLUMN api_key_hash_pending TEXT NULL + partial unique index. Additive only.
  • ORM: TenantORM gets api_key_hash_pending field.
  • Auth-additive: deps.py / middleware / ownership.py match api_key_hash OR api_key_hash_pending (no-op when pending is NULL).
  • scripts/rotate_key_grace_ain289.py: mint -> 1Password -> set pending -> verify NEW=200 -> promote -> verify again. --fallback-cutover preserves run-1 single-UPDATE path. Auto-detects missing column.

Depends on AIN-291 (must merge first; 0034 sits on 0033). 625/625 tests green, mypy --strict clean. Disc #12 untouched.

🤖 Generated with Claude Code


Note

Cursor Bugbot is generating a summary for commit 1c35b30. Configure here.

hizrianraz and others added 2 commits May 28, 2026 17:15
…033)

Charter A2 / Disc #12-bounded migration. Two additive things:

1. CREATE TABLE public.training_runs — one row per L14.2 daily training
   tick. Captures judge outcomes, policy_version_from→to, promotion
   verdict, per-cell deltas, replay-gate result, and ruleset_hash.

2. CREATE ROLE ainfera_labs LOGIN (no password set here; founder sets
   PASSWORD via Doppler-injected ALTER ROLE). Least-priv grants:
   - INSERT on training_runs (+ sequence USAGE)
   - SELECT on routing_outcomes, inferences, models, providers, agents
   - column-level UPDATE on routing_outcomes (judge_score, judge_model,
     judge_rationale, judge_labeled_at, judge_status, reward) — AIN-290
     columns only
   - column-level UPDATE on tenant_routing_policies (active_policy,
     active_policy_version) — AIN-296 columns only
   - REVOKE DELETE on every table

Verified via `alembic upgrade 20260528_0032:20260528_0033 --sql`:
DDL renders cleanly; `alembic heads` shows `20260528_0033 (head)`.

Disc #12 still binds: no edits to scoring, candidate-set, settlement,
auth, key prefix, or hard-delete rules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Charter v2 B2 fix for the run-1 single-column finding on tenants.api_key_hash.

* alembic 0034: ADD COLUMN api_key_hash_pending TEXT NULL + partial
  unique index. Additive only.
* ORM: TenantORM gets api_key_hash_pending field.
* Auth-additive: deps.py / middleware / ownership.py match EITHER
  api_key_hash OR api_key_hash_pending. No-op when pending is NULL.
* scripts/rotate_key_grace_ain289.py: mint + 1P store + set pending +
  verify NEW=200 + promote + verify again. --fallback-cutover preserves
  the run-1 single-UPDATE path. Auto-detects missing column. Never
  prints raw secrets.

625/625 tests green. mypy --strict clean. Disc #12 untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 28, 2026

AIN-289 🔴 [SECURITY] Rotate leaked ai_infera_* keys committed in .launch-snapshots/e2e-env.sh

🔴 Live production bearer keys committed to git. ~/code/ainfera-ai/.launch-snapshots/e2e-env.sh (line ~5) contains working ai_infera_* keys for 3 active agents — verified in prod (dftfpwzqxoebwzepygzl):

Agent Status Daily cap Per-call Last call
yavanna active $15 $1.00 2026-05-26
aule active $10 $0.75 2026-05-26
namo active $5 $0.50 2026-05-26

Anyone with repo (or git-history) read access can spend against Ainfera's provider accounts up to these caps daily. Caps bound the blast radius — contain-and-rotate, not catastrophe — but rotate today.

Sequence (founder/terminal — credentialed actions)

  1. REVOKE the 3 exposed keys now (status=revoked) — bleed-stop, before the cleanup PR.
  2. RE-ISSUE via issuance flow → update Doppler.
  3. SCRUB: git rm the file → add .launch-snapshots/ to .gitignore → history-scrub (git filter-repo) → force-push. Keys persist in history; deletion alone is insufficient.
  4. Audit: check provider dashboards for anomalous spend on the 3 keys since 2026-05-15 (file creation).

Branch

security/rotate-launch-snapshot-keys (Tulkas-class finding; Aulë executes the scrub PR; founder runs revoke/re-issue).

Done when

  • 3 keys revoked + re-issued; Doppler updated; fleet probes green on new keys
  • file removed + .gitignore'd + history scrubbed + force-pushed
  • provider spend audited for anomalies
  • grep the org for any other committed ai_infera_* / secrets (sweep)

Found during AIN-285 trace (probe agent 5298a483 = aule per e2e-env.sh:5).

Review in Linear

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.

Reviewed by Cursor Bugbot for commit 1c35b30. Configure here.

_emit({"agent": agent, "stage": "verify_new", "exit": rc_verify})
if rc_verify != 0:
_emit({"agent": agent, "stage": "ABORT_pre_promote", "reason": "verify failed"})
return rc_verify
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dry-run mode always fails due to live verification

Low Severity

When --dry-run is passed, _psql and _op_store correctly become no-ops, but _verify_via_harness still executes for real. Since the DB UPDATE was skipped, the newly minted key has no matching hash in the database, so verification always returns non-zero. In the grace path this triggers the ABORT_pre_promote exit before the promote step is ever shown; in the fallback-cutover path the function returns the failure code directly. This means --dry-run can never preview the full rotation flow and always exits non-zero, making it misleading.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1c35b30. Configure here.

@hizrianraz hizrianraz merged commit 544c251 into main May 28, 2026
4 checks passed
@hizrianraz hizrianraz deleted the hizrianraz/ain-289-b2-0034-api-key-hash-pending branch May 28, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant