DID Mapping Alignment, Reporting & Conformance
A tamper-proof, independent record that proves an atproto identity's history has not been quietly rewritten, hidden, or forked.
Open the console →An atproto identity is a signed, hash-linked log of operations, served from a single directory. Every operation is self-certifying, so a signature vouches for the operation it's attached to. It can't tell you whether the directory showed you all of them, the latest ones, or the same ones it showed everyone else.
This operation is authentic, signed by a key that was valid at the time. PLC already gives you this, per operation.
That you were shown the complete, current, and non-equivocated history. No signature can speak for the operations it isn't on, including the ones you never saw.
Every operation can be perfectly signed and the directory can still deceive you, because it alone decides which operations you see. None of these attacks needs a stolen key — just control of the directory a resolver trusts:
Your account key is stolen, and you rotate it out with a new signed operation. The directory serves your history with that one operation missing. To the rest of the network the stolen key is still live, and the thief keeps signing as you.
The lie is the operation left out.
The directory returns nothing for a DID that exists. Every app that looks the person up sees no such identity. Handles dangle, posts won't resolve, and there's no error to appeal.
Absence carries no proof to check.
Show the victim their real history, while showing one targeted service a forked version that points their account at a server the attacker runs. Neither side ever sees the other's copy.
No single viewer holds both halves.
Each of these is a real, ground-truthed scenario in the adversarial report. DIDMARC catches them with an independent, on-chain copy of the whole, current history: a resolver can tell when the directory's answer doesn't match it.
Each opted-in DID's full operation log lives on Starknet, block-timestamped and append-only.
The network proves the record and settles it to Ethereum, so there is no bespoke prover and
no trusted snapshot. On the read side the witness re-derives that DID's confirmed head from
the on-chain log, fetches the document the directory serves, and compares the two to produce
a status. plc.directory stays canonical. DIDMARC is a read-side witness any
resolver turns on by itself, with no coordination and no protocol change.
Never a
false diverged. A directory that's merely running ahead, or a contradiction
that plain clock skew could explain, stays at tentative-consistent. One false
alarm and operators switch the check off, so diverged is reserved for real
contradictions.
The witness is a small TypeScript library with no external dependencies. Hand it a DID, an anchor reader, and a directory client, and it hands back a status your resolver acts on.
import { check, AnchorReader, DirectoryClient } from "@plc-anchor/witness";
const anchor = AnchorReader.http(process.env.STARKNET_RPC, CONTRACT_ADDR);
const { status, reason } = await check("did:plc:…", anchor, new DirectoryClient());
switch (status) {
case "confirmed": // proven and immutable; trust the directory's answer
case "tentative-consistent": // agrees so far, nothing immutable yet; proceed
case "absent": // not anchored; no opinion, resolve as usual
break;
case "diverged": // proven head contradicted; the alarm fired
quarantine(reason);
}
Prefer the shell? The CLI exits 2 on diverged and 0
otherwise, so it drops straight into a health check or a CI gate:
STARKNET_RPC=https://your-node node cli.ts did:plc:… # [--contract 0x…] [--directory URL]
Adopt it like DMARC: log the status first, then
quarantine on diverged, then refuse to relay. Each step is one resolver's own
decision, at its own pace.
The alarm has to fire on real attacks and stay silent on benign disagreement. The adversarial report tests both, as real signed did:plc chains run against the shipping witness. Four of its scenarios:
A mirror serves your history with one confirmed operation quietly omitted. The witness re-derives the confirmed head from the on-chain log, finds it missing from the directory's answer, and flags the mismatch.
The directory returns nothing at all for an anchored DID. The anchor proves the identity exists with a confirmed head, so the empty answer is a contradiction, not an absence, and the alarm fires.
A head mismatch that ordinary clock skew near the 72-hour boundary could explain stays
tentative-consistent, never an alarm. This rule is what keeps
diverged free of false positives.
Even when the anchor's own on-chain log is corrupt and yields no confirmed head, the
witness degrades to tentative-consistent. A broken witness never accuses an
honest directory.
Write-side attacks fail the same way. A forged signature, a weaker-key recovery, and a late recovery can each be paid for and stored on chain permissionlessly, but the witness's read-side re-derivation drops every one, so a forged operation never reaches a resolver. Scenarios 9 through 12 cover these, including the capture attempt that motivated the imported-history guard.