Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save denniswon/93fa0bc44281a852465657442e75e85f to your computer and use it in GitHub Desktop.

Select an option

Save denniswon/93fa0bc44281a852465657442e75e85f to your computer and use it in GitHub Desktop.
Isaac - Lithic integration
Here's the design, in sections. The four decisions you made are baked in. Flagging upfront: exact Lithic ASA payload field names,
HMAC signature scheme, and the simulate endpoint path are marked [verify vs Lithic docs] — I'll confirm those against Lithic's API
during planning rather than guess them now.
---
1. Architecture overview
A new inbound webhook receiver POST /webhooks/lithic-asa is the card-rail analog of the x402 facilitator's executeIntent path. Lithic
calls it in real time for every card authorization; we decide APPROVE/DECLINE within the 2s SLA using the same Rego policy that
governs USDC spend, then fire an async Newton attestation for the crypto-economic audit trail.
Lithic card auth ──POST /webhooks/lithic-asa──▶ [HMAC verify (per-vault asa_webhook_secret)]
[resolve card_token → vault, agent]
[fetch agent spend from indexer]─┤ (synchronous, budgeted)
[LocalEvaluator: card-auth Rego input]
│ Allow→APPROVE, Deny/Queue→DECLINE
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
[respond ≤budget] [persist auth + latency] [emit auth.approved/declined]
[spawn async Newton attestation] ──▶ compare ──▶ on divergence:
record + emit auth.divergence
Trust model, stated honestly: the synchronous card decision is operator-local Rego (not BLS-attested), because card auth is
real-time. The async Newton attestation provides the crypto-economic backing after the fact — it can't reverse a cleared auth, but it
captures the rail in the audit trail and flags divergence.
---
2. D9.3 — ASA receiver (the core, NEWT-1329)
New module src/lithic/asa.rs (decision logic) + a thin handler wired via router() in routes/mod.rs. Steps:
1. Identify + authenticate — parse card token from the body, look up lithic_cards → vault_address →
vault_lithic_programs.asa_webhook_secret, decrypt (secret_crypto), verify HMAC over the raw body [verify vs Lithic docs: header name
+ scheme]. No vault match or bad sig → DECLINE (fail-closed).
2. Budgeted synchronous decision — wrap HMAC + indexer spend fetch + Rego eval in tokio::time::timeout(~1500ms). Any error or timeout
→ DECLINE. (Local eval is <10ms; indexer fetch ~50-100ms — comfortable.)
3. Outcome mapping — Allow→APPROVE; Deny→DECLINE; Queue→DECLINE (a real-time card auth can't wait for human approval — record the
queue reason but decline).
4. Persist + respond + emit — write the decision to a new lithic_card_auths table (audit + P95 source), respond to Lithic, emit
auth.approved/auth.declined via the existing D8 emitter.
5. Async attestation — tokio::spawn the real gateway createTask; compare to the local decision; on divergence persist + emit
auth.divergence. Off the synchronous path entirely.
6. Metrics — Prometheus histogram of decision latency for the P95-under-2s AC.
Does this section look right before I detail the policy adapter?
---
3. D9.4 — Rego card-auth adapter (NEWT-1330, policy-touching — care needed)
The existing agent_neobank.rego is shaped for ERC-20 transfer intents (decoded_function_arguments). A card auth is a different shape
(cardholder agent, merchant, MCC, amount). Proposed minimal, additive change: map a card auth onto the agent's existing per-token
policy — treat the card spend as a spend against the agent's max_daily_spend, with merchant/MCC checked against allowed_recipients
(reused as a merchant/MCC allowlist). Adds a card_auth_allow rule path keyed on a new input.kind == "card_auth" discriminant, leaving
the USDC path untouched. The adapter in Rust builds this input from the ASA payload. Exact param extension (new card block vs
reusing tokens) gets nailed in the spec — but the principle is: no breaking change to the USDC policy, smallest possible card branch.
Is reusing the per-agent token policy (vs a standalone card policy) the right call for the pilot?
---
4. D9.5 — Dashboard + demo (NEWT-1331)
- Dashboard: per-vault card list (existing lithic_cards) + an "auth stream" log (new lithic_card_auths) showing decision, reason,
latency, merchant. Follows the neo-industrial design system + existing table patterns. (One check: whether "Lithic" may appear in UI
text or must be functionally framed as "card issuing" — I'll confirm against the dashboard vendor-name rule.)
- CI E2E: synthetic ASA POST (valid HMAC) → receiver → verify → eval → respond → emit → audit assertion, with the gateway wiremocked
(mirrors existing facilitator integration tests; no external dependency).
- Recorded demo: Lithic sandbox simulate-authorization [verify vs Lithic docs: endpoint] for a real ASA round-trip.
---
5. Data model + testing
- New migration 014_lithic_card_auths.sql: lithic_card_auths (id, vault_address, card_token, agent_address, amount, merchant/MCC,
decision, reason, latency_ms, attestation_status, created_at). Must be idempotent + added to the hand-maintained run_migrations list
in db.rs (per the lessons-inbox migration trap).
- Webhook taxonomy: add auth.divergence to the event-type CHECK constraint (idempotent migration) — auth.approved/auth.declined are
already in the planned taxonomy.
- Tests: Rust units (HMAC verify, outcome mapping, fail-closed on timeout, divergence detection), Rego units (card-auth
allow/deny/over-limit), the wiremocked E2E.
---
Net scope: one new module (lithic/asa.rs), an additive Rego branch, one migration + one CHECK update, a dashboard surface, and the
test/demo split. Does this overall shape work, and do you want any section reworked before I write the spec?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment