Skip to content

fix(cipher): the mint printed the credential twice, and the second copy wore no label - #2988

Merged
POWERFULMOVES merged 1 commit into
mainfrom
fix/cipher-mint-single-emission
Sep 8, 2026
Merged

POWERFULMOVES merged 1 commit into
mainfrom
fix/cipher-mint-single-emission

Conversation

@POWERFULMOVES

Copy link
Copy Markdown
Owner

The defect

mint_cipher_token.py builds the bearer as token = f"cipher_{token_uuid.hex}" and stores that same token_uuid. Migration 20260728100000 declares token_uuid UUID PRIMARY KEY — no hash column — and it is the only cipher-token migration, so nothing superseded it. The stored row is the credential.

The insert uses Prefer: return=representation, so Supabase echoes that row back, and :53 printed it verbatim directly above the labelled line:

[{"token_uuid": "064cb5d4-3474-4faa-81f8-8c24f50bb776", ...}]
CIPHER_TOKEN=cipher_064cb5d434744faa81f88c24f50bb776

Same secret, two spellings — dashed in the row, hex in the token.

This is worse than a duplicate. Redaction keys on the label. An operator or scrubber removing CIPHER_TOKEN= takes away the copy they can see and leaves the one they can't — and the transcript then looks clean. .claude/PATTERNS.md already warns never to run this target in an agent transcript; this removes one of the two reasons why.

The change

  • The response body is no longer printed. It is still parsed, because "accepted but stored no row" must be an error, not a token the operator will try to use.
  • _redact() blanks both spellings in HTTPError detail before it reaches stderr. A 23505 duplicate-key error quotes the offending value back, and that value is token_uuid; server-controlled text isn't a safe place to assume the credential is absent.

Only consumer is Makefile:5727, which runs the script and shows its output — nothing parsed the JSON that was removed.

Tests: 6, every one negative-controlled

Two were wrong when first written, and the control is what caught them:

  • counting only the hex form passed against the unfixed script — the echoed row spells the uuid dashed, so the leak sailed straight past. Both forms are now counted.
  • ln is not line is an identity check; splitlines() rebuilds the string, so it fired on correct output. Now compared by value.

The redaction test was verified by disabling _redact() and watching it fail.

Scope, and why this lane kept expiring

This is the cheap, self-contained half of the twice-expired docs/reclaim-cipher-token-model lane. #2824 and #2864 both merged and were register-only (6+ 0-) — no remediation was ever written. Carried as one lane, the expensive half (schema change to a live auth table, operator sequencing) gated a change that needs neither.

Not fixed here, deliberately, and still unclaimed:

  • the token is stored unhashed (migration + fleet re-mint)
  • auth.ts:62-64 passes uuidHex to the PostgREST filter unvalidated when length ≠ 32 — lives in the Pmoves-cipher submodule, so its own PR
  • auth.ts:49 bootstrap mode grants all six scopes under one shared identity — the active mode on Z890 — and Makefile:5727 defaults to those same six scopes

Disclosure

No local interpreter in this repo has pytest_asyncio or pyyaml, so the root conftest.py fails to import and no pytest run under pmoves/ works out of the box. I ran these in a throwaway uv venv with pytest, pytest-asyncio, pyyaml. The 6 tests here are self-contained (network stubbed, no fixtures from conftest), but I could not run the wider suite locally — CI is the check on that.

Separately: register-claim wrote 1 CRLF into an otherwise pure-LF register on Windows. Git normalized it (staged blob has 0 CRLF, verified), so this PR is clean, but it's a papercut in register_append.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • coderabbit

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: af12de66-f685-40f9-8ad7-49a4d69f151f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added docs Documentation governance AGNOTE register / agent definitions / damage-control hooks labels Sep 7, 2026
@POWERFULMOVES
POWERFULMOVES force-pushed the fix/cipher-mint-single-emission branch from b0753e2 to af710b9 Compare September 8, 2026 08:47
…py wore no label

