Polaris
The Polaris emblem: a gold sun on a navy shield

POLARIS

A working reference implementation of an issuer-unlinkable, duress-aware identity-token system, signed with ML-DSA-65 under an audited algorithm-migration path.

Reference implementation · notional data only

Fixus inter mutabilia · fixed amid the mutable

46 schema tables · 134 routes · 338 machine-checked invariants · ML-DSA-65 signing default · X25519MLKEM768 TLS edge · Plonky2 ZK + second witness

What Polaris is

An authority issues a credential signed with ML-DSA-65; a holder presents it; anyone checks it two ways. Authenticity is offline: a standalone verifier checks the signature against published keys, with no database and no network. Authorization is answered online by a relying-party API, or offline by a short-lived signed status assertion. The cryptographic claim is algorithm agility under an audited migration path, not settled security against a quantum adversary.

The backbone is a schema whose constraints are the security boundary: the guarantees live in the database, not in application code. A rule enforced by a trigger, a CHECK constraint or a unique index binds every client and survives every restore from backup.

Around the credential: a protocol of signed statements (registry, trust list, exchange receipts, timestamps, document signing, login tokens, wallet presentations), each verified offline by the same verifier and both SDKs, frozen at version 1 and re-verified on every push.

The problem it models: Americans carry six to eight credentials that do not talk to each other. Polaris consolidates them into one token per person, verified through context-scoped events (banking, voting and healthcare are different events with different disclosure rules). The default disclosure level is zero-knowledge, so the typical verification stores no token identifier at all, and the zero-knowledge verification graph cannot be reconstructed even by someone holding the whole database (SELECTIVE and FULL events do carry a token id).

The ten guarantees

Above all ten sits the vocation: no person can be compelled to renounce, transfer, or surrender their identity against their will. Each guarantee is enforced in the database, where no client can bypass it, and machine-checked on every change.

C1

Append-only history. Triggers reject any UPDATE or DELETE on the audit tables. What happened, stays.

C2

Zero-knowledge means zero. A CHECK constraint refuses to store a token id on a zero-knowledge verification.

C3

One active token per person. A partial unique index makes a second ACTIVE row impossible, under any concurrency.

C4

Atomic lockout counting. Failed logins increment in a single statement; there is no check-then-act race.

C5

No inline scripts. Content-Security-Policy is script-src 'self', verified per route.

C6

Server-side disclosure. A client cannot upgrade what a verification reveals; redaction is tested at every read path.

C7

No hardcoded crypto. Algorithms are rows in a registry; rotation is an INSERT, not a redeploy.

C8

Bounded aggregates. Every map and API result set carries a hard cap; payload scales with the viewport, not the ledger.

C9

Real concurrency tests. Race-prone paths are tested with real threads against a live database, not mocks.

C10

Identity is not money. The schema carries no monetary claim; the boundary is structural, not policy.

The hard parts

Consolidating six credentials into one token is the easy half. The interesting half is what happens when an adversary shows up. Six answered by construction, at the database layer, not in policy.

Cryptographic compulsion

"Sign this or I break your fingers." The holder cannot refuse without injury.

A second secret produces a verification that looks identical and silently records a DuressEvent. By design the coercer sees success; the indistinguishability is a tested property of the modeled flow, not an audited side-channel guarantee.

Catastrophic loss

Token lost, holder unidentified, nothing to prove who they are.

A two-phase ceremony gated by independent out-of-band channels, a cooldown, and an admin-only decision. Compromising one channel is not enough.

Quantum migration

Today's signing algorithms break overnight when a quantum computer arrives.

Tokens carry classical AND post-quantum signatures simultaneously during cutover, with a hard database rule that exactly one is active. The default is already post-quantum.

Issuer concentration

One agency issues tokens that masquerade as any other agency's.

Explicit-only federation; no transitive trust. Every cross-agency verification gates on an active trust attestation row.

Auditability without privacy loss

"Prove this token was in the ledger" without revealing which one.

A Plonky2 proof over a Merkle commitment answers membership and nothing else. The verification graph cannot be reconstructed from zero-knowledge events.

Issuer overreach

An agency revokes tokens at industrial scale, outside policy.

A per-agency revocation-rate ceiling enforced by trigger, sanctioned by a policy row, audited under an advisory lock.

Verified, not asserted

Every number and every link on this page is checked before it is published: the counts are recomputed from the repository, the links and images are resolved against the tree, and either one failing stops the deployment. The two test counts are measured per release on the reference machine. CI does not just run tests; it exercises the artifacts.

1245product tests, live DB
126crypto-witness tests
338invariant checks
23CI jobs
5services booted in CI

test counts measured at v1.0.0-rc.62; the rest recomputed on every push

What happened when somebody else's software tried it

