Every OFAC verdict is signed — and now you can verify it

2026-06-12 · Engineering ed25519 signing agent-security

Every PaladinFi OFAC trust-check response is Ed25519-signed, and as of this week you can verify the signature yourself — with a key you pin from our docs, not one you take on faith.

Why it matters: when an autonomous agent asks "is this wallet sanctioned?" and routes a payment on the answer, the verdict is load-bearing. If anything in the path rewrites a block into an allow — a misconfigured proxy, a caching layer, a compromised RPC — your agent acts on a corrupted verdict and never knows. The signature is how it catches that.

What shipped

Every response from the free OFAC endpoint (POST /v1/trust-check/ofac) carries an Ed25519 signature over the response body. We published:

Here's the minimal core in Python — pin the key, strip exactly the three signature fields, verify the rest:

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
import json, base64

PUBKEY = "272b6b62230d9da810f3ed64b5e5147f1ae062cb46ad7f25044b0aab1d18fb6f"  # pin from the docs
SIG_FIELDS = ("_signature", "_signature_alg", "_signature_pubkey_hex")

assert body["_signature_alg"] == "ed25519"
assert body["_signature_pubkey_hex"] == PUBKEY                  # in-band key is convenience only
signed = {k: v for k, v in body.items() if k not in SIG_FIELDS}  # strip EXACTLY these three
msg = json.dumps(signed, sort_keys=True, separators=(",", ":")).encode()
Ed25519PublicKey.from_public_bytes(bytes.fromhex(PUBKEY)).verify(
    base64.b64decode(body["_signature"]), msg)                 # raises if tampered

Two things the docs make load-bearing, and you should too: strip exactly those three fields (not by prefix — anything else must stay in the signed message so injected fields fail the check), and sort keys recursively. If you verify in JavaScript, note that JSON.stringify does not sort nested keys — use a JCS / canonical-JSON library; a naive stringify will silently fail to verify. The docs carry the full hardened verifier (error handling, multi-key rotation) in both languages.

What signing does — and what it doesn't

We'd rather scope this honestly than oversell it — a trust vendor that overstates its own guarantees is exactly the thing you shouldn't trust.

It gives you: detection of tampering of the signed response body, in transit between us and you. Pin the key from our docs — a different host than the API — and a forging origin can't hand you a matching key with a forged body. This matters most when our responses pass through infrastructure you don't fully control: an agent runtime, a shared gateway, a caching proxy.

It does not give you: protection against us being compromised (we sign and serve, so an attacker who fully controlled our origin could rotate both), and it is not anti-replay on its own — a validly-signed older response stays valid, so a replayed allow for a wallet that was SDN-listed an hour ago still verifies. Make a fresh request per decision if you need freshness; don't treat a forwarded signed response as proof. (If we rotate the signing key, the new key is published on the same docs page with an overlap window.)

Why publish the contract at all

Because "we sign our responses" is unverifiable marketing until you can reproduce the check. A key that only lives inside the response payload isn't an out-of-band anchor — so we put the key and the canonical-JSON contract on a page you pin from, and a verifier you can run today.

Try it: the OFAC screen is free and anonymous — one POST, no key — at swap.paladinfi.com/v1/trust-check/ofac. Verify the signature with the docs. If you're wiring trust checks into agent infrastructure and want a hand, that's what PaladinFi does: paladinfi.com/build.

Published: 2026-06-12 · Verify-responses docs: paladinfi.com/docs/verify-responses/