Public lookup over the signed model identity registry. Records are returned as canonical JWS values; clients verify locally against the published JWKS. No accounts. No keys to obtain. No request-time signing.
The API is a propagation layer. The signed static registry at attest.fallrisk.ai/registry.json and the JWKS at attest.fallrisk.ai/.well-known/jwks.json are the trust root.
The API does not create trust. It serves signed claims that already exist on the authority surface, and it fails closed when the manifest signature cannot be verified.
https://api.attest.fallrisk.ai/v1/https://attest.fallrisk.aifallrisk-96cd5e6a01e1The API is not the authority. It is the window.
{"status": "ok"}.
Compute the SHA-256 of an artifact (a model shard, typically a .safetensors or .gguf file) and look it up:
$ curl -s "https://api.attest.fallrisk.ai/v1/verify/hash/\
b477be7572f0ab3ae3cbba38d508cc33e70600b2045669c4ad848051c3432094" | jq
{
"sha256": "b477be7572f0ab3ae3cbba38d508cc33e70600b2045669c4ad848051c3432094",
"status": "verified",
"record_jws": "eyJraWQiOiJmYWxscmlzay05NmNkNWU2YTAxZTEi...",
"record": { ... decoded JWS payload ... },
"registry_snapshot_at": "2026-05-02T05:43:28+00:00",
"registry_manifest_digest": "0568fe38fc3fb4801b016450d23d2fce963f523204eb105db59fa4755ff13846",
"registry_kid": "fallrisk-96cd5e6a01e1"
}
Example value as of May 3, 2026 (registry v0.2.3, 211 records). The live value is the response of GET /v1/registry/manifest_digest. The format is locked at 64-character lowercase raw hex with no sha256: prefix.
The decoded record field is convenience data. The authoritative value is record_jws. Always verify the JWS locally against the published JWKS before trusting the result.
| Value | Meaning |
|---|---|
verified | Hash matches a signed enrollment record. |
not_enrolled | Hash is not in the registry (HTTP 404). |
revoked | Hash matched a record that has since been revoked (HTTP 410). |
For verifying an entire model directory in one request:
$ curl -s -X POST https://api.attest.fallrisk.ai/v1/verify/manifest \
-H "Content-Type: application/json" \
-d '{
"hashes": [
{"sha256": "b477be7572f0ab3ae3cbba38d508cc33e70600b2045669c4ad848051c3432094"},
{"sha256": "eb356aacae443e30f52712b1e98fadf206976365e2f5ee886321b0bb38c7cea8"}
],
"client": {"name": "my-tool", "version": "1.0"}
}' | jq
Per-hash results are returned in the same order as the input. Each result carries the same status and record_jws fields as the single-hash endpoint. Maximum 1,000 hashes per request.
The published JWKS contains a single RSA public key. Pin the kid in operational configuration to prevent silent issuer rotation.
| Field | Value |
|---|---|
| JWKS URL | attest.fallrisk.ai/.well-known/jwks.json |
| kid | fallrisk-96cd5e6a01e1 |
| kty | RSA |
| alg | RS256 |
| Key fingerprint | sha256:FlqonYOsEwXi5eaLuhjMKmHzbKxtM0MrM7yGg2xW-2M |
| Fingerprint method | rfc7638-sha256 (JWK Thumbprint, RFC 7638) |
The fingerprint is reproducible by any compliant JWK library. Reference implementation:
import json, hashlib, base64, urllib.request
with urllib.request.urlopen('https://attest.fallrisk.ai/.well-known/jwks.json') as r:
keys = json.load(r)['keys']
key = next(k for k in keys if k['kid'] == 'fallrisk-96cd5e6a01e1')
canonical = json.dumps(
{'e': key['e'], 'kty': 'RSA', 'n': key['n']},
sort_keys=True, separators=(',', ':')
)
fp = base64.urlsafe_b64encode(hashlib.sha256(canonical.encode()).digest()).rstrip(b'=')
print(f"sha256:{fp.decode()}")
# sha256:FlqonYOsEwXi5eaLuhjMKmHzbKxtM0MrM7yGg2xW-2M
End-to-end verification of a returned record:
import json, urllib.request
from jose import jws
# 1. Fetch a verified record from the API.
with urllib.request.urlopen(
'https://api.attest.fallrisk.ai/v1/verify/hash/'
'b477be7572f0ab3ae3cbba38d508cc33e70600b2045669c4ad848051c3432094'
) as r:
response = json.load(r)
token = response['record_jws']
# 2. Fetch the published JWKS.
with urllib.request.urlopen('https://attest.fallrisk.ai/.well-known/jwks.json') as r:
jwks = json.load(r)
# 3. Verify the JWS. Raises on signature mismatch or algorithm substitution.
payload = jws.verify(token, jwks, algorithms=['RS256'])
print(json.loads(payload))
Per source IP. All limits use a one-minute rolling window.
| Endpoint | Limit |
|---|---|
GET /v1/verify/hash/{sha256} | 300 req/min |
POST /v1/verify/manifest | 60 req/min |
| Batch size | 1,000 hashes per request |
| Global circuit breaker | 5,000 req/min across all sources |
Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. Retry-After is added on 429.
Individual hash queries are not retained. Specifically:
/v1/verify/hash/[redacted], not the hash itself.POST /v1/verify/manifest, are accepted but not logged.Standard nginx access logs (with redaction applied) are retained 30 days.
Every response carries the registry_manifest_digest of the signed registry the API is serving. Pin that digest in your client. Compare against the value at:
GET /v1/registry/manifest_digest
That endpoint returns both the digest and a manifest_signature. Verify the signature against the published JWKS — do not trust the digest alone. For the full static-registry check, compare against registry.json → manifest.manifest_digest at attest.fallrisk.ai/registry.json; the static registry is the authority, the manifest endpoint is the convenience proof surface.
If any of the three values disagree, the API has drifted. Treat any manifest digest mismatch as fail-closed.
For independent verification of the registry itself — reproducing the trust path from the published JWKS to a single signed enrollment record without trusting Fall Risk's word for it — see the verification guide.
This API answers one question: is this artifact known and signed against the public registry. That is artifact verification.
It does not perform runtime structural verification. Runtime structural verification — measuring a running model against an enrolled identity anchor and producing a fresh signed JWT — is Trustfall Deep, an enrollment-based service.
Trustfall Lite verifies what model artifact you have.
Trustfall Deep verifies which model is actually computing.
For Deep: integrations@fallrisk.ai.