Everything above is this project checking itself. These are the only results that are not. A constraint lattice and a verifier that an unmodified wallet already spoke to is the whole of what is demonstrated here; the scale the architecture is designed for is a design target, not a deployment.

A wallet nobody here wrote

On 15 September 2026 an unmodified walt.id Wallet API v2 presented an SD-JWT VC over OpenID4VP 1.0 and polaris-oid4vp accepted it. It first refused Polaris, correctly: a certificate defect that every internal check had passed over.

OpenID Certified verifier

OpenID Certified

OpenID Certified™ by Egor Khaklin to the OpenID4VP 1.0 + HAIP 1.0 Verifier profile: polaris-oid4vp 1.0.0rc7, 24 September 2026 (listing). A self-certification the Foundation reviewed: not an endorsement, not an audit, and not a certification of the rest of Polaris.

Installable without cloning anything

Release candidates on PyPI and npm: pip install --pre polaris-verify, pip install --pre polaris-oid4vp, pip install --pre polaris-sdk-python, npm install polaris-sdk-ts@next. Each was verified by installing from the live registry into a clean environment.

Still missing

No independent security review by anyone who did not build this. No non-author operator has run it. No pilot, no real users, no real identity data. Distribution is not validation, and a green test suite is not proof of correctness.

Real cryptography

Which algorithm signed a token is data, not code: a foreign key into a first-class registry, so adding or retiring one is a row, not a redeploy. The shipped signer implements ML-DSA-65.

ML-DSA-65 signing

NIST FIPS 204 lattice signatures, Level 3, on every new token. Both shipped production paths sign with real liboqs bytes, cross-checked by a second implementation. A development run without that flag records a deterministic SHA3-256 placeholder under a label that says so, so a development signature can never pass for a real one.

SLH-DSA registered

FIPS 205 hash-based signatures rest on different assumptions than lattices, so both parameter sets are rows in the algorithm registry and a rotation is a row update. No SLH-DSA signer is wired yet; that gap is on the ledger in PQC-POSTURE.md.

Plonky2 ZK-SNARK

FRI-based, transparent setup. Proves epoch membership without revealing which token. The epoch root is recomputed bit-for-bit by an independent Python witness.

Post-quantum TLS edge

The public edge negotiates X25519MLKEM768 hybrid key exchange; CI proves the handshake on every push. What stays classical is mapped honestly in PQC-POSTURE.md.

WebAuthn MFA

Admin accounts enroll a FIDO2 credential against a per-account deadline; once enrolled, or once the deadline passes, a password alone cannot reach the admin role. The relying party offers ML-DSA-65 first.

What this is not

Not production-ready for real identity data. Every engineering gap the readiness ledger enumerated is closed and pinned by a check. What remains cannot be built here: nine decisions that belong to the deploying organization, two engineering limits carried openly, and the deployment-scale work the roadmap tracks phase by phase.

The nine decisions, each one a named accountable choice rather than a missing feature:

  • Legal basis, data-protection impact assessment, regulator approval
  • Signing-key custody and the rotation authority
  • The PostgreSQL high-availability topology and promotion policy
  • Encryption at rest on the host volume, and its key custodian
  • The offsite backup target, its retention and its schedule
  • The alerting backend and the named on-call rotation
  • The right-to-erasure policy against an append-only audit
  • The retention schedule per class and jurisdiction, and the counsel who says it satisfies the statute
  • An independent penetration test and threat-model sign-off

The bound on every claim on this page is the ledger itself: PRODUCTION-READINESS.md. This is a reference implementation on notional data; it holds no real identity record.

Run it

Evaluate it locally (macOS, Docker Desktop)

git clone https://github.com/EgorKhaklin/polaris-id.git polaris
cd polaris
./Polaris.command

The launcher pulls PostgreSQL, builds the image, loads the schema, runs the SQL self-tests and opens the console on notional data with development credentials.

Single-host compose profile (any Docker host)

./scripts/polaris-generate-secrets.sh
export POLARIS_DOMAIN=polaris.example.com
./scripts/polaris-deploy.sh prod
curl -fsS https://$POLARIS_DOMAIN/api/health

Five services behind a self-built TLS edge that provisions its own certificate. One host, so the database has no standby: that is one of the nine decisions above.

Linux server under systemd

sudo POLARIS_DOMAIN=polaris.example.com deploy/linux/install.sh

The same stack as a system service on Debian, Ubuntu or an RHEL-family host, installed by one script and exercised on Debian and Rocky in CI. Hardening is a separate, documented step: LINUX-SERVER.md then HARDENING.md.

Kubernetes reference profile

helm install polaris deploy/helm/polaris --namespace polaris

The same topology with enforced network policies and the restricted pod security standard, booted on kind in CI. It runs a single PostgreSQL replica; high-availability automation is roadmap work, not a shipped feature. KUBERNETES.md.

Evaluate it

The documents an assessor reads first, in the order they usually matter.