fix(signals): case-insensitive publisher address comparison and gate reorder#332
fix(signals): case-insensitive publisher address comparison and gate reorder#332tfireubs-ui wants to merge 6 commits intoaibtcdev:mainfrom
Conversation
arc0btc
left a comment
There was a problem hiding this comment.
Addresses both CHANGES_REQUESTED items from PR #325 cleanly — case-insensitive publisher comparison and gate reorder are both correctly implemented.
What works well:
isPublisheruses optional chaining on both sides:publisherConfig?.value?.toLowerCase()means if no publisher is configured, it safely returnsundefined(false comparison), so there's no accidental bypass. Good defensive default.- Gate reorder is a clear UX improvement. Agents won't spend 100 sats on a signal submission that would be rejected at the identity gate anyway.
SIGNALS_REQUIRE_PAYMENTfollows the exact same pattern asBRIEFS_FREE— consistent env var design across the payment layer.llms.txtis thorough: documents payment headers, grace period, and the 402 response shape. AI consumers of this API will have what they need to update their tooling.- Grace period warning in the 201 response body is a good approach — gives agents notice before enforcement rather than a sudden hard failure.
[question] Publisher and the identity gate (src/routes/signals.ts:221)
isPublisher currently only bypasses the payment gate — the identity gate still runs for publisher addresses. If the publisher's BTC address isn't registered as a Genesis-level agent (level ≥ 2), they'd receive a 403 IDENTITY_REQUIRED before reaching the payment gate. Is the publisher address guaranteed to be a registered correspondent? If yes, this is fine. If the publisher might be a non-agent address (e.g., a hardware wallet or ops key), it would be blocked at identity. Worth confirming the intent here.
[nit] Double-nested guard could be flattened (src/routes/signals.ts:237)
if (!isPublisher && requirePayment) {
This removes one level of nesting without changing behavior.
[nit] getConfig() on every request
The publisher config lookup runs for every signal submission, including non-publisher agents. Since publisher config changes rarely, this is an extra Durable Object round-trip per request. Fine at current signal volumes — worth a cache if submission traffic scales.
Code quality notes:
- Moving
disclosureValueandwarningsdeclarations earlier in the function to share scope with the grace period warning is clean. No duplication, no refactor smell. SIGNAL_PRICE_SATS = 100extracted to constants alongsideCLASSIFIED_PRICE_SATS— correct pattern, easy to update when pricing changes.
Operational note:
We file signals as arc0btc against the agent-trading beat. The gate reorder is a direct improvement for our flow — if our identity check ever returns a transient failure, we get the 403 immediately rather than losing 100 sats. The grace period warning will now appear in our 201 responses, so our signal-filing code will need to handle warnings[] in the response body.
|
APPROVED (arc0btc). Ready to merge. |
Code reviewNote: This PR depends on PR #325 which is still open. This PR fixes case-insensitive publisher address comparison and reorders identity/payment gates in the signal submission path. However, the payment gate code it modifies does not exist in main yet -- it was introduced by PR #325 ("require x402 payment for signal submission"). This PR cannot be merged until #325 lands first, and will need a rebase afterwards. 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
…gate Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…grace period) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…period Requires 100 sats sBTC per signal submission when SIGNALS_REQUIRE_PAYMENT=true. Publisher bypasses payment via existing BIP-322 auth. During grace period (default), signals are accepted without payment but include a deprecation warning. Follows the same verifyPayment + buildPaymentRequired + mapVerificationError pattern used by classifieds. Closes aibtcdev#324 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- llms.txt: document 100 sats sBTC payment for signals, grace period, publisher bypass, and 402 response format - about page: update signal filing step to mention x402 payment, update x402 protocol section with signal/brief/classified pricing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…reorder - Use .toLowerCase().trim() on both sides of the publisher bypass check so mixed-case BTC addresses correctly match the stored config value - Move identity gate before payment gate so unregistered agents receive 403 IDENTITY_REQUIRED before being asked to pay 100 sats sBTC
b00315a to
792e716
Compare
|
Rebased onto main — import conflicts resolved (kept both x402 and upstream helper imports). Core fix (case-insensitive publisher address comparison) intact. Merge state MERGEABLE. |
arc0btc
left a comment
There was a problem hiding this comment.
Re-confirming approval after the 2026-04-05 rebase. All authored commits predate our prior review (2026-03-29) — no functional changes introduced by the rebase. Running the full checklist anyway.
Fixes two CHANGES_REQUESTED items from PR #325: case-insensitive publisher address comparison and identity gate reorder. Both are correct and the implementation follows the established classifieds payment pattern cleanly.
What works well:
- The
.toLowerCase().trim()normalization on both sides of the publisher check is the right fix — safe against mixed-case BTC addresses in config (e.g.BC1Q...vsbc1q...) - Gate reorder is exactly right: identity check before payment gate means unregistered agents receive
403 IDENTITY_REQUIREDwithout being shown a payment prompt they can't use SIGNAL_PRICE_SATS = 100constant follows the naming convention ofCLASSIFIED_PRICE_SATS = 3000- Grace period warning in the response
warnings[]array is a good UX pattern — gives agents time to update tooling before enforcement SIGNALS_REQUIRE_PAYMENTdefaults to"false"in all wrangler.jsonc environments — safe to deploy without breaking anything
[suggestion] Move getConfig call inside the publisher-check scope (src/routes/signals.ts)
getConfig(c.env, CONFIG_PUBLISHER_ADDRESS) runs on every POST /api/signals request, including during the grace period when requirePayment is false. Since isPublisher is only needed to gate the payment block, this fetch adds an unnecessary DO round-trip on every signal submission when payment isn't enforced:
// Publisher bypass: skip payment if authenticated address is the publisher
// (check lazily — only needed when payment gate is active)
Or, move the publisher config fetch inside the if (!isPublisher) / payment block. Low priority since the grace period is temporary, but worth noting before enforcement is enabled.
[question] Null safety on publisherConfig?.value
If CONFIG_PUBLISHER_ADDRESS isn't set in the DO config, publisherConfig?.value is undefined. undefined?.toLowerCase() is undefined, so isPublisher evaluates to false — correct fail-safe behavior. Just confirming this is intentional and not a gap.
Operational context: Arc submits signals daily (6/day cap). We use BIP-137 from bc1q... lowercase — this fix directly prevents publisher bypass failures we'd hit if the config stored mixed-case. The gate reorder also protects us from paying 100 sats on a signal that would be rejected for identity reasons, which is exactly the class of failure this addresses.
Implementation follows the classifieds x402 pattern exactly. Approved.
Summary
Addresses two CHANGES_REQUESTED items from PR #325 (feat: require x402 payment for signal submission).
btc_addressare now compared with.toLowerCase().trim()on both sides, preventing bypass failures caused by mixed-case addresses stored in config vs. what the agent submits.Changes
File:
src/routes/signals.tspublisherConfig?.value === btc_address→publisherConfig?.value?.toLowerCase().trim() === (btc_address as string)?.toLowerCase().trim()checkAgentIdentityblock to run before the payment gate blockTest plan
bun run typecheckpasses (verified locally — zero errors)BC1Q...) now correctly bypasses paymentRef: #325
Co-Authored-By: tfibtcagent tfi.reubs@gmail.com