Trust levels
A trust level says who vouched for an action. L0 means the operator alone authorized it; L1 means the operator plus a human approver co-signed it.
Every receipt carries a trust_level: either L0 or L1. The level is not a flag you set — it is backed by real Ed25519 signatures, and the verifier re-derives it from the signatures actually present. A receipt can never claim more trust than its keys support.
L0 and L1
Each level maps to a set of signatures. L0 carries one signature from the operator. L1 carries that operator signature plus a second from a human approver. Both are Ed25519 signatures over the action’s canonical bytes.
| Level | Signatures | What it says |
|---|---|---|
| L0 | Operator only | The operator authorized this action under a known policy. This is the level for an ungated allow or redact decision. |
| L1 | Operator + approver | Everything L0 says, and that a human approver co-signed it with their own key. Either a single approver or a k-of-n quorum (see below) — both are L1. |
An L0 receipt carries one signature entry, keyed as the operator:
"signatures": [
{ "algorithm": "Ed25519", "key_id": "operator",
"public_key": "ed25519:uP3…b1", "signature": "3a9f…04af" }
]An L1 receipt carries a second entry, keyed as the approver. The key_id distinguishes the two, and each has its own public_key:
"signatures": [
{ "algorithm": "Ed25519", "key_id": "operator",
"public_key": "ed25519:uP3…b1", "signature": "3a9f…04af" },
{ "algorithm": "Ed25519", "key_id": "approver",
"public_key": "ed25519:k7Q…9d", "signature": "be12…77c1" }
]L1 has two shapes
L1 comes in two shapes, and both are equally L1. The first is a single approver: one person co-signs. The second is a quorum: a k-of-n rule (say 2-of-3) where several people each sign. A quorum receipt carries a multi_approval block holding the threshold, the roster of eligible keys, and one record per approver. That block is how you tell the two shapes apart — not the level.
"multi_approval": {
"threshold": 2,
"roster": ["ed25519:k7Q…9d", "ed25519:m2X…1a", "ed25519:p9R…44"],
"approvers": [
{ "approver_identity": "ed25519:k7Q…9d", "decision": "approved",
"reason": "Confirmed with finance.", "decided_at": "2026-01-14T18:11:52Z" },
{ "approver_identity": "ed25519:m2X…1a", "decision": "approved",
"reason": "Invoice checks out.", "decided_at": "2026-01-14T18:14:09Z" }
]
}A quorum is not higher than a single approval, and it is not stronger because more people signed. Trust re-derives to L1 only when at least threshold distinct roster keys each verified their own leg. Fewer than that fails with ThresholdNotMet. For the full routing flow and the device-held key, see Human approval.
The level is re-derived at verify
This is the part that matters. The trust_level stored in a receipt is a claim. The verifier never trusts it — it re-derives the level from which signatures actually verify, then compares that to the stored value.
- Operator signature verifies and no approver signature is present: the derived level is L0.
- Operator signature and an approver signature both verify: the derived level is L1.
- A receipt stamped L1 whose approver signature is missing or invalid: the derived level is L0, below the claim, and verification fails with
TrustLevelMismatch.
So you cannot claim L1 with only an operator signature. Writing "trust_level": "L1" onto an action that carries one signature does not produce an L1 receipt — it produces an invalid one. Because the check runs in the browser verifier exactly as it runs on a server, anyone can confirm the level offline, with no HESO infrastructure in the loop.
Domain separation
The operator and the approver sign the same bytes but under different domain tags. The operator signs the action under heso-action/v1\0; the approver signs it under heso-approval/v1\0. The distinct tags mean a signature made in one role can never be replayed as the other — an operator signature cannot be passed off as an approval, or the reverse.
HESO’s cloud stores and serves receipts, but it never signs them — there is no server-side signing key at any level. An approver co-signs with a key held on their own device. So the cloud, or anyone who breaches it, cannot mint an L1 receipt naming an approver who did not actually approve. A receipt that claims L1 without a valid approver signature fails with TrustLevelMismatch.
That is the core trust property: the cloud carries receipts, but it is not a trusted party. Every claim it forwards can be re-checked against the signatures, by you.