Skip to content

feat(fabricate): Phase 1 money-plane schema + atomic wallet/authz primitives - #308

Merged
ecto merged 1 commit into
mainfrom
claude/fabricate-money-plane
Jun 19, 2026
Merged

ecto merged 1 commit into
mainfrom
claude/fabricate-money-plane

Conversation

@ecto

@ecto ecto commented Jun 19, 2026

Copy link
Copy Markdown
Owner

What

The first slice of the post-quote money plane — durable, revocable primitives only. Nothing spends yet: place_order / authorize_spend / Stripe land in the next slice (test-mode + flag-gated). This migration encodes the three guardrails the adversarial review flagged as non-negotiable.

Migration 027

  • wallets (cached balance mirror) · wallet_ledger (append-only source of truth; per-user idempotency key) · spend_authorizations (DB-backed, full lifecycle pending_human → authorized → consumed/revoked/expired + kill switch) · processed_events (durable webhook idempotency — replaces the in-memory Map).
  • orders gains authorization_id / idempotency_key / stripe_payment_intent_id + a one-order-per-quote unique index.
  • debit_wallet() — one atomic, balance-floored txn, advisory-lock serialized on (user, key) so concurrent same-key retries replay cleanly instead of raising unique_violation. Enforces every authz field: max amount, per-authz daily cap, process/fab allowlist, doc_hash binding, quote binding, expiry, ownership. Consumes one_time authzs; standing budgets persist.
  • credit_wallet() (top-ups/refunds; refund requires an order), revoke_user_authorizations() (kill switch), fn_wallet_drift() (reconciliation: cached vs sum(ledger)).
  • Defense in depth: assert_money_caller() rejects authenticated/anon callers; wallets+wallet_ledger grant SELECT-only to service_role so the SECURITY DEFINER RPCs are the sole writers; a per-call $1M ceiling.

Adversarial review → fixes

A 5-lens review (double-spend, replay, authz-bypass, balance-integrity, Postgres-correctness) found the core sound but flagged: concurrent-same-key unique_violation, globally-unique key + cross-user replay, dead authz fields (daily_cap/allowlists/doc_hash), missing o.user_id check, non-re-runnable DDL, no reconciliation. All addressed.

Validation

scripts/money-plane/validate.sh spins an ephemeral Postgres 16, applies 027 twice (re-runnability), and runs the behavior matrix:

  • idempotent replay (debit + credit), reused-key-different-amount rejection
  • one-time consume; consumed/expired/revoked/wrong-user/over-max rejections
  • insufficient funds, per-authz daily cap, fab + process allowlists, doc-hash binding
  • refund-requires-order, zero reconciliation drift, service-role guard blocks an authenticated caller

→ ALL LOGIC TESTS PASSED · SERVICE-ROLE GUARD PASSED.

Deploy

Not applied to prod. Unlike 024–026, this is money infra — it stays unapplied until the full Phase-1 slice (authorize_spend, place_order, Stripe test-mode) is built, reviewed, and flag-gated. No changelog entry (no user-facing behavior yet).

🤖 Generated with Claude Code

…mitives

Lands the durable, revocable money primitives the adversarial review flagged as
must-fix — schema only, NOTHING spends yet (place_order / authorize_spend /
Stripe land later, test-mode + flag-gated).

Migration 027:
- wallets (cached mirror) · wallet_ledger (append-only source of truth,
  per-user idempotency key) · spend_authorizations (DB-backed, full lifecycle +
  kill switch) · processed_events (durable webhook idempotency). Orders gains
  authorization_id / idempotency_key / stripe_payment_intent_id + a one-order-
  per-quote unique index.
- debit_wallet(): one atomic, balance-floored txn that is advisory-lock
  serialized on (user, key) so concurrent same-key retries replay cleanly
  (no unhandled unique_violation), enforces EVERY authz field (max amount,
  per-authz daily cap, process/fab allowlist, doc_hash, quote binding, expiry,
  ownership), and consumes one_time authorizations (standing budgets persist).
- credit_wallet() (top-ups/refunds, refund requires order),
  revoke_user_authorizations() (kill switch), fn_wallet_drift() (reconciliation).
- Defense in depth: assert_money_caller() rejects authenticated/anon callers;
  wallets+ledger grant SELECT-only to service_role so the SECURITY DEFINER RPCs
  are the sole writers; per-call $1M ceiling.

Addresses every consensus finding from the 5-lens review (idempotency race,
cross-user key, dead authz fields, missing ownership check, re-runnable DDL,
no reconciliation).

Validated against ephemeral Postgres 16 (scripts/money-plane/validate.sh):
applies twice cleanly + full behavior matrix passes (idempotent replay, all
authz-failure modes, insufficient funds, per-authz daily cap, allowlists,
doc-hash binding, refund rules, zero drift, service-role guard).

NOT applied to prod — money infra holds until the full Phase-1 slice is built,
reviewed, and flag-gated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

4 Skipped Deployments
Project Deployment Actions Updated (UTC)
mecheval Ignored Ignored Jun 19, 2026 1:42am
vcad Ignored Ignored Jun 19, 2026 1:42am
vcad-docs Ignored Ignored Jun 19, 2026 1:42am
vcad-mcp Ignored Ignored Jun 19, 2026 1:42am

Request Review

@ecto
ecto merged commit 0d30029 into main Jun 19, 2026
12 checks passed
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