Proof, not promises. An open, vendor-neutral way to sign, publish, and independently verify exactly what an AI system was told the moment it acted — Ed25519 over a canonical envelope, keys published as JWKS, optional Bitcoin timestamping. Free to implement. Verifiable by anyone, in any language, even against us.
When an AI only writes text, a wrong answer is a bad sentence. When an AI acts — a robot moves, a trade fires, a filing is submitted — you need a tamper-evident record of exactly what it was told at the moment it acted, and you need it to be checkable by a third party who doesn't trust the source. "Trust me" is not an audit trail.
DF-VERIFY/1 defines a minimal way to attach a cryptographic signature to any JSON data response, publish the verifying key openly, and optionally timestamp the record on a public blockchain. It is the grounding-and-audit layer for agentic and embodied AI — and it is deliberately small, open, and implementable in a few lines on any platform.
The keywords MUST, SHOULD and MAY are used per RFC 2119.
A signed response is any JSON object that carries a top-level signature block. The signature is detached: it covers the entire object except its own signature field.
{
"snapshot_id": "1644aefdefbb43e7b540b8716b526e78",
"issued_at": "2026-06-09T20:47:52Z",
"issuer": "dynamicfeed.ai",
"schema": "awareness/v1",
"... any payload fields ...": "...",
"signature": {
"alg": "Ed25519",
"key_id": "df-ed25519-4cb32e72f333",
"canonicalization": "json-sorted-compact",
"sig": "base64url( Ed25519 signature, 64 bytes )"
}
}
The signature block MUST contain alg, key_id, canonicalization and sig. sig is the base64url (unpadded-tolerant) encoding of the raw 64-byte Ed25519 signature. key_id binds the signature to one published key (see §5), so keys can rotate without breaking old records.
json-sorted-compactTo get the exact bytes that were signed, a verifier MUST: (a) remove the signature field from the object; (b) serialize the remainder as JSON with all object keys sorted recursively, compact separators (, between items and : between key and value, no spaces), and non-ASCII escaped as \uXXXX (astral characters as UTF-16 surrogate pairs); (c) encode as UTF-8.
This is exactly Python's:
import json
canonical = json.dumps(payload_without_signature,
sort_keys=True,
separators=(",", ":")).encode("utf-8")
Numbers are emitted verbatim as they appear in the source JSON — a conformant verifier MUST NOT reparse them through a lossy float, or re-canonicalization will not match byte-for-byte.
signature block; keep the rest as the payload.GET /.well-known/keys (§5). Look up signature.key_id.Ed25519( sig, canonical_bytes, public_key ). If it passes, the record is authentic and unaltered. Change a single byte of the payload and it fails.# 1) fetch a signed verdict (keyless)
curl -X POST https://dynamicfeed.ai/v1/awareness -H "Content-Type: application/json" \
-d '{"robot":{"class":"aerial"},"location":{"lat":51.5,"lon":-0.12}}'
# 2) fetch the public key, then verify the Ed25519 signature over the canonical payload
curl https://dynamicfeed.ai/.well-known/keys
Reference implementations (all share this exact canonical form, byte-for-byte):
Python — scripts/verify_awareness.py; in-browser JavaScript — /verify.js (powers the live check at /proof); C# — clients/csharp/AwarenessClient.cs.
GET /.well-known/keys returns a map of key_id → base64url-encoded raw 32-byte Ed25519 public key. A verifier MUST select the key whose id matches the signature's key_id; if no such id is present (e.g. the key has rotated), verification MUST fail closed.
GET /.well-known/keys →
{ "df-ed25519-4cb32e72f333": "base64url( 32-byte Ed25519 public key )" }
Issuers SHOULD use a stable, long-lived signing key so historical records keep verifying. (An issuer with no configured key MAY sign with an ephemeral key for demos; such signatures carry an ephemeral_key flag and will not verify after a restart.)
An issuer's capabilities — keys, profiles, endpoints, reference verifiers, conformance vectors — are machine-discoverable at /.well-known/df-verify.json.
A signature proves who said it and that it is unaltered. To also prove when — independently, against the issuer — a record's hash MAY be committed to the Bitcoin blockchain via OpenTimestamps.
POST /v1/anchor { "snapshot": { ...the signed record... } } →
{ "chain": "bitcoin", "status": "pending",
"digest_sha256": "…", "proof_ots_b64": "…" }
Only the SHA-256 of the canonical record goes on-chain — never the data itself. Once confirmed (~one block), the .ots proof shows the record existed no later than that block, verifiable with any standard OpenTimestamps client. It is free, wallet-less, and never on the request hot path.
The envelope (§2–§6) is generic. A profile fixes the payload shape for a use case; the schema field names it.
awareness/v1 — a go / caution / no-go verdict for an acting system{ "schema": "awareness/v1", "issuer": "dynamicfeed.ai",
"snapshot_id": "…", "issued_at": "…Z",
"robot": { "class": "aerial" }, "location": { "lat": 51.5, "lon": -0.12 },
"verdict": { "status": "caution", "advisory": "…" },
"facts": [ { "id": "wx.wind_kmh", "value": 21, "unit": "km/h",
"source": "Open-Meteo", "age_s": 240, "stale": false } ],
"degraded": false,
"signature": { "alg": "Ed25519", "key_id": "…", "canonicalization": "json-sorted-compact", "sig": "…" } }
A verdict MUST NOT be go when degraded is true — missing or stale safety inputs floor it to caution. A physical system never gets a falsely-confident answer.
Machine-readable schema: /schemas/awareness-v1.json (JSON Schema 2020-12; the degraded → not go safety floor is encoded as an if/then constraint, so a conformant validator rejects a degraded go).
POST /v1/receipt with {"claim":"…"} (or {"data":…}) returns a signed receipt/v1 envelope — provable evidence of what an AI was told or asserted, and when. It is the audit artifact a regulated workflow keeps, and it verifies with the exact same procedure as §4 (drop signature, canonicalize, check the Ed25519 signature).
{ "schema": "receipt/v1", "issuer": "dynamicfeed.ai",
"issued_at": "2026-06-09T20:47:52Z",
"data": { "claim": "unit price was 19 USD at quote time" },
"signature": { "alg": "Ed25519", "key_id": "…", "canonicalization": "json-sorted-compact", "sig": "…" } }
Machine-readable schema: /schemas/receipt-v1.json. Anchor a receipt's hash to Bitcoin via POST /v1/anchor for independent proof of when.
Grounded receipts. Because data is arbitrary JSON, a receipt can carry not just the claim but the evidence behind it — the live datapoints, each with source, licence and timestamp, that the AI was told when it acted. That is the artifact a regulated workflow keeps: not "the model said $19," but "the model said $19 because these signed sources reported it at time T" — a tamper-evident, independently-checkable record of the grounding behind a decision.
Every value Dynamic Feed serves carries source, licence, measured_at and age_seconds, so a grounded claim can be traced to its publisher and its freshness checked.
Parametric catastrophe policies pay out when a measured trigger is crossed — wind above X, rainfall above Y, quake above M. The recurring friction is the settlement dispute: was that the official reading, at the right place and time, unaltered, and judged by a neutral party? GET /v1/trigger-receipt answers it with a signed trigger-receipt/v1 envelope built as the neutral calculation-agent-of-record — Dynamic Feed underwrites nothing, so its determination is structurally independent.
{ "schema": "trigger-receipt/v1", "issuer": "dynamicfeed.ai", "peril": "weather",
"trigger_definition": { "metric": "wind_kmh", "operator": "gte", "threshold": 150,
"source_of_authority": "Open-Meteo (open-meteo.com)",
"methodology": "Trigger MET iff observed wind_kmh ≥ 150 km/h …",
"methodology_hash": "sha256:…" },
"observation": { "measured_value": 162.4, "unit": "km/h", "source": "Open-Meteo",
"source_url": "https://api.open-meteo.com/v1/forecast?…",
"raw_reading_hash": "sha256:…" },
"determination": { "triggered": true, "payout_factor": 1.0, "rationale": "…" },
"signature": { "alg": "Ed25519", "key_id": "…", "canonicalization": "json-sorted-compact", "sig": "…" } }
Four properties make it dispute-proof, and each is independently checkable: (1) a methodology_hash pins the determination function so nobody can move the goalposts after the event; (2) a re-fetchable source_url + a raw_reading_hash — with the exact, reproducible hashing recipe named in the receipt (hash_method), so per-request timing noise can't break reproduction — let any party re-pull the official source and confirm the reading was not altered: verifiability, not "trust us"; (3) the determination is a deterministic function of the pinned methodology and the reading, so any party re-computes the same triggered + payout_factor; (4) the Ed25519 signature (anchorable to Bitcoin via POST /v1/anchor) proves the receipt existed at time T and that no byte was changed.
Scope. The receipt attests that the cited official source reported a reading at a time, and what the pinned methodology implies from it. It does not attest to ground-truth physical loss — that boundary is what keeps the role neutral and the determination reproducible. Machine-readable schema: /schemas/trigger-receipt-v1.json. v0.1 covers weather metrics (wind, temperature, precipitation); location-resolved intensity layers for more perils are rolling in.
A parametric trigger needs the intensity of the parameter at the insured coordinate, not just that an event occurred. GET /v1/intensity?peril=&lat=&lon= returns that point reading as a signed intensity/v1 envelope — value + unit, the resolution method (interpolation_method), a latency tier (detection / provisional / operational / authoritative), and a re-fetchable source_url + reproducible raw_reading_hash. v1 covers flood (nearest USGS NWIS stream gauge — gage height / discharge, point-resolved, no interpolation); drought, wildfire and earthquake (ShakeMap, with a kept-forever version archive) intensity are rolling in. Schema: /schemas/intensity-v1.json. It is the reading layer beneath trigger-receipt/v1.
A response is DF-VERIFY/1 conformant if its signature block verifies against the published key under §3–§5. "Verified by Dynamic Feed" means exactly that — checked client-side, against the open key, with the open reference verifier. The standard is free to implement and imposes no dependency on Dynamic Feed: anyone can issue, and anyone can verify, including against us. That independence is the point.
Conformance vectors. The repo ships language-agnostic test vectors at tests/vectors/ — canonicalization cases (recursive key sorting, ensure_ascii \uXXXX escaping including astral surrogate pairs, and signature-stripping) plus a real signed envelope and a tampered twin. A conformant verifier reproduces every canonical byte, accepts the authentic record, and rejects the tampered one. Run python3 tests/verify_vectors.py to check an implementation; the Python and JavaScript reference verifiers both pass byte-for-byte.
This is DF-VERIFY/1, the live form that signer and all reference verifiers share today. The canonicalization field names the algorithm so future forms are unambiguous; a strict RFC 8785 (JCS) profile is a planned refinement and will ship under a new canonicalization name, leaving existing records verifiable. Profiles are versioned by their schema string (e.g. awareness/v1).
The entire verifier is a dozen lines. Copy this — it needs only a standard Ed25519 library and works against any DF-VERIFY/1 issuer, including us:
import base64, json, urllib.request
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
def b64(s): return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
def verify(env, base="https://dynamicfeed.ai"):
sig = env["signature"]
payload = {k: v for k, v in env.items() if k != "signature"}
canon = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()
jwks = json.load(urllib.request.urlopen(base + "/.well-known/keys"))
Ed25519PublicKey.from_public_bytes(b64(jwks[sig["key_id"]])).verify(b64(sig["sig"]), canon)
return True # raises on any tampering, or if the signing key isn't published
Or don't write it at all. Clone the runnable example agent — it grounds on a live feed and refuses to act on data it can't verify — and have it running in under five minutes:
git clone https://github.com/dynamicfeed/df-verify
cd df-verify/examples/verified-agent
pip install cryptography
python agent.py # verify a live verdict, then act
python agent.py --tamper # altered after signing -> the agent refuses to act
Reference implementations in the repo: clients/python (the dynamicfeed-verify package), examples/verified-agent (the demo above), in-browser /verify.js, and C# clients/csharp — all sharing the §3 canonical form byte-for-byte.
If your product grounds on DF-VERIFY and checks signatures before it acts, show it. This badge is self-contained (inline styles, no script) and links back to this standard — paste it anywhere:
<a href="https://dynamicfeed.ai/standard"
style="display:inline-flex;align-items:center;gap:7px;text-decoration:none;
font:600 13px/1 ui-sans-serif,system-ui,sans-serif;color:#cfe9ff;background:#0a0e16;
border:1px solid #1c2740;border-radius:999px;padding:8px 14px">
<span style="color:#46e6da">✓</span> Verified by Dynamic Feed
</a>
Renders as: ✓ Verified by Dynamic Feed
For a README or repo, use the SVG badge:
[](https://dynamicfeed.ai/standard)