Key custody
A seal proves that a key signed a record. Key custody says whose hand was on that key. Every key on a business’s register has a statement, signed by MasterDB, saying one of two things:
| Custody | Who holds the private half | What a seal by the key proves |
|---|---|---|
hosted |
MasterDB, for the business. This is the key behind standard publishing: a person of the business signs in with an email link and confirms each publish with a one-time code sent to their verified address. | MasterDB signed, on a request a person with a grant on the business confirmed. Each use of the key is in the public transparency log, with a hash of the session that used it. |
self |
The business: a person’s passkey, or an integration key held by the business’s own system. | A key the business holds signed it. |
A key with no statement is reported unstated. A verifier never assumes self.
Where it is published
Section titled “Where it is published”GET /v1/certificates/{uuid}/key-custody on verify.masterdb.ai (and api.masterdb.ai), beside the ADL Certificate and the business’s public keys (GET /v1/certificates/{uuid}/keys). It is public, needs no account, and lists every statement ever issued for the business, newest first:
{ "uuid": "0b7d6c3e-2f4a-4c1b-9d8e-7f6a5b4c3d2e", "statements": [ { "key_id": "7Hs2pQ9vLm4xZk1Nt8Rc5Yw3Bf6Ju0Ea2Di7Go9Ks1M", "custody": "hosted", "effective_from": "2026-10-03T09:00:00.000Z", "issued_at": "2026-10-03T09:00:00.000Z", "statement_id": "sha256:…", "statement": { "payloadType": "application/vnd.masterdb.key-custody.v1+json", "payload": "…", "signatures": ["…", "…"] } } ]}Only statement counts. The other members repeat what it says, so a person can read the answer without decoding it.
The signed statement
Section titled “The signed statement”statement is a DSSE envelope of type application/vnd.masterdb.key-custody.v1+json over this payload, in RFC 8785 form:
| Member | Meaning |
|---|---|
v |
1, the format. Any other value is refused (version_unknown). |
business_uuid |
The business the key belongs to. |
key_id |
The key’s RFC 7638 thumbprint, as the seal and the published keys name it. |
custody |
hosted or self. Anything else is refused (payload_malformed). |
issued_at |
When MasterDB issued the statement. |
effective_from |
From when it holds. A first statement takes effect from the key’s own start; it is never later than issued_at. |
sandbox |
true on a sandbox statement only, trusted only under the sandbox anchors. |
No other member is allowed. The statement names no person.
It is signed the way the ADL Certificate is signed: by MasterDB’s issuance key, with both halves of it, the P-256 signature and the ML-DSA-65 signature, each by an issuance key valid at issued_at in the signed key set. Both are required. A statement carrying only one is refused with post_quantum_required. The issuance key, not a working key, signs custody because custody changes what a seal proves, so it carries the certificate’s own trust.
When a statement is issued
Section titled “When a statement is issued”- A hosted key: when MasterDB issues it, before it is ever used. No hosted seal exists without a
hostedstatement before it. - Every other key: when the certificate is issued or re-issued, and when the key first seals, if it has no statement yet.
- A change: if a key’s custody changes, a new statement is issued with a new
issued_atandeffective_from. The old one is never edited and stays listed. The statement in force at an instant is the one with the latesteffective_fromat or before it; a tie goes to the later issuance.
Every issuance is a key_event leaf in the transparency log, over the SHA-256 of the envelope.
Checking it with the verifier libraries
Section titled “Checking it with the verifier libraries”| TypeScript / Python | What it does |
|---|---|
verifyKeyCustody / verify_key_custody |
One statement: the payload type, the format and its exact members, both issuance signatures, and sandbox against the anchors. With expectUuid / expect_uuid, a statement about another business is refused (key_unknown). |
verifyKeyCustodyStatements / verify_key_custody_statements |
The whole answer of the route (or a list of envelopes): every statement checked, all about one business. |
keyCustodyInForce / key_custody_in_force |
The statement in force for a key at an instant. |
verifySeal / verify_seal with keyCustody / key_custody |
The seal as usual, and custody in the result: hosted, self or unstated for the seal’s key at sealed_at. |
import { productionKeySet, verifySeal } from '@masterdb/verifier';
const keySet = await productionKeySet();const base = `https://verify.masterdb.ai/v1/certificates/${uuid}`;const [certificate, keys, keyCustody] = await Promise.all([base, `${base}/keys`, `${base}/key-custody`].map(async (u) => (await fetch(u)).json()));const result = verifySeal(recordBytes, seal, { publishedKeys: keys, keySet, certificates: [certificate.certificate], keyCustody });console.log(result.custody); // 'hosted', 'self' or 'unstated'from masterdb_verifier import SealContext, production_key_set, verify_seal
result = verify_seal(record_bytes, seal, SealContext(published_keys=keys, key_set=production_key_set(), certificates=[certificate["certificate"]], key_custody=key_custody))print(result.custody) # "hosted", "self" or "unstated"The cases both libraries are held to, accepted and refused, are key-custody.json in the test vectors.
Why it is not in the certificate
Section titled “Why it is not in the certificate”Version 1 of the ADL Certificate admits no member it does not name, and the verifier libraries refuse one they do not know, so custody is published beside the certificate rather than inside it.