Pilot Deployment Profile

The supported shape for running a sovereign in a pilot. It is the v1 deployment profile of the Security Review for v1, made concrete, and CI rehearses it on every change (scripts/pilot_rehearsal.py, .github/workflows/pilot-readiness.yml).

A pilot run this way can produce the evidence that 1.0.0 does not claim: an operator outside Genesis Core holding its own keys, infrastructure, policy and revocation (see ops/plan-v1.0.0.md, Workstream 2).

Shape

Choice

Profile

Process

gunicorn genesis_mesh.na_service.wsgi:app (the container image’s entry point), configured only through environment variables

Instances

One instance on SQLite, or two or more on PostgreSQL with NA_HA_MODE=on behind a load balancer

Network

TLS terminated by the operator’s reverse proxy or load balancer; NA_PROXY_HOPS set to the number of proxies in front (0 if none)

Signing key

NA_KEY_PROVIDER=azure-keyvault with the instance’s managed identity, or env from the platform’s secret store; never a key file in HA

Operator keys

Generated by the operator on its own machines. One privileged key per person who may change trust; standard keys for automation that only evaluates and records

Governed workloads

BOUNDARY_POLICY_ENFORCEMENT=required, EVIDENCE_STORE=on

Clients

The TypeScript SDK (full governed lifecycle), Go or .NET SDK, or raw HTTP

Configuration

GENESIS_FILE=/config/genesis.signed.json
NA_KEY_ID=pilot-na-2026
NA_KEY_PROVIDER=azure-keyvault          # or env, with NA_PRIVATE_KEY_SEED from the secret store
AZURE_KEY_VAULT_URL=https://<vault>.vault.azure.net
NA_KEY_SECRET_NAME=na-signing-seed
DATABASE_URL=postgresql://...           # omit for a single SQLite instance (DB_PATH)
NA_HA_MODE=on                           # with PostgreSQL and two or more instances
OPERATOR_PUBLIC_KEYS_JSON='{"alice":"<base64>","ci":"<base64>"}'
OPERATOR_KEY_TIERS_JSON='{"alice":"privileged","ci":"standard"}'
BOUNDARY_POLICY_ENFORCEMENT=required
EVIDENCE_STORE=on
NA_PROXY_HOPS=1

All settings are in Configuration Reference. The database needs code-point (C) collation; see High Availability.

What the rehearsal proves

Every change runs this flow between two independently keyed sovereigns, each a gunicorn NA configured from the environment (one on SQLite, one in HA mode on PostgreSQL):

  1. Each operator generates its own root, NA and operator keys; only public material crosses between sovereigns.

  2. Sovereign A exports and validates B’s trust bundle, takes B’s key from B’s live /sovereign.json, and issues a scoped treaty. A standard-tier key is refused.

  3. B attests a member; A accepts it under the treaty and rejects a role the treaty does not grant.

  4. B revokes the member; A imports B’s signed feed and rejects the member. A replayed feed and a feed signed by another key are refused.

  5. A’s database is backed up, restored into a new instance and verified with na verify-db; the restored instance still holds the treaty and still rejects the revoked member.

The recovery drill (scripts/recovery_drill.py) does the same for PostgreSQL: pg_dump, pg_restore into a new database after the original is dropped, and a new instance verifying every record and decision.

Sizing and corporate networks

  • Rate limits are per client address. Each governed action makes one /admin/boundary/evaluate call and one evidence submission, and the admin limit defaults to 30 requests per minute. Behind a corporate proxy or NAT every client shares one address, and therefore one budget. Raise NA_RATE_LIMIT_ADMIN_PER_MINUTE (and NA_RATE_LIMIT_EVIDENCE_PER_MINUTE) to the pilot’s peak, for example 600, and keep NA_PROXY_HOPS correct so the limits apply to real client addresses.

  • TLS-inspecting proxies. Where outbound TLS is re-signed by a corporate proxy (Zscaler and similar), clients must trust the proxy’s CA: NODE_EXTRA_CA_CERTS=/path/to/proxy-ca.pem for the TypeScript SDK. Python 3.13 and later also reject proxy CAs that do not mark their basic constraints critical; use the operating system’s trust store through the truststore package for the CLI and Python clients, or run them from a network path without inspection.

  • Memory. One VM with PostgreSQL, two NA instances of two workers and a reverse proxy uses about 750 MB; give it 2 GB, or 1 GB plus swap for tests.

Operations

Task

How

Back up

SQLite: genesis-mesh managed backup; PostgreSQL: pg_dump -Fc (see Backup and Restore)

Restore

Into a new database or file, then genesis-mesh na verify-db before serving

Upgrade

Upgrade and Rollback: back up, upgrade, verify; rollback is a restore

Readiness

/readyz for load balancer probes: writable primary, schema version, key fingerprint

Revocation propagation

Import partners’ feeds on a fixed schedule (RFC-007); the stale window is the import interval

Audit

genesis-mesh managed audit-export; evidence export GET /admin/evidence/export, verifiable offline

Leaving

Operator Exit and Fork

Roles for the pilot

Fill these in before the pilot starts and keep them with the pilot’s runbook.

Role

Responsibility

Who

Operator

Holds the keys, decides recognition and revocation, runs the NA

pilot operator

Incident owner

First response to a security or availability incident; follows Incident Response Runbooks

to be named

Release owner

Ships a fix release (1.0.x) and confirms the upgrade path

to be named

Managing partner (if any)

Assists within Managing Partner Control Boundary

optional

Limits

  • The NA signing key cannot yet be rotated while keeping the sovereign’s identity (security review SR-06). Keep it in Key Vault; a compromise means a new identity.

  • Imported revocation feeds have no enforced maximum age (SR-07); schedule imports to the pilot’s tolerance.

  • Mesh routing nodes, the Rust gateway and beta surfaces are outside the profile (see Public Contract).