Repository navigation
PAY-002B1 — Define caller-scoped command identity and deterministic Stripe keys #163
Description
Officer impact: None. This creates an unused server-side safety library. It does not add checkout, refund, admin, or officer actions.
Officer documentation: None — no officer-visible behavior, procedure, permission, provider setting, or deployed surface changes. Current commerce remains NOT AVAILABLE YET.
Deployment evidence: None at creation. Closure proves source, tests, review, merge, and exact-commit CI only. It must not claim website publication, Firebase deployment, Stripe configuration, provider objects, or live behavior.
Parent and dependencies
Atomic pure source child of catalogued PAY-002B.
- PAY-001A/PAY-001A — Add bounded request validation and safe logging primitives #157 is merged and supplies bounded, fail-closed request normalization.
- PAY-002A1/PAY-002A1 — Add pure canonical commerce-state reducers and a safe legacy classifier #161 is merged and supplies canonical commerce state.
- PAY-002B2 will own Firestore transactions, leases, fencing, result replay, and audit records.
- PAY-002C/D will own race and merchandise checkout sagas.
- PAY-003/PAY-003 — Build idempotent asynchronous Stripe event processing #106 owns webhook adoption.
- PAY-005 owns refund commands.
No live PAY-002B issue, claim, or open pull request existed at creation. The current queue had no unclaimed status:ready issue.
Problem
Retries currently have no shared identity. A double click or lost network response can create a second Stripe object. Different endpoints could also invent incompatible key formats, leak caller information into provider keys, or mistake a reused command ID for a safe retry.
The cryptographic identity contract must be pure and stable before Firestore lease logic or a checkout endpoint adopts it.
Atomic outcome
Create one dependency-free CommonJS library that:
- accepts one canonical lowercase UUID v4 commandId;
- accepts a server-derived caller scope and explicit environment;
- creates a caller-scoped command document key;
- creates a versioned payload fingerprint from the trusted command type and an already schema-validated, deeply frozen payload;
- creates a deterministic versioned Stripe idempotency key for one logical provider operation and immutable provider-attempt number.
No runtime endpoint imports the module in this child.
Identity contract
Command key
commandKeyHash = SHA-256(domain, version, environment, caller-scope-kind, caller-scope-value, commandId)
- The key deliberately does not include command type or provider operation.
- The same caller cannot reuse one command ID for another operation without PAY-002B2 seeing the same record and a different fingerprint.
- Environment separates local, test, staging, and production records.
- Caller scope comes from trusted server context: a verified Firebase UID, a server-issued anonymous principal, or a fixed internal-system principal.
- Email, phone, name, IP address, browser role, body-supplied membership state, and unverified provider identity are forbidden caller scopes.
- Caller scope is collision/idempotency identity only. It never authorizes the command.
Payload fingerprint
payloadFingerprint = SHA-256(domain, version, commandType, canonicalPayload)
- commandType is a trusted code constant using a bounded safe slug.
- The payload must already have passed its endpoint-specific PAY-001 schema.
- This helper independently verifies a deeply frozen, accessor-free, proxy-free, bounded JSON-like value with sorted object encoding and integer-only numbers.
- The helper does not make an unknown field valid and does not replace an endpoint schema.
- Object key order is irrelevant; array order remains meaningful.
Stripe key
A deterministic ASCII key derives from:
- contract version;
- expected Stripe mode (test or live);
- bounded provider operation;
- command key hash;
- positive bounded provider-attempt number.
The key contains no raw caller, command, aggregate, payload, provider object, or personal value and remains comfortably below Stripe's 255-character maximum.
A Firestore lease takeover must preserve the same provider-attempt number and Stripe key. PAY-002B2 may advance the provider attempt only after a separately proven safe recovery decision. An uncertain or lost Stripe response preserves the same key. PAY-002B2 may repeat the exact POST only inside its conservative stored safe-send window; after that deadline or when send time is unknown, it must stop POSTing and reconcile. The local journal remains authoritative after Stripe may prune a key.
Official constraints:
Exact source boundary
Create only:
- functions/commerceCommandIdentity.js
- functions/commerceCommandIdentity.test.js
Minimum exact design/status hunks only in:
- STRIPE_COMMERCE_DESIGN.md
- SYSTEM_DESIGN.md
- SECURITY.md
- IMPLEMENTATION_PLAN.md
- GITHUB_ISSUES.md
- GITHUB_ISSUE_SLICES.md
Post a scope amendment before editing any endpoint, webhook, Function export/index, Firestore/Rules file, package/lockfile, workflow, environment file, UI, officer guide, provider setting, or production data.
Security and correctness invariants
- No Firebase, Stripe SDK, network, filesystem, logger, clock, randomness, process environment, or provider import/call.
- Use domain-separated, length-framed hashing so concatenation cannot create ambiguity.
- Return recursively frozen fixed-shape values.
- Throw only fixed-message/fixed-reason safe errors; never echo an unknown value.
- Do not mutate or log inputs.
- Reject raw/mutable objects, custom prototypes, proxies, accessors, symbols, cycles, sparse arrays, non-finite numbers, fractions, negative zero, malformed Unicode, dangerous keys, and over-budget payloads without invoking hostile code.
- UUID input is canonical lowercase RFC 4122 variant UUID v4 only. Reject uppercase, padded, nil, other versions, and lookalikes.
- Provider-attempt is a bounded positive safe integer supplied by trusted journal state, never the browser.
- Digest/key outputs must not contain raw IDs, PII-like canaries, payload text, or secrets.
- A matching hash does not authorize a command and does not itself prove replay; PAY-002B2 compares the stored fingerprint and state.
- No source library enables commerce or proves Stripe/Firebase configuration.
Acceptance criteria
- Canonical UUID v4 validation has fixed safe failures.
- Command key is deterministic and changes when environment, caller scope kind/value, or command ID changes.
- Command key does not change with command type; the fingerprint does.
- Fingerprint is stable across object-key order and changes for array order, type, or any value.
- Stripe key is deterministic, safe-character-only, below 255 characters, and separated by mode, provider operation, command, and provider attempt.
- No output/error/log contains any raw caller, UUID, payload, or hostile canary.
- Payload verification is immutable, integer-only, bounded, and fails closed on hostile structures without invoking getters/proxy traps.
- All returned objects and nested values are frozen.
- Source-boundary tests prove no forbidden import or side effect.
- Node 20 focused/full Functions tests, Functions lint, frontend/SPA/Rules regressions, diff/sensitive-pattern review, and two independent exact-snapshot reviews pass.
- Root docs distinguish this pure contract from PAY-002B2 journal persistence, runtime adoption, deployment, provider configuration, and live behavior.
- No endpoint, webhook, Rule, package, workflow, environment, UI, officer, provider, or production setting changes.
Required tests
- Malformed, uppercase, nil, non-v4, wrong-variant, padded, and Unicode-lookalike UUIDs.
- Same tuple stability and one-dimension-at-a-time command-key separation.
- Cross-command-type same key plus different fingerprint.
- Equivalent object insertion orders, nested objects, significant array order, and integer boundaries.
- Mutable root/nested values, cycles, proxies, accessors, custom/polluted prototypes, symbols, sparse/extra-key arrays, malformed Unicode, dangerous keys, fractions, non-finite values, negative zero, and payload byte/depth/entry limits.
- Stripe key stability, mode/operation/command/attempt separation, bounded attempt validation, safe charset/length, and raw-value absence.
- Repeated calls, input descriptor snapshots, frozen results, no console/logger output, and static forbidden-import/global scans.
Excluded
- Firestore reads/writes, transactions, leases, fencing epochs, clocks, result replay, audit events, or emulator tests (PAY-002B2).
- Stripe calls or provider-object recovery.
- Endpoint schemas and adoption (PAY-001B/C/D and PAY-002C/D).
- Webhook, refund, capacity, inventory, mail, admin, rate-limit, confirmation-capability, or legacy migration work.
- Website/Firebase/provider deployment or production testing.
- Officer procedures because no officer action exists.
Claim protocol
Creation is not a claim. Before editing: confirm exact current main, assign the issue, post CLAIMED by /root at ; branch codex/issue--command-identity, apply status:in-progress, and use an isolated worktree.
Activity
daliu commentedon Jul 13, 2026
CLAIMED by /root at 2026-07-13T15:54:14Z; branch codex/issue-163-command-identity; exact base 68dbcae. Ownership is limited to the two new functions/commerceCommandIdentity files and the six root-document status/design hunks listed in the issue.
daliu commentedon Jul 13, 2026
Pre-implementation contract amendment — security review
This amendment supersedes any looser wording in the issue body. The file boundary does not change.
Stripe retry safety
Closed environment/provider pairing
Exact environments are local, test, staging, and production. Exact Stripe modes are test and live.
Identity, privacy, and encoding
Additional required tests
Add hard-coded golden digests, wrong environment/mode pairs, every UUID variant/form, nested hostile descriptors/proxies, prototype pollution, Date/Map/Set/Buffer/typed arrays/classes, shared aliases versus cycles, safe-integer boundaries, exact-byte Unicode distinction, fixed error serialization/inspection, and zero console calls.
Official references:
daliu commentedon Jul 13, 2026
EXTRACTION FINISHED by /root at 2026-07-13T16:18:24Z.
Exact staged boundary: the eight claimed paths only. Cached diff SHA-256:
180ef0213fc6e881a5b3a06d94c65a6566d069824e15e4989934a6c7c4e1defb.Exact source SHA-256:
9458adfba96da5218f20df005dd8a268755bfb3a10ffb58755b8cd9a57781af7.Exact test SHA-256:
db36cec470c6ddba4e8a80f9513cc7fb8319281a8ecde169f63dbeca74f624d1.Local Node 20 proof:
Independent exact-snapshot reviews: code APPROVED, adversarial security APPROVED after the revoked nested Proxy regression was fixed, and documentation/scope APPROVED after the Stripe safe-send cutoff and multi-tenant boundary were clarified.
This is source and local-test evidence only. No endpoint imports the library. No website, Firebase, Stripe provider, environment, or production behavior changed or was verified.
feat(commerce): add command identity contract (#163) (#164)
daliu commentedon Jul 13, 2026
COMPLETED by /root at 2026-07-13T16:24:06Z.
PR #164 was squash-merged to
mainas exact commit6c5d39ad3204c24928c11f194de9f3330198dc6d.Proof:
6a550ff61ece23000829a97creached ready for exact PR head3005fbbdb1191a6f6ac4b679286fdde24cdbde4f.Release truth:
6a55107e1ece2300082bb2cbiserrorand unpublished.runmprc.comreturned HTTP 200, but the latest ready Netlify production deploy remainse86a0f702cff6495f50630c5de3337290db8b8cb; this merge is not live there.This outcome is intentionally source-only. No endpoint imports the library, so no checkout/refund behavior changed. PAY-002B2 remains the required journal/lease/replay/reconciliation adoption slice.