BitSeal
API reference

Two endpoints. Plain JSON.

The public API is small on purpose. Seal a manifest, look up a root. Both are protected by Cloudflare Turnstile and documented below. All responses are JSON, all hashes are lowercase hex.

Base URL

https://bitseal.orygn.tech
POST/api/seal

Accepts a precomputed manifest (root hash, leaves, metadata, Turnstile token) and returns the signed seal plus a PDF certificate. The server re-derives the root from the supplied leaves before signing, so a client cannot pin a signature to a root that does not match its own leaves.

Request body
root_hash*string64-char lowercase hex, the Merkle root of the file.
merkle_tree*string[]Leaf hashes in file order, lowercase hex.
seal_mode*stringMust be "merkle-blake3-64k-v1".
chunk_size_bytes*numberMust be 65536 for v1.
blake3_hash*stringWhole-file BLAKE3 digest.
sha3_512_hash*stringWhole-file SHA3-512 digest.
entropy*numberShannon entropy of the file, 0.0 to 8.0.
filename*stringAdvisory, stored as metadata, not signed.
size_bytes*numberFile size in bytes.
mime_type*stringAdvisory MIME type.
tokenstringCloudflare Turnstile token. Required for browser callers; optional when the X-API-Client header is set.
200 response
successbooleanAlways true for 200.
seal_idstringUnique ID for the seal record.
signaturestring128-char hex Ed25519 signature over root || LE_f64(timestamp_utc).
timestamp_utcnumberUTC unix seconds with fractional precision, the value that was signed.
signerstringHuman-readable signer label.
verification_key_idstringKey ID of the Authority key that produced this signature.
pdf_base64stringBase64-encoded PDF certificate.
Example request
curl -X POST https://bitseal.orygn.tech/api/seal \
  -H "Content-Type: application/json" \
  -d '{
    "root_hash": "6d56f7...",
    "merkle_tree": ["a1b2c3...", "d4e5f6..."],
    "seal_mode": "merkle-blake3-64k-v1",
    "chunk_size_bytes": 65536,
    "blake3_hash": "...",
    "sha3_512_hash": "...",
    "entropy": 7.91,
    "filename": "contract.pdf",
    "size_bytes": 130000,
    "mime_type": "application/pdf",
    "token": "<turnstile-token>"
  }'
GET/api/verify

Looks up a root on the Neon Postgres ledger. Returns the three-axis verdict: ledger presence, Ed25519 signature validity, and Merkle tree consistency. Each axis is reported on its own field.

Query parameters
root*string64-char lowercase hex root to look up.
tokenstringCloudflare Turnstile token. Required for browser callers; optional when the X-API-Client header is set.
200 response
validbooleanOverall verdict, true only when all axes pass.
foundbooleanAxis 1. Was the root present in the ledger.
signature_verifiedbooleanAxis 2. Did the Ed25519 signature verify against the resolved key.
verification_key_idstringWhich Authority key ID verified, or was attempted.
signature_notestringShort human-readable note when axis 2 is inconclusive.
tree_checkedbooleanAxis 3. Whether the Merkle fold was re-run (only possible when leaves are stored).
tree_consistentbooleanAxis 3 verdict. True if the re-folded root matches the stored root.
tree_notestringShort note when axis 3 is inconclusive or skipped.
seal_modestringThe manifest format of the record.
leaf_countnumberNumber of leaves in the stored tree.
dataobjectFull manifest record: filename, size, entropy, signer, timestamp, leaves, signature.
Status codes
200OKA verdict was produced. Read fields to interpret.
400Bad requestMissing token, malformed root.
404Not foundThe root is not in the ledger.
429Rate limitedPer-IP or global rate limit exceeded. Check Retry-After header.
Example request
curl "https://bitseal.orygn.tech/api/verify?root=6d56f7...&token=<turnstile>"

Programmatic access (SDK, scripts)

The Turnstile human-check exists to shield the endpoints from drive-by web abuse. It is not a good fit for scripts. Any caller that sends a non-empty X-API-Client header may omit the token field. Rate limits (below) apply identically to web and SDK traffic, so the header is not a privilege upgrade, only an ergonomic bypass.

The reference Python SDK sends X-API-Client: BitSeal-SDK/<version> python/<ver> on every request. Third-party integrations should set their own identifier, e.g. X-API-Client: acme-sealer/1.2.

Rate limits

Limits are enforced per client IP over a sliding window. The server returns 429 with a Retry-After header and a retry_after_seconds field in the JSON body.

Per-IP limits
POST /api/sealburst20 requests per minute.
POST /api/sealhourly10 requests per hour.
GET /api/verifyburst100 requests per minute.
GET /api/verifyhourly60 requests per hour.
Global limits
POST /api/sealhourly500 seals per hour across all IPs. Distributed-abuse ceiling.

Need higher throughput for an integration, a research audit, or a production batch? Email [email protected] and describe the use case.

Offline verification

The cryptography here is standard and published. Given the manifest, the public key at /.well-known/bitseal-authority.json, and a BLAKE3 plus Ed25519 library, anyone can verify a seal with no network call to BitSeal. The Python SDK does this, and its source is the normative reference.