Personhood attestations on the Sigstore transparency log.
passportsign binds a GitHub account to a passport-holding human, without
revealing the human's identity, and publishes that binding to the public
Sigstore Rekor log as an
in-toto attestation with a custom
predicateType of https://passportsign.dev/personhood/v1.
It is Sigstore-adjacent in the same way that
gitsign is. gitsign signs Git
commits with OIDC identity and logs to Rekor — but it doesn't establish
personhood. An AI agent with a GitHub account passes gitsign trivially.
passportsign fills that gap.
At time T, a human holding a valid government-issued passport (and optionally: from country X, if the subject chose to disclose) was in control of the GitHub account
@username.
That's it. See docs/passportsign.md §1 for the
full list of things the badge does not claim, and §8 for the limits of
its Sybil resistance.
v0 — pre-alpha, CLI proof-of-concept. v0 shipped 2026-05-25; six-point acceptance walkthrough green, including a real-passport bind on the public Sigstore Rekor log:
That badge is the v0-shipped evidence for @cynarlab — the
maintainer behind debugmcp/mcp-debugger. Other users generate
their own by running passportsign bind (see Quick start below);
your badge will have your own username, country, and date baked in.
The companion bundle is at docs/evidence/binding.passportsign.json
— run passportsign verify against it to re-derive the proof yourself.
You'll need:
- Node 22.5+ (uses
node:sqlite). - The ZKPassport mobile app (iOS / Android) with your real e-passport loaded.
- A GitHub account you can create a public gist on (you'll create one during the bind).
npx @passportsign/cli bind <your-github-username> --countryThe CLI walks four steps:
- Gist — prints a nonce and a filename (
passportsign.txt); you create a public gist ongist.github.comwith that exact content under the username you're binding. - Scan — renders a QR; you scan with the ZKPassport mobile app, approve the disclosure on your phone.
- Submit — submits the resulting in-toto attestation to public Sigstore Rekor.
- Bundle + badge + index — writes
binding.passportsign.json,passportsign-badge.svg, andpassportsign-index.jsonnext to where you ran the command. Commit the index to the root of your profile repo (github.com/<you>/<you>) — it's how badge services andpassportsign listdiscover your binding (public Rekor isn't searchable by predicate type).
npx @passportsign/cli list <github-username> # via their published index
npx @passportsign/cli list <github-username> --entry <rekor-uuid> # direct inspectionFetches every entry the index references, integrity-checks it against
the log, verifies its inclusion proof, and prints the state of each
binding: active, stale (older than 12 months), or revoked.
--json for scripting.
npx @passportsign/cli revoke <github-username>Needs only a fresh scan of the same passport — no GitHub access
(that's the recovery property: if your account is hijacked, you can
still revoke). Submits a revocation entry to Rekor and updates your
passportsign-index.json; commit the updated index or nobody learns
of the revocation. Conversely: anyone with brief access to your
passport can do this, and the harm is reversible by re-binding.
npx @passportsign/cli verify ./binding.passportsign.jsonFour checks, all run without any dependency on a passportsign.dev operator — verification needs only public Sigstore Rekor and a local zkPassport SDK:
- statement bytes hash to the Rekor entry's recorded payloadHash
- inclusion proof verifies against the captured root
- captured root is consistent with the current witnessed root (no log rewrite that orphans the entry)
- zkPassport SDK accepts the proof + the returned uniqueIdentifier matches the statement
Add --no-rekor-refetch for offline structural verification only
(skips the consistency check). Add --gist-recheck to also re-fetch
the captured gist URL as a liveness signal.
- Bind: the laptop and phone both need internet. They do not
need to be on the same LAN — the zkPassport SDK uses a hosted relay
(
@obsidion/bridge) to ferry the proof from phone to laptop. Mobile data on the phone + any internet on the laptop works. - Verify: any internet (to talk to
rekor.sigstore.devfor the online checks). Pure offline verification —--no-rekor-refetch— needs only the bundle plus the zkPassport SDK (downloaded with the CLI install).
packages/
├── core/ shared state machine: canonical serialization, in-toto
│ statement builder, bundle format, GitHub gist check,
│ Rekor client, RFC 6962 Merkle, verifier, profile-index
│ convention, state classification. Runtime-neutral —
│ `@passportsign/core/web` runs on Workers and browsers.
├── cli/ `passportsign` binary: bind, verify, list, revoke.
└── web/ hosted surface: Cloudflare Worker serving live-state
badges (/badge/<u>.svg), /verify/<u>, and the static
/bind browser flow. No database, no keys.
docs/
├── passportsign.md v0.6 spec
├── roadmap.md v0.5 / v1.0 / v2 milestones + status
├── v0-acceptance.md six-criteria walkthrough + living evidence
├── evidence/ real-passport bundle + badge from v0 ship
├── index/ operator overlay indexes (append via PR)
└── upstream-issues/ drafts for SDK fixes filed at zkpassport
Apache-2.0. See LICENSE.