Every OFAC verdict is signed — and now you can verify it
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:
- the public key, on a page you pin it from (paladinfi.com/docs/verify-responses/);
- the exact canonical-JSON contract the signature is computed over; and
- copy-paste verifiers in Python and JavaScript, both tested against the live endpoint.
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/