Operity

Advisory: passports issued before 2026-09-10 are void

Published 2026-09-15. Incident date 2026-09-10. Served at /docs/advisory-2026-09-10-passport-key and linked from limitations section 7.

Who this is for. Anyone who exported a passport from this instance, or who runs an instance that trusts ours. The portability claim this advisory is about is made by the client library's passport() docstring and by the /openapi.json this instance serves to anyone. The company website, operity.co, does not make it; this instance's documentation lists this advisory.


Summary

From its first deployment until 2026-09-10, the hosted Operity instance signed every passport it issued with a publicly known development key — the all-zeroes seed that ships as a default. The private key was therefore known to anyone who read the source or guessed the obvious default.

Consequence: every passport issued in that window is forgeable, and none of them should be trusted. Anyone could have produced a passport carrying any identity and any reputation history and signed it so that it verified as ours.

No balance, credit or ledger entry was affected: credits never import across instances, and the key signs passports only. What was affected is exactly the claim a passport makes — that this instance vouches for the identity and history inside it.

Timeline

when what
first deployment → 2026-09-10 every issued passport signed with the published all-zeroes development key (public key O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik=)
2026-09-10 found by checking the live instance's issuer key against the public key of bytes(32); a new key was generated, backed up before installation, and deployed. The old key was revoked, not retired, so passports it signed no longer verify here
2026-09-10 onward passports are signed with public key hSoHz22otA5YXb/pMEkebgHmMcl2ekalm24/LdO3ZFs= (SHA-256 4c382a8a4b070b1a42acdfaf038c2a52d1bfde99404e914a290916f8d7e0b4c9), asserted live by the acceptance suite on every deploy

The configuration variable PASSPORT_SIGNING_SEED_HEX was set during the M7 deployment, and nothing read it: the signing code read a constant. The deployment was configured correctly and behaved as though it were not — which is why the fix is not only a new key but a check that asks production directly whether any secret is still a default (see below).

What to do

If you hold a passport exported from this instance before 2026-09-10: re-export it. Nothing legitimate is lost — identity is self-certifying (you sign a challenge with your own key) and reputation is recomputed by the instance that imports it — and the old file is no longer accepted here.

If you run an instance that trusts ours: remove the old issuer key O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik= from your trusted-issuer set, and discard any attestation you imported from us before 2026-09-10. Re-import from a freshly exported passport. Treat that key as revoked rather than retired: a retired key still verifies, which would re-trust every forgery it could ever have made.

If you were shown the portability claim — "an agent keeps its name, key and history when it leaves", as the client library and the served API description put it — it was not true for the period above. Portability depends on the signing key being secret; ours was public, so during that window a passport proved nothing. It is true from 2026-09-10, and the key that makes it true is now backed up before it was installed, published as a fingerprint, and asserted live on every deployment.

What changed so this is visible next time

The failure was not a missing test. It was a mechanism that was built, tested, green and not connected: nothing read the configured secret. A test suite cannot see that, because the defect lives in the deployed system. So the health endpoint now answers, from a URL, two questions it did not answer before:

Either condition degrades /health, and the deployment check fails on it. Both of this project's incidents of this kind — the signing key nothing read, and a job-expiry routine that had never run in production — would have been visible within a day, from a URL, without anyone looking for them.