`mint_cipher_token.py` builds the bearer as `token = f"cipher_{token_uuid.hex}"`
and stores that same `token_uuid`. Migration 20260728100000 declares
`token_uuid UUID PRIMARY KEY` with no hash column, and it is the ONLY cipher-token
migration, so nothing has superseded it: THE STORED ROW IS THE CREDENTIAL.

The insert is sent with `Prefer: return=representation`, so Supabase echoes that
row back -- and line 53 printed it verbatim, immediately above the labelled
`CIPHER_TOKEN=` line:

    [{"token_uuid": "064cb5d4-3474-4faa-81f8-8c24f50bb776", ...}]
    CIPHER_TOKEN=cipher_064cb5d434744faa81f88c24f50bb776

Same secret, two spellings. That is worse than a duplicate: redaction keys on the
label, so an operator or log scrubber removing `CIPHER_TOKEN=` takes away the copy
they can see and leaves the one they cannot -- and the transcript then LOOKS clean.
`.claude/PATTERNS.md` already warns never to run this target in an agent
transcript; this removes one of the two reasons why.

- the response body is no longer printed. It is still parsed, because "accepted
  but stored no row" must be an error rather than a token the operator will try
  to use.
- `_redact()` blanks both spellings in HTTPError detail before it reaches stderr.
  A 23505 duplicate-key error quotes the offending value back, and that value is
  token_uuid; server-controlled text is not a safe place to assume the credential
  is absent.

Six tests, every one negative-controlled against the unfixed script. Two of them
were WRONG when first written and the control is what caught it:
  * counting only the hex form PASSED against the broken code -- the echoed row
    spells the uuid dashed, so the leak sailed past. Both forms are now counted.
  * `ln is not line` is an identity check; splitlines() rebuilds the string, so it
    fired on correct output. Now compared by value.

Scope: this is the cheap, self-contained half of the twice-expired
`docs/reclaim-cipher-token-model` lane (#2824, #2864 -- both merged, both
register-only, `6+ 0-`, no remediation ever written). Carrying it as one lane is
why it never shipped: the expensive half needs a schema change to a live auth
table plus operator sequencing, and it was gating a change that needs neither.

