Trust Model
SpendSafe provides trust in enforcement without handing over keys. This page explains current guarantees, verification process, and roadmap.
What You Can Rely On Today
- Non-custodial by construction – the SDK signs transactions locally with your keys. The SpendSafe API only receives intent metadata (chain, asset, amount, recipient, memo hash).
- Fail-closed enforcement – if Gate 1, Gate 2, or your wallet/RPC stack fails, the SDK throws an error and does not sign. There is no override or “force send” path.
- Cryptographic policy hashes – every asset rule in the dashboard has a SHA-256 hash. The API returns the hash used for the decision so the SDK can detect stale or tampered configuration.
- Decision signatures – Gate 1 responses include an HMAC signature over the policy hash, decision, timestamp, API key ID, and intent fingerprint. This creates a tamper-evident record that SpendSafe made the call.
- Audit logging – each decision/payment attempt is written to the dashboard audit trail with the same metadata the SDK sees (decision, counters, policy hash, signature, intent fingerprint).
Transaction Flow

Transaction flow: each transaction passes through two gates before signing locally with your private keys.
Verifying a Policy Decision
When you call PolicyWallet.send(...), Gate 1 responds with:
{
"decision": "allow",
"policyHash": "ba500a3fee18ba269cd...",
"signature": "hmac-hex...",
"signatureIssuedAt": "2025-02-14T12:34:56Z",
"apiKeyId": "api_key_123",
"intentFingerprint": "7c5bb4...",
"counters": {
"todaySpent": "25000000000000000",
"remainingDaily": "75000000000000000",
"remainingHourly": "50000000000000000",
"txCountLastHour": 3
},
"auth": "eyJhbGciOiJIUzI1NiIs..."
}
To verify the decision:
-
Compare policy hash
- Fetch the hash for the asset from the dashboard.
- If it differs from
policyHash, throwPOLICY_HASH_MISMATCH(the SDK does this automatically whenexpectedPolicyHashis set).
-
Check counters
- Use
todaySpent,remainingDaily,remainingHourly, etc., to diagnose the decline reason.
- Use
-
Store the proof
- Persist
{ policyHash, signature, signatureIssuedAt, apiKeyId, intentFingerprint }alongside your transaction log. This lets you reconcile dashboard entries later.
- Persist
-
Call Gate 2
- The SDK automatically posts
authandintentFingerprinttoPOST /verify-authorisation. Gate 2 marks the token as consumed and ensures the fingerprint matches the original intent.
- The SDK automatically posts
⚠️ Current Limitation: Decision signatures use HMAC with SpendSafe's secret key. You cannot independently verify these signatures without access to this key—you must trust that SpendSafe generated them. We plan to replace HMAC with EdDSA and expose a public verification key so you can validate proofs independently. Until then, rely on policy hash comparison and audit logs for integrity checks.
Intent Fingerprints
SpendSafe normalises the intent before hashing:
const canonical = {
chain: intent.chain.toLowerCase(),
asset: intent.asset.toLowerCase(),
to: intent.to.toLowerCase(),
amount: intent.amount,
memoHash: intent.memo ? sha256(intent.memo) : undefined,
nonce: intent.nonce
};
const fingerprint = sha256(JSON.stringify(canonical));
Gate 1 and Gate 2 both compute this fingerprint. If the payload changes between gates (e.g. an attacker swaps the recipient), Gate 2 rejects the token with AUTH_INVALID.
Why You Don’t Need the Raw Policy
- Customers manage rules exclusively through the dashboard (daily limits, per-transaction caps, whitelists, frequency controls).
- SpendSafe converts those settings to canonical JSON, hashes them, and uses the hash as the enforcement “fingerprint”.
- By comparing hashes before signing, you know the API enforced the same rules you set—even though the raw JSON stays on SpendSafe’s side.
What’s on the Roadmap
- Public verification keys – replace HMAC with EdDSA so you can verify signatures offline.
- Customer-exportable proofs – downloadable proof bundles (hash + signature + counters) for audit/archival.
- Additional denial states – “review” workflows that pause a transaction until a human approves it.
- Expanded telemetry – more granular counters (weekly/monthly) and webhook alerts.
- Merkle-anchored audit proofs – aggregate decision proofs into Merkle roots for tamper-evident reconciliation.
We’ll update this page as each capability ships.
Summary
Even though the policy engine is hosted, SpendSafe provides the hooks you need to trust every decision:
- Compare policy hashes to detect stale configuration.
- Rely on fail-closed behavior—no signature means no spend.
- Capture decision proofs for reconciliation.
- Monitor the dashboard audit log for the same events your SDK sees.
Together, these controls let you integrate a closed policy service with confidence, without ever giving up custody of your keys.