Skip to content

The operator manifest reference

A UniDPP deployment is data. One versioned, validated file, which is the operator manifest (unidpp-operator.yaml), declares every knob of every service: binds, secrets, feature toggles, crypto-suite policy, branding, and the sovereignty declaration. Processes stay plain environment-configured; the manifest is rendered into their environment at launch. Zero code differs between the reference deployment and a tenant: the manifest is the product.

This page documents every field of the manifest schema (api_version: unidpp.org/v1). Each knob below has an anchor (#api_version, #services-issuer-pack_suites, …), and the field list is machine-checked against the schema’s exported JSON Schema by the coverage check in the docs repository.

YAML by convention; a leading { parses as JSON (the schema is identical). Secrets are ${VAR} references substituted from the environment at load — the manifest never carries token values.

unidpp-operator.yaml — the reference deployment
api_version: unidpp.org/v1
deployment:
name: unidpp-reference
profile: reference
base_url: https://registry.unidpp.org
branding:
organization: UniDPP
product_name: UniDPP Reference Deployment
services:
registry:
bind: 127.0.0.1:8390
state_file: registry-journal.jsonl
trust:
bind: 127.0.0.1:8391
state_file: run/trust-journal.jsonl
log:
bind: 127.0.0.1:8392
log_id: unidpp-pilot-log-1
state_file: run/log-journal.jsonl
external_tsa_url: http://timestamp.digicert.com
issuer:
bind: 127.0.0.1:8393
pack_suites: [ecdsa-p256, sm2]
registry_url: http://127.0.0.1:8390
projector:
bind: 127.0.0.1:8394
gateway:
bind: 127.0.0.1:8395
issuer_url: http://127.0.0.1:8393
archive:
bind: 127.0.0.1:8396
sovereignty:
external_calls: external

Four rules the loader enforces before anything starts:

  1. Unknown knobs are loud. A typo is a schema error naming the field and the legal alternatives, so that a typo is never a silent no-op:

    Terminal window
    $ unidpp-config validate bad-manifest.yaml
    unidpp-config: manifest does not match the schema: unknown field `binds`, expected one of `bind`, `admin_token`, `state_file`
  2. Secrets must resolve. ${VAR} substitutes from the environment; an unset reference is an error (secrets never silently resolve to empty). Variable names are ASCII alphanumerics and _; an unterminated ${... is an error.

  3. Semantic validation. Beyond shape: the API version, theme colors, pack suites, and the profile × egress rule (below) are checked.

  4. Profiles constrain. A sovereign deployment with any egress beyond none refuses to validate unless egress_override_reason records why, because data-sovereignty claims are validated rather than aspirational.

unidpp-config is the manifest CLI. All three subcommands are read-only over the file plus the environment:

Terminal window
$ unidpp-config validate unidpp-operator.yaml
valid: unidpp-reference (profile reference, 7 service(s): ["registry", "trust", "log", "issuer", "projector", "gateway", "archive"])
$ unidpp-config services unidpp-operator.yaml
registry
trust
log
issuer
projector
gateway
archive
$ unidpp-config render-env log unidpp-operator.yaml
UNIDPP_LOG_BIND=127.0.0.1:8392
UNIDPP_LOG_EXTERNAL_TSA_URL=http://timestamp.digicert.com
UNIDPP_LOG_ID=unidpp-pilot-log-1
UNIDPP_LOG_STATE_FILE=run/log-journal.jsonl

render-env <service> <manifest> prints exactly the UNIDPP_* variables the named service’s own environment-based configuration reads, one KEY=value per line in stable order. It fails when the deployment declares no such service block or the name is unknown. unidpp-stack launches processes (stack and tenant launches alike) from this rendering, which is how manifests drive unmodified binaries.


Type string
Required yes
Constraint must equal unidpp.org/v1
Example api_version: unidpp.org/v1

The manifest’s API version, which is the only version this generation of the schema speaks. Anything else is refused at load with the offending value named.

Type object (Deployment)
Required yes
Default —

The deployment identity and shape. Fields: name, profile, base_url.

Type object (Branding)
Required no
Default organization UniDPP, product name UniDPP Platform, no logo, default theme, empty footer

The whitelabel surface. The console (and any explorer surface) renders its chrome from these values; see the console manual for the preview. Fields: organization, product_name, logo, theme, footer, locale.

Type object (Services)
Required no
Default no services

Per-service configuration blocks. An absent service block means that service is not part of this deployment, and unidpp-stack will neither start it nor render its environment. Present blocks: registry, trust, log, issuer, projector, gateway, archive, console.

Type object (Features)
Required no
Default all toggles true

Cross-cutting toggles. Fields: untp_ingest, cddal_negotiation, presentation_render.

Status: declared, not yet consumed. The features block is part of the schema (typed, validated, unknown keys rejected), but as of this writing no service in the running stack reads it and render-env does not emit it. The toggles below describe the intent each knob will govern. Set them only if you accept that they are currently inert.

Type object (Sovereignty)
Required no
Default no residency pinned, egress none, no override reason

The sovereignty declaration: what may leave the box. Fields: data_residency, external_calls, egress_override_reason.


Type string
Required yes
Constraint must not be empty (or whitespace)
Example name: acme-eu

The deployment’s name, which is the tenant name, unique per operator. It appears in the console’s identity document and dashboard.

Type enum: reference | whitelabel | sovereign
Required yes
Example profile: whitelabel

The deployment shape. See deployment profiles:

  • reference — the hosted reference deployment.
  • whitelabel — an organization’s own branded instance.
  • sovereign — on-prem, jurisdiction-pinned, external calls off (or on only with a recorded reason — see egress_override_reason).

Type string (URL)
Required yes
Example base_url: https://dpp.acme-mobility.example.org

The public base URL of the deployment’s primary surface. Informational for now and is used in the console dashboard; the deployment’s own binds and upstreams are declared per service.


Type string
Default UniDPP
Example organization: ACME Mobility

The operating organization’s legal/display name. Rendered in the console chrome (header, login card) and carried by every branded surface.

Type string
Default UniDPP Platform
Example product_name: ACME 产品数字护照

The product name shown in chrome, which covers login, page titles and footers. Unicode is accepted (the sovereign CN tenant ships its product name in Chinese).

Type string (path or data URI), optional
Default absent (no logo; the organization name stands alone)
Example logo: "data:image/svg+xml;base64,..."

A logo the console and explorer serve. Consumed by the console chrome: when set, an <img class="logo"> renders before the organization name.

Type string, optional
Default en
Example locale: zh-CN

The console chrome’s language: nav, titles, login, cards, badges, and the primary buttons render through the console’s i18n table (en, zh-CN today; validated). A new language is a table entry plus the config crate’s SUPPORTED_LOCALES, and it is not a code change. Body prose is English in v1 (chrome-level i18n, honestly scoped).

Type object (Theme)
Default primary #0f62fe, accent #08bdba

Two hex colors. Fields: primary, accent.

Type string
Default #0f62fe
Constraint six-digit hex, #-prefixed (#rrggbb); anything else is refused

The primary color, used for headers, badges and the login card border.

Type string
Default #08bdba
Constraint #rrggbb (same as primary)

The accent color.

Validation error when a color is wrong:

Terminal window
unidpp-config: branding.theme.primary must be a #rrggbb hex color (got `red`)

Type object (Footer)
Default no links

Footer links. Fields: legal_url, contact_url.

Type string (URL), optional
Default absent
Example legal_url: https://www.acme-mobility.example.org/legal

The legal/imprint link rendered in the console footer.

Type string (URL), optional
Default absent
Example contact_url: mailto:dpp@acme-mobility.example.org

The contact link rendered in the console footer.


Each block below lists the fields and the environment variables render-env produces. Every service also accepts the common knobs; the service-specific knobs come after.

Type object (RegistryService = common + the seed knobs)

The ISO 19135 item + discovery registry. See the registry reference.

Manifest field Environment variable
bind UNIDPP_REGISTRY_BIND
admin_token UNIDPP_REGISTRY_ADMIN_TOKEN
state_file UNIDPP_REGISTRY_STATE_FILE
seed_on_demand UNIDPP_REGISTRY_SEED_ON_DEMAND
seed_express UNIDPP_REGISTRY_SEED_EXPRESS

Type object (TrustService = common + the fixture and signing-seed knobs)

The SIGNATIF trust-graph service. See the trust reference.

Manifest field Environment variable
bind UNIDPP_TRUST_BIND
admin_token UNIDPP_TRUST_ADMIN_TOKEN
state_file UNIDPP_TRUST_STATE_FILE
no_seed_fixtures UNIDPP_TRUST_NO_SEED_FIXTURES
dev_seed UNIDPP_TRUST_DEV_SEED
sign_seed UNIDPP_TRUST_SIGN_SEED
sign_seed_p256 UNIDPP_TRUST_SIGN_SEED_P256

Type object (LogService = common + log_id + external_tsa_url)

The transparency-log anchor service. See the log reference.

Manifest field Environment variable
bind UNIDPP_LOG_BIND
admin_token UNIDPP_LOG_APPEND_TOKEN
state_file UNIDPP_LOG_STATE_FILE
log_id UNIDPP_LOG_ID
external_tsa_url UNIDPP_LOG_EXTERNAL_TSA_URL
suite UNIDPP_LOG_SUITE
seed UNIDPP_LOG_SEED

Note the token’s variable name: the log calls it the append token (UNIDPP_LOG_APPEND_TOKEN) because appending commitments is its only mutation.

Type string
Default unidpp-log-1
Constraint 1-64 printable ASCII characters
Example log_id: unidpp-pilot-log-1

The log’s identity, which is the log_id inside every signed tree head and receipt and what a verifier pins along with the operator key.

Type string (URL), optional
Default absent (no external anchoring)
Example external_tsa_url: http://timestamp.digicert.com

An RFC 3161 timestamp-authority endpoint; every append’s tree head is also anchored there. An unreachable TSA degrades explicitly: the submission failure is recorded, never silently skipped.

Setting this on a sovereign deployment whose external_calls is none is a contradiction the validator refuses, even with an egress override reason on record.

Type object (IssuerService = common + pack_suites + registry_url)

The passport lifecycle issuer. See the issuer reference.

Manifest field Environment variable
bind UNIDPP_ISSUER_BIND
admin_token UNIDPP_ISSUER_ADMIN_TOKEN
state_file UNIDPP_ISSUER_STATE_FILE
pack_suites UNIDPP_ISSUER_PACK_SUITE (comma-joined)
registry_url UNIDPP_ISSUER_REGISTRY_URL
registry_token UNIDPP_ISSUER_REGISTRY_TOKEN
max_age UNIDPP_ISSUER_MAX_AGE
event_seed UNIDPP_ISSUER_EVENT_SEED
pack_seed UNIDPP_ISSUER_PACK_SEED
seed UNIDPP_ISSUER_SEED

Type list of strings
Default ["ecdsa-p256"]
Constraint at least one suite; no empty entries
Example pack_suites: [ecdsa-p256, sm2] — the co-signature policy

The sovereign pack-signing policy: one suite, or a co-signature set where every listed suite signs the same pack body (a pack verifiable in both circuit-styles). Known suites: ecdsa-p256, sm2. The default EU posture is ecdsa-p256; a Chinese sovereign deployment sets pack_suites: [sm2].

Type string (URL), optional
Default absent (no registry forwarding)
Example registry_url: http://127.0.0.1:8390

The registry the issuer forwards profile registrations and applicability bindings to (its /admin/profiles and /admin/applicability endpoints forward on success). Absent = the issuer keeps profiles locally.

Type object (ProjectorService = common + the resolution sources and the roll-up sealer)

The lens projection service. See the projector reference.

Manifest field Environment variable
bind UNIDPP_PROJECTOR_BIND
admin_token UNIDPP_PROJECTOR_ADMIN_TOKEN
state_file UNIDPP_PROJECTOR_STATE_FILE
registry_url UNIDPP_REGISTRY_URL
registry_token UNIDPP_PROJECTOR_REGISTRY_TOKEN
passports_dir UNIDPP_PROJECTOR_PASSPORTS_DIR
primmel_dir UNIDPP_PROJECTOR_PRIMMEL_DIR
rollup_seed UNIDPP_PROJECTOR_ROLLUP_SEED
rollup_attester UNIDPP_PROJECTOR_ROLLUP_ATTESTER

The projector reads passports, profiles, units, and transforms from its own environment (UNIDPP_PROJECTOR_PASSPORTS_DIR, UNIDPP_REGISTRY_URL, …) — those are deployment environment, not manifest fields; see the projector reference for the full list.

Type object (GatewayService = common minus state_file + issuer_url + the report/scan/timeout knobs)

The interop gateway. See the gateway reference.

Manifest field Environment variable
bind UNIDPP_GATEWAY_BIND
admin_token UNIDPP_GATEWAY_ADMIN_TOKEN
issuer_url UNIDPP_ISSUER_URL
feedback.journal UNIDPP_GATEWAY_FEEDBACK_JOURNAL
feedback.rate_per_minute UNIDPP_GATEWAY_FEEDBACK_RATE
scan_policy.token_ttl_secs UNIDPP_GATEWAY_SCAN_TTL_SECS
scan_policy.issue_limit_per_minute UNIDPP_GATEWAY_SCAN_LIMIT_PER_MIN
timeout_ms UNIDPP_GATEWAY_TIMEOUT_MS

The gateway renders rather than stores, and it declares no state_file. Its one journal is the consumer-report channel (below), append-only and replayed on start like every other service journal.

Type object (FeedbackPolicy), optional (defaults: no journal, no rate)
Example feedback: { journal: run/feedback-journal.jsonl, rate_per_minute: 10 }

The consumer report channel’s deployment policy. journal is the append-only JSONL path (absent = in-memory only, dev mode); rate_per_minute is the per-identifier admission window (0/absent = permissive; MobileQR’s captcha + SMS verification is a deployment’s pluggable choice behind the same interface, never architecture). Reports are receipted with sequence and instant; the public citation form withholds the reporter’s contact by a stated omission.

Type object (ScanPolicy), optional
Example scan_policy: { token_ttl_secs: 300, issue_limit_per_minute: 60 }

The scan-token gate: short-TTL bearer tokens with per-source issuance throttling guarding the render routes, declared as data rather than per-service code. Absent = the gate is open: public resolution is the default doctrine, and gating is what a deployment under load opts into (the reference pilot declares none). Every gate refusal states itself: an absent, unknown or expired token names the policy window (401); an over-limit issuance is a stated 429; issuance with no policy configured is a stated 409.

Type string (URL), optional
Default absent (fixture mode)
Example issuer_url: http://127.0.0.1:8393

The issuer upstream whose passports the gateway renders. Absent = the gateway serves its built-in fixtures only (issuer-upstream-with-fixture-fallback: when the upstream is unreachable the gateway answers from fixtures and says so in the render metadata).

Type object (ArchiveService = common + the snapshot directory and the log anchor)

The Tier-C notarized snapshot service. See the archive reference.

Manifest field Environment variable
bind UNIDPP_ARCHIVE_BIND
admin_token UNIDPP_ARCHIVE_ADMIN_TOKEN
state_file UNIDPP_ARCHIVE_STATE_FILE
snapshot_dir UNIDPP_ARCHIVE_SNAPSHOT_DIR
dev_seed UNIDPP_ARCHIVE_DEV_SEED
sign_seed UNIDPP_ARCHIVE_SIGN_SEED
log_url UNIDPP_LOG_URL
log_token UNIDPP_ARCHIVE_LOG_TOKEN
log_timeout_ms UNIDPP_ARCHIVE_LOG_TIMEOUT_MS

The archive’s snapshot directory (UNIDPP_ARCHIVE_SNAPSHOT_DIR) and its log upstream (UNIDPP_LOG_URL) are deployment environment; see the archive reference.

Type object (ConsoleService = common + the manifest path)

The admin console. See the console manual.

Manifest field Environment variable
bind UNIDPP_CONSOLE_BIND
admin_token UNIDPP_CONSOLE_ADMIN_TOKEN
state_file UNIDPP_CONSOLE_STATE_FILE
manifest UNIDPP_CONSOLE_MANIFEST

The console’s manifest path is environment, not manifest: UNIDPP_CONSOLE_MANIFEST points the console at the file it manages.

The manifest’s shape is published as a JSON Schema (draft 2020-12), generated from the model itself and never hand-maintained:

  • This page’s companion artifact: /operator-manifest.schema.json
  • Regenerate after changing unidpp-config: unidpp-config schema > public/operator-manifest.schema.json (the docs CI fails on drift)

The schema is exactly as strict as unidpp-config load (unknown fields are rejected everywhere). It describes shape only: fields are rejected everywhere). It describes shape only; semantic validation, which covers ${VAR} secret substitution, hex colors and the what editors’ CI preflight should call for the final word.

These four knobs appear on every service block (registry, trust, projector, archive, console carry exactly these; log, issuer, gateway add the knobs listed above).

Type string (host:port)
Required yes (on any present service block)
Example bind: 127.0.0.1:9390

The address the service listens on. render-env emits it as the service’s UNIDPP_<SERVICE>_BIND; a bad address makes the service exit at startup.

Type string — an ${VAR} reference, optional
Default absent (open dev mode)
Example admin_token: ${ACME_ISSUER_TOKEN}

The Bearer token guarding the service’s mutations and /admin/* endpoints. Absent = open dev mode: every mutation endpoint answers without authentication. This is the pilot posture; it is not a production posture. The value should always be an ${VAR} reference; inline secrets work at load time but defeat the doctrine (the console editor, for one, is built around references never resolving on screen).

Type string (path), optional
Default absent (state lives in memory only)
Example state_file: run/issuer-journal.jsonl

The service’s append-only JSONL journal, which is the audit log that replays on start. Absent = no persistence across restarts. Paths are relative to the process working directory; unidpp-stack launches from the pilot-data root, which is why the reference manifest says registry-journal.jsonl for one service and run/… for others.

Type object (ResolverService), optional
Default absent (no resolver)

The identifier-resolution service, which is the ninth declarable service. Fields: bind, admin_token, state_file, upstream (national-intermediary mode: the upstream resolver base URL), cache_ttl_secs (default 300), public_url. render-env emits the resolver’s own generic UNIDPP_BIND / UNIDPP_UPSTREAM / … names — the binary is unchanged.

Type object (ServiceCommon), optional
Default absent (no hub)

The translation hub, which is the tenth declarable service: a stateless signed relay between willing pairs divided by protocols (SI-3’s hub contract). Fields: bind, admin_token (kept for future use, because the hub has no mutations to gate), hub_id (the hub’s trust-graph node id, what relay signatures name; default unidpp-hub-1), seed (the relay-signing seed, an ${VAR} reference in production; absent = seeded-dev mode, which says so on every start), public_url. Nothing persists — after a relay the hub holds nothing. render-env emits the hub’s own names: UNIDPP_HUB_BIND, UNIDPP_HUB_ID, UNIDPP_HUB_SEED.

Type string (URL), optional
Default absent (the service is loopback-only)
Example public_url: https://registry.unidpp.org

The service’s public hostname when a tunnel or ingress fronts it. The console’s services matrix renders it as the service’s public link; absent renders loopback-only, and the console invents nothing. Declaring it does not create the tunnel: provisioning (tunnel token + DNS record) is an operator act, documented in the pilot repo’s README.

The gateway declares no state_file (it is stateless by design); the console’s is accepted by the schema but the console’s own state is the manifest file itself plus in-memory sessions.


Recall the status note: validated, not yet consumed by the running services.

Type boolean
Default true

Accept UNTP ingest on the gateway (POST /untp/ingest; see UNTP interop).

Type boolean
Default true

Serve the CDDAL dictionary form on content negotiation (registry collection reads with Accept: text/cddal; see the registry reference).

Type boolean
Default true

Serve the presentation render on the projector (GET /render; see the projector reference).


Type string, optional
Default absent (no residency pinned)
Example data_residency: EU — or CN, JP

The jurisdiction this deployment pins data to. Displayed in the console dashboard’s egress summary.

Type enum: none | tsa-only | external
Default none

The egress policy, which states what may leave the box:

  • none — nothing leaves the box.
  • tsa-only — only the RFC 3161 TSA submission (the log’s external_tsa_url).
  • external — upstream fetches permitted (the reference deployment’s posture: the issuer forwards to the registry, the gateway fetches the issuer, the archive anchors into the log).

Type string, optional
Default absent
Example egress_override_reason: CN AICPA timestamp regulation 2026-14

Required when a sovereign profile permits egress beyond none: the recorded, auditable reason. A sovereign manifest with external_calls: tsa-only and no reason is refused:

Terminal window
unidpp-config: profile `sovereign` with external_calls `tsa-only` requires sovereignty.egress_override_reason (recorded, auditable)

With the reason on record, the same manifest validates. The point is honesty: sovereignty claims that contradict the deployment’s actual egress do not load.


The three real deployments of the pilot workspace, all validated:

  • reference — the manifest shown above: seven service blocks in the excerpt (the live pilot manifest adds the console and hub blocks), dual pack suites, external egress.
  • whitelabel (acme-eu) — three services (registry 9390, issuer 9393, console 9389), EU residency, ecdsa-p256 packs, egress none, full branding block (theme #7c3aed/#f59e0b, footer links).
  • sovereign (acme-cn) — three services (registry 9590, issuer 9593, console 9589), CN residency, sm2-only packs, egress none, the policy enforced by the validator, not by convention.

See multi-tenant operations for how these run.

The docs repository carries a coverage check that extracts every field name from the exported operator-manifest JSON Schema (of unidpp-config, the model) and checks each has a documented anchor on this page. Run it from the docs repo:

Terminal window
npm run check:manifest

It prints one line per field and a final coverage: N/N fields documented. If a knob is added to the schema without documentation, the check fails — this page and the schema move together or not at all.