Agent Trust Standard
ATS-1
Open draft for portable agent trust. One signed receipt per verified action. Any verifier with the issuer's public key validates offline — like JWT for authentication, but for execution proof.
Motivation
Agents move money at machine speed. Humans cannot audit every payload before execution. An attacker who swaps a destination after review — or accumulates approvals to an unknown counterparty — drains funds before monitoring reacts.
Attack
Payload swap (Bybit class)
- 1.UI shows address A
- 2.Execution broadcasts address B
- 3.Signers never see B
✗ Funds gone before monitoring alerts
Stvor
Commitment at intent
- 1.Hash committed before channel interaction
- 2.Verify with timingSafeEqual before rail
- 3.Mismatch → DENY, no capture
✓ Execution blocked at source
ATS-1 defines the minimum portable substrate: signed receipt per action, canonical payload hashing, and a verification gate any marketplace or custody stack can implement without trusting Stvor's servers.
TrustReceipt schema
A TrustReceipt is JSON. Implementations MAY add fields; verifiers MUST ignore unknown fields.
interface TrustReceipt {
ats_version: "0.1.0-draft";
receipt_id: string; // unique, issuer-scoped
agent_id: string;
action: string; // e.g. payment.execute
decision: "ALLOWED" | "DENIED";
reason?: string; // required when DENIED
checks_passed: Array<
"destination" | "payload" | "trust" | "policy"
>;
destination?: string;
amount?: string;
payload_hash: string; // sha256 hex
committed_at: string; // ISO 8601
executed_at?: string; // omitted when DENIED
escrow_status?: "OPEN" | "FUNDED" | "VERIFIED" | "COMPLETE" | "CANCELLED";
signature: string; // ECDSA P-256 over canonical body
}Signing requirements
Implementations MUST sign with ECDSA P-256 (secp256r1) over SHA-256 of the canonical payload. The signature MUST cover required fields in stable JSON order — no signature field in the signed body.
GET /.well-known/ats1-public-key
{
"publicKeyB64": "...",
"algorithm": "EC P-256",
"format": "SPKI DER"
}Verification gate
Before any execution rail call, run all four checks:
- Destination match against commitment
- Payload hash match via timingSafeEqual
- Counterparty trust ≥ minimum threshold
- Policy evaluation pass
Any failure → decision: DENIED, execution rail MUST NOT be called, receipt SHOULD still be issued for audit.
Escrow lifecycle
When a payment rail supports hold/capture semantics (Stripe manual capture in the reference impl), contracts SHOULD follow:
CANCELLED is terminal on DENY — funds return to buyer. COMPLETE requires VERIFIED attestation first.
Offline verification
import crypto from "node:crypto";
function verifyReceipt(
receipt: Omit<TrustReceipt, "signature">,
signatureB64: string,
publicKeySpki: Buffer,
) {
const body = canonicalJson(receipt);
const digest = crypto.createHash("sha256").update(body).digest();
return crypto.verify(
"sha256",
digest,
{ key: publicKeySpki, dsaEncoding: "ieee-p1363" },
Buffer.from(signatureB64, "base64"),
);
}Compatibility
HTTP agents
Agents with a webhook endpoint can participate without an SDK. Register, receive tasks, return deliverables — commitment hash is enforced at the marketplace boundary.
Stripe
ATS-1 reference uses capture_method: manual. Any processor with authorize/hold/capture semantics qualifies.
Stvor transport (optional)
Separate from ATS-1: PQC transport (ML-KEM-768 + ECDH P-256) for E2EE agent channels. Optional extension — not required for receipt verification.