Skip to main content

zk-age-verifier

zk-age-verifier is a verifier service for EU age verification, accepting Longfellow zero-knowledge proofs over mdoc through the W3C Digital Credentials API. It runs as a sidecar HTTP service beside a consumer backend. A verdict contains one boolean per requested check. The service has no authentication and is intended to be reachable only from the consumer backend, not the browser or internet. It is experimental and unstable.

CI Docs PyPI License: Apache 2.0

Installation

pip install zk-age-verifier
uv add zk-age-verifier
docker pull ghcr.io/pipe23-org/zk-age-verifier:latest

Usage

The verifier needs a configured origin and at least one trust source.

[service]
expected_origin = "https://av.example"

[[trust.sources]]
pem = "/etc/zk-age-verifier/anchors"
python -m zk_age_verifier --config config.toml

POST /sessions opens a session and returns the navigator.credentials.get() argument under transports.dc.

$ curl -X POST http://127.0.0.1:8000/sessions \
    -H 'content-type: application/json' -d '{"checks": ["age_over_18"]}'
{"session_id": "tmcdOPmo7oCgd4AmmMFyYg",
 "transports": {"dc": {"digital": {"requests": [{"protocol": "org-iso-mdoc",
   "data": {"deviceRequest": "omd2ZXJzaW9u…", "encryptionInfo": "gmVkY2FwaaJ…"}}]},
   "mediation": "required"}},
 "expires_at": "2026-07-20T09:34:22.089682Z"}

The consumer backend relays the wallet response to POST /sessions/{session_id}/presentation. A verified and a failed verification both return 200.

POST /sessions/{session_id}/presentation
{"response": "<wallet response, base64url>"}

{"state": "verified", "result": {"age_over_18": true}, "verified_at": "<iso8601>"}
{"state": "failed", "reason": "decrypt-failed"}

GET /health returns {"status": "ok"} while the process is up.

GET /debug/transcript/{session_id} returns the transcript inputs stored for a session — the origin and the encryptionInfo string — with the handover hash and session-transcript bytes reconstructed from them, hex-encoded. It is a development route, unauthenticated like the rest of the service.

Configuration

Two TOML tables, [service] and [trust], passed with --config.

  • expected_origin (required) — the exact scheme://host[:port] origin the presentation asserts.
  • backend (default google-cpp) — the pylongfellow verifier backend, google-cpp or isrg-rust; an unknown or unbuilt name fails startup.
  • session_ttl_seconds (default 300) — session lifetime.
  • session_cap (default 1000) — live-session limit; POST /sessions returns 503 at the cap.
  • timestamp_skew_seconds (default 300) — proofs with a timestamp older than this fail stale-proof.
  • cors_allowed_origins (default []) — origins for which CORS headers are emitted.
  • trust.sources (required) — non-empty list; each entry sets one of pem (a PEM file or directory of issuer CA certs) or etsi_xml (an ETSI trusted-list URL).

A presented document-signer certificate must carry the keyUsage extension asserting digitalSignature; an anchor accepted as the issuer of a chained leaf must assert keyCertSign.

Configured trust sources merge into one anchor set. Any anchor in that set can vouch for a certificate that signs age credentials. The certificate layer carries no required marker restricting what an anchor's certificates may sign. ETSI TS 119 412-6 clause 6 places no type indicator on EAA signing certificates. The trust.sources list must name only anchors intended to vouch for age credentials. A mixed-purpose or broad list authorizes every CA on it as an age-credential issuer.

Environment variables ZK_AGE_VERIFIER_<SECTION>__<KEY> override scalar values; lists and nested tables come from the TOML file only. Environment variables take precedence over the TOML file, which takes precedence over the defaults.

Documentation

Full documentation: https://zk-age-verifier.readthedocs.io/

Development

uv sync
uv run pytest

make test-live runs the suite against a running server over HTTP. make test-container runs it against the built container image.

Status

You should not rely on this code.

  • End-to-end testing covers Chrome on one Android 16 device.
  • No rate limiting.
  • The session store is in-process.

License

Apache-2.0.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

zk_age_verifier-0.2.0.tar.gz (1.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

zk_age_verifier-0.2.0-py3-none-any.whl (336.6 kB view details)

Uploaded Python 3

File details

Details for the file zk_age_verifier-0.2.0.tar.gz.

File metadata

  • Download URL: zk_age_verifier-0.2.0.tar.gz
  • Upload date:
  • Size: 1.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for zk_age_verifier-0.2.0.tar.gz
Algorithm Hash digest
SHA256 9db241f5528c303fcc5d63806b2ff294a3a037e1b57869143d7b2e90c77dcedc
MD5 03b65a0359fb6306930e0cd8aaffb0b7
BLAKE2b-256 6ff0c02a3db8d9c2489944c2815efd6455f80d9076888a5c6a38834a998abb98

See more details on using hashes here.

Provenance

The following attestation bundles were made for zk_age_verifier-0.2.0.tar.gz:

Publisher: release.yml on pipe23-org/zk-age-verifier

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zk_age_verifier-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: zk_age_verifier-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 336.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for zk_age_verifier-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f1abd751b3d60a32884b5aece9f11dfb393928baeca9a6eb42df906ad41f38c4
MD5 a26eed195f3fa5193cce2d85e2b9e03f
BLAKE2b-256 f1d431448925372bd65207f588141880b0e702d0347ac59ebdf5e6ce9896fc0d

See more details on using hashes here.

Provenance

The following attestation bundles were made for zk_age_verifier-0.2.0-py3-none-any.whl:

Publisher: release.yml on pipe23-org/zk-age-verifier

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.0

2 files

0.3.0

2 files

This release

0.2.0 This release

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page