Trust model
What an Attestra attestation asserts, what it does not assert, and what remains trusted after you accept one.
What an attestation asserts
An attestation asserts one thing: that at a stated time, Attestra read a stated fact from a stated chain.
The fact is the one carried in the signed fields, a credential's status as
recorded on its source chain. The time is IssuedAt, and the window in which
the statement is offered is ExpiresAt. The chain is SourceChain. That is the
entire claim. An attestation does not assert that the credential is fit for your
purpose, that the issuer is reputable, that the subject is who they say they
are, or that the chain's own record is correct. It asserts a reading.
What Attestra cannot do
Attestra cannot issue a credential. It cannot revoke one. It holds no issuer key.
This separation is structural rather than a convention, and it is worth being specific about what that means. The code that issues credentials lives in a separate binary; it is never linked into the oracle. Both the issuer package and the registry service carry the same statement in their own source: the oracle is a notary, and the fact that it signs statements about credentials it did not issue is the whole basis of its trust model. Giving the oracle an issuer key would collapse that model, so the boundary is enforced at the level of what is compiled into which program, not by an operational rule someone could relax.
One honest note about the credential-issuing service that ships alongside the demo. It is a demo fixture generator, not a product. It binds to localhost by default because the mnemonics it holds can mint and revoke credentials. It retains that issuer material in memory and deliberately does not persist it, so restarting the service orphans every credential it seeded: the mnemonic cannot be regenerated, and those credentials can never be revoked afterwards. That is acceptable for disposable demo credentials and for nothing else. Do not read it as a feature of the system.
What you trust when you accept an attestation
When you accept an attestation and it verifies, you are trusting that at least
threshold distinct authorized signers each independently read the source chain
and agreed on the same digest.
You are not trusting that any single server is honest. Each node in a quorum
receives the original request and reads its own source chain; no node is ever
sent a finished attestation to counter-sign. N nodes are therefore N
independent opinions rather than one node wearing N hats. The verifier on each
chain recovers an address per signature, requires each recovered address to be
authorized in the signer registry, and requires the count to reach threshold.
The mechanics of that check, in all four implementations, are on Verifying an attestation.
What a dishonest operator can and cannot do
A single node that lies is outvoted. Replies are grouped by the digest they signed, and the largest group wins, so a node reporting a different fact from its peers lands in a minority group and is excluded from the bundle. The honest majority still reaches quorum and the attestation is produced normally.
An operator cannot forge another operator's signature: signatures are ECDSA over the digest, and the collector recovers the signer address locally rather than trusting a peer's claim about who it is.
An operator cannot vote twice with one key. Distinctness is enforced in two places. The collector keeps only the first reply per recovered address. On chain, each verifier requires recovered addresses to be strictly ascending, which proves distinctness in a single pass and rejects a repeated or malleated signature, since both recover to an address that is not greater than the previous one.
What remains trusted
The properties above are real, and they are bounded. After you accept an attestation, the following remain trusted, and no part of the protocol removes them:
- The source chain's own record. Attestra reports what the chain says. It does not audit it. If a credential was recorded incorrectly on eIDChain, an attestation will faithfully report the incorrect record.
- The signer registry's contents, and whoever controls it. The registry owner can add signers, remove signers, and change the threshold. That key is the governance surface of the whole system: an owner who adds enough cooperating signers can reach any threshold they like. Guards exist (no zero address, no duplicates, a removal that would drop the signer count below the threshold is refused, and the threshold must stay within one and the signer count), but they constrain the shape of the set, not the intent of whoever administers it. Control of the registry owner key is a trust assumption you are making, and it should be treated with the seriousness that implies.
- The operators' independence. k-of-n only means something if the n are genuinely separate parties. If several signing keys are held by one organisation, or run on one machine, the threshold is a number rather than a guarantee. The protocol cannot enforce independence; it can only count distinct addresses. Only the registry's administration, and whatever process governs who is admitted to it, can establish that the addresses correspond to separate parties.
Freshness, and its current limit
IssuedAt, ExpiresAt and Nonce are all inside the signed digest, as words
seven, eight and nine of the preimage. An attestation therefore cannot be
re-dated, its validity window cannot be widened, and its nonce cannot be swapped:
any change to those bytes produces a different digest, and the signatures no
longer recover to authorized addresses.
That gives a consumer a usable property. If you issued the nonce yourself as a challenge, and the attestation you receive carries that nonce, you can confirm the attestation was minted for your request rather than replayed from an earlier one.
The limit is this. The presentation's own challenge is not currently checked against that nonce. The eIDChain adapter calls the SDK's presentation verification with no challenge option set, and its source records the omission as a follow-up.
Stated as plainly as we can: the nonce binds the attestation to your request, and it does not yet prove the holder answered a fresh challenge. Read that as the exact boundary it is, no wider and no narrower. The holder binding, the claim hash recomputation, the expiry check and the on-chain read all still happen during verification; what is missing is the link between your challenge and the holder's signature over it, which means a presentation captured earlier could be re-presented and would produce an attestation carrying your nonce.
What the event feed is not
The WebSocket feed carries notifications from Attestra's own chain subscriptions. Nothing on it is signed. Its delivery is deliberately lossy: a slow consumer misses events rather than backing up the publisher.
It is not evidence. It is a hint that something happened, useful for driving a user interface or triggering a fetch, and it should never be the basis of a decision. Evidence comes from a signed attestation, which is a document you can check yourself. Feed semantics are documented at WebSocket.
Verifying without us
The strongest property in this system is the one that costs Attestra the most. Every attestation is signed over a digest that any of the three chains can recompute from the attestation's own fields, using a rule that is implemented four times over in four languages and pinned by a shared fixture. A consumer holding an attestation and a signer set can therefore check it entirely on chain.
That means a consumer never has to trust Attestra's availability or honesty at the moment of use. If our API is down, an attestation you already hold still verifies. If you doubt what we say about an attestation, you do not have to argue with us; you recompute the digest and check the signatures against the registry yourself. The rule you would implement to do that is on The canonical digest.