Review the security model
The wrapper assumes provider responses, browser input, network transport, and caller-supplied identifiers can be hostile or incomplete. It fails closed at identity, origin, amount, resource ownership, and state-transition boundaries.Separate caller authority
Customer routes require customer sessions. Operator routes can view operations and mandates and can suspend a mandate, but cannot move money. Agent execution requires both an OAuth scope and an active customer mandate bound to the same client identifier. Internal rules must match an approved rule revision. Cookie-authenticated mutations require the exact application origin and the session cross-site request forgery token. Sensitive customer actions also consume a short-lived confirmation intent bound to the normalized payload.Encrypt provider authority
Vault Transit encrypts Perflo access and refresh credentials, beneficiary destinations and chains, browser approval sessions and URLs, and provider preparation context. Derived-key contexts bind connection ciphertext to the exact customer and provider-binding generation, and bind beneficiary and operation ciphertext to the exact customer and row ID. Moving ciphertext between rows or connection generations therefore fails decryption. Request and audit JSON contains only safe identifiers, fingerprints, normalized amounts, and policy values. The public API never returns raw provider credentials, primary account numbers, card verification values, raw reveal tokens, token addresses, networks, or unmasked bank identifiers. Operators do not receive customer approval URLs.Prevent duplicate writes
The API stores the operation, idempotency record, and unique outbox event before provider submission. Equal replays return the same operation; another body with the same key returns a stable conflict. Each idempotency key is honoured for twenty-four hours from first acceptance, and a replayed acceptance carries the response headerIdempotent-Replayed: true — sent only on a replay, never as false. A key presented after its window is refused with 409 and code idempotency_key_expired and is never executed: once the window lapsed the API cannot promise “no duplicate”, and executing would risk a second transfer. Between expiry and the thirty-day record retention that refusal is deterministic; pruning removes only the idempotency record and touches no operation row.
The worker commits submitting before a provider money or authority write. A timeout, reset, possible-acceptance server error, invalid accepted response, or restart while submitting becomes indeterminate. The worker never resubmits an indeterminate write.