An attestation is a claim someone is willing to sign: this account belongs to a verified human, this issuer is accredited, this device is registered. KryptoOS is the ecosystem's identity layer for making and checking such claims, and EmpoorioChain is where the anchors live. This post explains the circuit and tells the story of the bug that broke it.
The pieces
- DIDs:
did:emp:<identifier>, registered on chain inpallet-identity-ssi(runtime index 18). Registration is permissionless. - Verifiable credentials: signed JSON-LD documents issued by an issuer API, checked by a verifier API and a Rust verifier service, with revocation.
- On-chain anchors: the pallet stores DID documents (
Dids), the account-to-DID mapping (AccountToDid) and revocations (Revoked). Credentials themselves live off chain; their hashes and revocation status are anchored. - Zero-knowledge proofs: planned for selective disclosure. The repository's earlier ZK module attached the secret in clear — not zero-knowledge — and was removed.
- Crypto core: genuine cryptography, ~700 real tests across the stack.
The bug
The TypeScript SDK's on-chain verifier read the DID document's key list and always got an empty array, so verifyCredentialOnChain returned verified: false for every credential. Its unit test passed because the test stub fabricated the field the code read.
The first audit (2 September 2026) read the polkadot-js library source and concluded the field name was wrong. Phase 3 (13 September) tested against a real node and found the opposite: with the dependency versions actually installed, the key list does live where the code read it, and the "fix" would have broken it. The real problems were elsewhere — the WebSocket provider was constructed without ever connecting, so the issuer and verifier APIs had never reached the chain over WebSocket at all.
The rule that came out: test the decoder against a node, never infer it from the library's code. Live tests are now gated by an RPC URL and a seed; they run against a development chain in CI.
Why nobody noticed
Because the testnet had zero DIDs. Nothing could be verified because nothing had been registered, and nothing had been registered because — as with the rest of the ecosystem — no client had encoded the chain's transaction envelope correctly until 7 September. The first did:emp was registered on 14 September 2026.
Where it stands
- Circuit: 8 of 10 Phase-3 tasks done and tested against a real chain (local, runtime 218), then the first DID on the public testnet.
- Issuer API revocation endpoint: was unauthenticated; fixed. The Rust verifier treated revoked credentials as valid despite a doc comment claiming fail-closed; fixed.
- Deployment: none public. The domains the older documentation named for KryptoOS do not resolve. A production app in the ecosystem had a "Continue with KryptoOS" button pointing at a nonexistent host; it was removed.
- 123 tasks in 15 phases remain, each with a written success test.
What it enables
On EmpoorioChain, a credential is not only something a person shows; it is something the runtime can require. pallet-compliance can demand a credential before an asset moves; pallet-creator-studio-fees grants up to 50 % fee discounts to accounts holding a proof-of-humanity credential; paymasters can sponsor only credentialed users. Identity becomes a runtime input — once there is more than one DID.
Based on the KryptoOS audit (2026-09-02) and its Phase 3 verification (2026-09-13), and PALLET_REFERENCE.md.


