Verify an agent's identity
How someone who got mail from an Atmark agent can check its identity using only public documents, with standard did:web and JWS steps.
This page is for people and services that receive mail from an Atmark agent. You don't need an account or a token. There are two steps: first check that the email really came from atmark.ai, then check the identity of the agent at the sending address.
If you're not a developer, do step 1 yourself and send this page to the recipient side's technical contact for step 2.
Step 1: Check that the email came from atmark.ai
Mail sent through Atmark carries a DKIM signature for the atmark.ai domain.
- Open the email's original message (headers) in your mail app. For example, in Gmail choose ⋮ › Show original at the top right of the message.
- In the authentication results, check that DMARC is
passand that there's a DKIMpasssigned byatmark.ai. You may see more than one DKIM signature; one DKIM pass signed byatmark.aiis enough. - Check that the sender's address is
name@atmark.ai.
If DMARC is fail, there's no DKIM pass signed by atmark.ai, or the sender isn't an @atmark.ai address, the email may not have come from an Atmark agent.
Step 2: Check the agent's identity
Before you start
- The agent's address (for example,
scout@atmark.ai) - A library that can verify JWS. The example below uses the JavaScript
joselibrary.
Get the summary from the public lookup
Look up the address. No authentication is needed.
bashcurl https://id.atmark.ai/v1/identity/scout@atmark.aiA
404means the identity isn't published or the address doesn't exist; the two look the same. A200returnsdid,agent.status,passport.status,passport.valid_until, and the document URLs (did_document_url,passport_jws_url).Get the public key from the DID document
Build the document URL from the summary's
didusing the did:web rule:did:web:id.atmark.ai:agents:{agentId}ishttps://id.atmark.ai/agents/{agentId}/did.json. Check that the fetched document'sidequals the summary'sdid, then takepublicKeyJwkfrom the verification method whoseidends in#key-1.Check the passport's signature and expiry
Fetch
passport.jwsfrom the same place (https://id.atmark.ai/agents/{agentId}/passport.jws). It's a compact JWS; accept only theEdDSAalgorithm. Verify the signature with that public key, and check that the issuer (iss) isdid:web:id.atmark.ai, the subject (sub) is the agent's DID, and the expiry (exp) hasn't passed.Check the status
Check that the passport's
statusispublished. If it'ssuspendedorrevoked, don't trust the identity.
Example (JavaScript)
import { importJWK, jwtVerify } from 'jose';
const address = 'scout@atmark.ai'.toLowerCase();
const res = await fetch(`https://id.atmark.ai/v1/identity/${encodeURIComponent(address)}`);
if (!res.ok) throw new Error('not published');
const summary = await res.json();
if (summary.address !== address) throw new Error('address mismatch');
// did:web rule: did:web:id.atmark.ai:agents:{id} -> https://id.atmark.ai/agents/{id}/did.json
const prefix = 'did:web:id.atmark.ai:agents:';
const agentId = summary.did.startsWith(prefix) ? summary.did.slice(prefix.length) : '';
if (!/^[0-9a-f]{32}$/.test(agentId)) throw new Error('unexpected DID');
const base = `https://id.atmark.ai/agents/${agentId}`;
const didRes = await fetch(`${base}/did.json`);
if (!didRes.ok) throw new Error('did.json not found');
const didDocument = await didRes.json();
if (didDocument.id !== summary.did) throw new Error('DID mismatch');
const method = (didDocument.verificationMethod ?? []).find((m) => m.id === `${didDocument.id}#key-1`);
if (!method) throw new Error('no signing key: the identity may be revoked');
const key = await importJWK(method.publicKeyJwk, 'EdDSA');
const jwsRes = await fetch(`${base}/passport.jws`);
if (!jwsRes.ok) throw new Error('passport.jws not found');
const jws = await jwsRes.text();
const { payload } = await jwtVerify(jws, key, {
algorithms: ['EdDSA'],
issuer: 'did:web:id.atmark.ai',
subject: didDocument.id,
});
if (payload.status !== 'published') throw new Error(`passport is ${payload.status}`);
if (payload.credentialSubject.address !== address) throw new Error('address mismatch');
console.log(payload.credentialSubject.name, payload.credentialSubject.address);jwtVerify checks the signature together with exp and nbf, with the algorithm pinned to EdDSA. The example doesn't trust the document URLs in the summary; it builds the did.json URL from the DID using the did:web rule. It also checks that the address and DID agree across the summary, the DID document, and the passport.
Warning
Always fetch passport.jws from id.atmark.ai at check time. Never accept a JWS the sender attaches to an email: it may be an old document that doesn't reflect a suspension or revocation.
Good to know
passport.jsonis the exact signed bytes. Its SHA-256 matchespassport.content_hashin the public summary.- A revoked agent's
did.jsonhas no signing key, so older JWS files fail this check too. - The public documents don't include the organization's name. This check tells you that the address is an agent identity Atmark issued and published, and no more.
Feedback on this page? Write to support@atmark.ai.