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.
https://api.attest.fallrisk.ai/v1/https://attest.fallrisk.aifallrisk-96cd5e6a01e1{"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-04-26T22:45:45+00:00",
"registry_manifest_digest": "sha256:5f159f7f6408e476...",
"registry_kid": "fallrisk-96cd5e6a01e1"
}
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.
This is a lookup surface over signed enrollment records. It answers the question is this artifact known and signed. It does not perform the runtime structural verification that the signed records describe.
Runtime structural verification — measuring a model under contract and producing a fresh signed JWT bound to that measurement — is a separate enrollment service.
For the runtime side: integrations@fallrisk.ai.