First Seal
Seal a Fact in 5 Minutes
CTO · Staff Engineer · SRE
This page shows how to seal a fact technically. It does not explain how to interpret its meaning.
One Endpoint
POST /streams/{stream_id}/facts
Content-Type: application/jsonA stream is identified by stream_id, provided by the client in the URL. If no stream with that ID exists, Horizon creates it implicitly when the first fact is sealed.
The only identifier you manage is stream_id; Horizon does not impose any business semantics on it.
One Request
{
"tenant_id": "acme-corp",
"actor": "ops-lead@acme-corp.com",
"custom_payload": {
"action": "stop_service",
"context": "Memory leak confirmed"
}
}In production deployments, tenant_id is typically derived from authentication context rather than provided in the payload.
Your payload is opaque to Horizon. It is recorded exactly as provided.
One Response
{
"fact_id": "fact-01HRX7G2NB",
"stream_id": "stream-034",
"tenant_id": "acme-corp",
"actor": "ops-lead@acme-corp.com",
"sealed_at_ms": 1706284801000,
"custom_payload": {
"action": "stop_service",
"context": "Memory leak confirmed"
},
"fact_hash": "a1b2c3d4e5f6...",
"prev_hash": "9f8e7d6c5b4a...",
"parent_fact_id": "fact-01HRX7F1MA"
}Fields such as fact_hash and prev_hash are used for integrity and verification, not business logic.
What Happened
Horizon did not interpret custom_payload. That's your data.
- Horizon assigned sealed_at_ms (authoritative timestamp)
- Horizon computed fact_hash from a deterministic representation of the fact
- Horizon chained to previous fact via prev_hash
- Horizon stored the fact (append-only)
Idempotency
{
"tenant_id": "acme-corp",
"actor": "ops-lead@acme-corp.com",
"client_ref": "incident-2024-01-26-stop-001",
"custom_payload": { ... }
}Add client_ref to make the request idempotent:
Same client_ref → same fact returned. No duplicate sealing.
Idempotency does not alter the proof. It prevents duplication only.
Storage Guarantees
| Property | Guarantee |
|---|---|
| Append-only | Facts cannot be modified or deleted |
| Hash chain | Each fact links to previous via prev_hash |
| Tamper detection | Recompute hashes to detect modification |
| Tenant isolation | Facts scoped by tenant_id |
| Proof authority | sealed_at_ms assigned by Horizon |
These properties hold even if the client system is compromised, because any modification is detectable.
What Horizon Does Not Do
- No interpretation·custom_payload is opaque
- No workflow·No states, no transitions, no approvals
- No validation·Your payload, your schema
- No business logic·Seal facts, nothing else
Verify Chain Integrity
POST /streams/{stream_id}/verifyReturns { "valid": true } if hash chain is intact.
Verification recomputes hashes and signatures. It does not interpret facts or assert correctness.
Verification results only attest that the recorded sequence hasn't been tampered with.