NOT fixed here, deliberately, and still unclaimed:
  * the token is stored unhashed (needs a migration + fleet re-mint)
  * `auth.ts:62-64` passes `uuidHex` to the PostgREST filter unvalidated when its
    length is not 32 -- lives in the Pmoves-cipher submodule, so its own PR
  * `auth.ts:49` bootstrap mode grants all six scopes under one shared identity,
    which is the ACTIVE mode on Z890, and `Makefile:5727` defaults to those same
    six scopes

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jxd5gryAFPXwhjaCMw9Qrx
@POWERFULMOVES
POWERFULMOVES force-pushed the fix/cipher-mint-single-emission branch from af710b9 to f1125fa Compare September 8, 2026 08:59
@POWERFULMOVES
POWERFULMOVES merged commit b0b193a into main Sep 8, 2026
29 checks passed
@POWERFULMOVES
POWERFULMOVES deleted the fix/cipher-mint-single-emission branch September 8, 2026 09:11
POWERFULMOVES added a commit that referenced this pull request Sep 11, 2026
…d-but-unreleased (#3007)

Six expired-and-unreleased claims were each blocking a future claimant while
nobody had said they were still on it. Five were already DELIVERED and simply
never released; one was mine and was genuinely unfinished.

MINE -- docs/reclaim-cipher-token-model, RELEASED with the real verdict: still
needed, never started, now SPLIT. Claimed twice (#2824, #2864) and both PRs were
REGISTER-ONLY -- #2864 was 6+ 0-, one file. Any audit keying on PR state reads
two merged PRs as closure. It kept expiring because one claim held two lanes and
the expensive half gated the cheap half. Cheap half landed as #2988; the
expensive half (unhashed storage on a live auth table, auth.ts:62-64 in a
submodule, bootstrap's six-scope shared identity) is named and left UNCLAIMED.

FIVE BACKFILL ACKNOWLEDGEMENTS, not owner-signed closures -- B850 x3, HERMES x1,
SPARK x1. Each carries filed-by: Z890-CLAUDE, merge SHA + timestamp, and states
that the owner-authored RELEASE and signed ACK remain OWED. Attribution stays
with the owner; only the signature is mine. That split is the point:
AGENT_IDENTITY_PROPOSAL_2026-09-04.md section 4 names "written about and never
writes" as the failure mode, so a backfill must not become another agent's
ledger entry authored by me.

CITATION CORRECTION. The precedent for this (#2960, the Mavis row) cites
"AGENTS.md:L60-62, co-owner has no authority to close another owner's claim".
That clause is not in AGENTS.md, and `git log -S` over AGENTS.md history returns
EMPTY -- it has never been there. The practice is sound and is followed here;
the phantom citation is not propagated. Grounded instead on the Village Rule
that AGENTS.md does carry: claim -> work -> sign -> release, "on completion
write a RELEASE row and a signed ACK block" -- which makes RELEASE the owner's
act without inventing a clause.

Verification: register is a strict byte prefix of origin/main plus 8589 appended
bytes, 6 rows, NUL count 1 preserved, postdate clean. Expired-and-unreleased
falls 6 -> 4.

SPARK's three remaining expired lanes are deliberately NOT backfilled: each has
an OPEN PR (#2946, #2959, #2943), so they are active work past TTL, not
abandoned work.


Claude-Session: https://claude.ai/code/session_01Jxd5gryAFPXwhjaCMw9Qrx

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: kiloclaw <kiloclaw@users.noreply.github.com>
POWERFULMOVES added a commit that referenced this pull request Sep 17, 2026
…ng correction I had backwards

OPERATOR CORRECTION, recorded at the top because it changes what the rest means:
the signing card is NOT an unlock or an authority gate. It is a LIVING DOC OF
PROVENANCE -- checkable, verifiable, current, deterministically maintained --
and the AGInTZ is ATTRIBUTED WITH THE RELEASE.

I spent this session building gates. The same code reads differently under the
right frame: when mint_cipher_token refuses an uncarded agent I read it as "you
lack authority"; under provenance it means THERE IS NOTHING TO ATTRIBUTE TO YET.
Not permission denied -- ledger absent.

The distinction decides the architecture. A permission model needs a gatekeeper
per act and cannot reach autonomy. A provenance model lets agents act and keeps
the record current, so attribution flows with the release. With self-improvement
near and the goal of all AGInTZ running autonomously round-trip, only one of
those scales.

LANES FILED:

  fix/guard-names-vs-targets         the Bash guard matches a protected path's
                                     NAME, not a resolved target -- four refusals
                                     for merely naming a file, including the
                                     remedy its own error recommends
  fix/skip-hides-absence             a skipped test reports green; that is how
                                     six of nine launchers stayed unwired under
                                     a REQUIRED check on a protected branch
  governance/identity-provenance-    operator-owned; the vocabulary declarations
    round-trip                       and the four real cards, with every
                                     measurement on the ledger

A CORRECTION INSIDE THE THIRD: I reported NINE signatures that "cannot sign" as
though all nine were defects. Five are correctly uncarded -- minimax is
model_backend, the three botz-* are role templates, and powerfulmoves is the
Legendary/umbrella class whose signing facet IS darkxside, which HAS an active
card. The root of trust was never broken. Real gaps: 4.

AND ONE ON MY OWN MERGED WORK: commit #2988 states ".claude/PATTERNS.md already
warns never to run this target in an agent transcript". The warning is real but
lives in .claude/skills/pmoves-cipher-memory/SKILL.md:52. Right fact, wrong
file -- a phantom citation of the kind this register has corrected before, now
merged under my name. Recorded rather than quietly fixed, because the register's
own precedent is that a bad citation gets named and not propagated.

I did NOT run cipher-mint-token. The mint prints the bearer, and my 2026-08-28
row records leaking a bootstrap token exactly that way. A credential scattered
into a transcript is what breaks the provenance chain the card exists to keep
verifiable -- which is the whole point of the corrected frame.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Sep 18, 2026
…rged but not in effect

Three RELEASE rows, each verified on main by CONTENT rather than by PR state —
the distinction the docs/reclaim-cipher-token-model row warns about, where an
audit keying on PR state reads merged-but-unremediated as closure.

  fix/nats-legacy-jetstream-volume  #3077 (40198cc)
  fix/cipher-mint-single-emission   #2988 (b0b193a)
  fix/cipher-skills-stale-blocker   #3009 (bb6168c)

THE NATS ROW CARRIES A DELIBERATE NON-CLOSURE. The compose fix is on main and
verified there, but `pmoves-nats-1` has NOT been recreated, so the volume does
not exist yet and JetStream is still writing to /tmp/nats/jetstream. The fix is
merged and NOT in effect. The recreate briefly drops the bus 51 containers use
and is an operator-sequenced act, so the row says so plainly instead of letting
a merged PR imply a fixed node.

THE TWO CIPHER ROWS ARE REFILED, and the reason is worth recording. They were
first written earlier today onto a local branch that was never pushed. The rows
existed, were committed, and still never reached main — so both lanes continued
to read as OPEN. A register row on an unpushed branch is not a filed row. That
is the same class as the defect they were closing: something that looks done
from one angle and is invisible from the one that counts.

Both are also LATE (7d and 5d past TTL), disclosed in the rows rather than
smoothed over. Delivery was on time in both cases; only the release was missed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Sep 18, 2026
…ng correction I had backwards

OPERATOR CORRECTION, recorded at the top because it changes what the rest means:
the signing card is NOT an unlock or an authority gate. It is a LIVING DOC OF
PROVENANCE -- checkable, verifiable, current, deterministically maintained --
and the AGInTZ is ATTRIBUTED WITH THE RELEASE.

I spent this session building gates. The same code reads differently under the
right frame: when mint_cipher_token refuses an uncarded agent I read it as "you
lack authority"; under provenance it means THERE IS NOTHING TO ATTRIBUTE TO YET.
Not permission denied -- ledger absent.

The distinction decides the architecture. A permission model needs a gatekeeper
per act and cannot reach autonomy. A provenance model lets agents act and keeps
the record current, so attribution flows with the release. With self-improvement
near and the goal of all AGInTZ running autonomously round-trip, only one of
those scales.

LANES FILED:

  fix/guard-names-vs-targets         the Bash guard matches a protected path's
                                     NAME, not a resolved target -- four refusals
                                     for merely naming a file, including the
                                     remedy its own error recommends
  fix/skip-hides-absence             a skipped test reports green; that is how
                                     six of nine launchers stayed unwired under
                                     a REQUIRED check on a protected branch
  governance/identity-provenance-    operator-owned; the vocabulary declarations
    round-trip                       and the four real cards, with every
                                     measurement on the ledger

A CORRECTION INSIDE THE THIRD: I reported NINE signatures that "cannot sign" as
though all nine were defects. Five are correctly uncarded -- minimax is
model_backend, the three botz-* are role templates, and powerfulmoves is the
Legendary/umbrella class whose signing facet IS darkxside, which HAS an active
card. The root of trust was never broken. Real gaps: 4.

AND ONE ON MY OWN MERGED WORK: commit #2988 states ".claude/PATTERNS.md already
warns never to run this target in an agent transcript". The warning is real but
lives in .claude/skills/pmoves-cipher-memory/SKILL.md:52. Right fact, wrong
file -- a phantom citation of the kind this register has corrected before, now
merged under my name. Recorded rather than quietly fixed, because the register's
own precedent is that a bad citation gets named and not propagated.

I did NOT run cipher-mint-token. The mint prints the bearer, and my 2026-08-28
row records leaking a bootstrap token exactly that way. A credential scattered
into a transcript is what breaks the provenance chain the card exists to keep
verifiable -- which is the whole point of the corrected frame.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation governance AGNOTE register / agent definitions / damage-control hooks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant