Backup and restore¶
The remote backup.restore operation remains unavailable. Runtime discovery and
the live parameter-schema endpoint omit it. Complete generated JSON, TypeScript,
CLI resources and OpenAPI retain its identity with available: false (OpenAPI:
x-zeroecho-available: false). Legacy remote
requests still require authorization and approval, then fail with
POLICY_REJECTION / BACKUP_RESTORE_UNAVAILABLE; no archive files are extracted.
Backup export, verification and explicit local staging are available. The offline operator procedure
below assumes a securely protected working directory used exclusively by PKI.
The built-in exporter now creates a version-1 logical backup from one frozen store state. Store mutations wait while that state is copied to private work storage; archive creation then proceeds after mutations resume. Export omits keyrings, locks, incomplete content, derived indexes and unrelated files. It does not inspect opaque accepted domain payloads for embedded secrets. Back up private keys, external CT journals and server-owned configuration/audit state separately.
The optional embedded SQL credential-search database is one such derived index. It is excluded from logical backups and rebuilt from recovered PKI records when an enabled store opens. Preserve the deployment's search configuration separately and allow time and disk space for rebuilding after restore.
EST server-keygen delivery ciphertext is also outside the logical PKI backup. Preserve its configured artifact directory together with the corresponding server-control state and enrollment credential keyrings during an offline backup and recovery. Pending delivery cannot be reconstructed by generating another key; missing ciphertext that is still required for retained delivery causes recovery to fail. Expired, released replay records do not require deleted ciphertext.
Use BackupService.createBackupSource or PkiStore.createBackupSource for the
file-backed API and close the returned source after consuming its repeatable
streams. verifyBackupSource checks the complete manifest, every entry and normal
offline store recovery. prepareBackup returns close-owned verified staging; it
does not activate it. The existing byte-array API is limited to 2 MiB by default.
Large artifacts remain on disk. The separately enabled HTTP streaming workflow
below uses the same verification and cold activation rules. The legacy
backup.restore identity remains disabled; the new operation is
backup.stream.restore.
Opt-in HTTP transfer¶
Remote transfer is disabled unless the server is started with
--backup-transfer-config /path/to/transfer.json. The separate version-1
document preserves the main server schema (version 8).
Start from the repository example docs/pki-backup-transfer-example.json
and choose every value for the deployment. In the packaged server, set
ZEROECHO_BACKUP_TRANSFER_CONFIG to the mounted configuration file.
Provision the artifact directory durably with owner-only access before startup. It must be exclusively used by this service, outside the PKI root, and preserved across restart. Server control, audit and signing-keyring paths must also be outside the replaceable PKI root and artifact directory. Overlapping storage is rejected. Storage must support atomic replacement and directory fsync.
The example permits 64 MiB archives, 256 MiB retained archive bytes, eight descriptors and two concurrent jobs. Each verification/restore job reserves the archive maximum plus two 4 GiB expanded trees. Workspace reservation is admission accounting, not an OS disk quota: provision additional recovery/index headroom and filesystem limits. The source archive remains charged to retained storage until closed. Artifact lifetime is one hour; each binary transfer has a five-minute deadline. All budgets are mandatory.
Use Backup and restore in the administration console:
- Create an export; retain its backup identifier and SHA-256.
- Download it once. An interrupted download consumes its handle; create a new export. The browser streams the attachment to its download destination.
- Select an archive and enter its backup identifier. The browser hashes fixed 64 KiB blocks, reserves an upload and sends the file without Base64 encoding.
- Verify the uploaded archive on the server. Independently establish provenance; integrity verification does not authenticate its origin.
- Stage restoration. Request approval, obtain an independent decision, then retry with the approval identifier.
- Preserve the displayed restore workflow identifier. Restart the server through
the normal operational procedure and inspect the outcome. Only
ACTIVATEDacknowledges activation. AcknowledgeACTIVATEDorREJECTEDafter review.
UNCERTAIN requires reconciliation after restart. Never infer rejection from
a timeout or blindly retry restoration. The server persists exact manifest and
restore identities. A provably unpublished attempt is cleaned by the domain
coordinator before becoming REJECTED. Unresolved outcomes keep their metadata
quota until acknowledged, even after the archive expires.
Every action requires authentication, permissions and applicable approval policy.
Handles are principal/realm-bound and grant no authority on their own. Restore
always requires high-risk approval. Browser POSTs must have the exact configured
origin. Nonbrowser clients without Origin must send X-ZeroEcho-Transfer: 1.
Handles belong in request bodies or headers, never URLs.
Binary routes are POST /admin/v1/backup/upload (application/zip,
X-ZeroEcho-Artifact, optional X-ZeroEcho-Approval) and
POST /admin/v1/backup/download (URL-encoded artifactHandle, optional
approvalId). Other steps use the generated backup.stream.* catalog.
The remote CLI supports those JSON workflow steps, but has no binary file
transport. Direct CLI upload/download attempts fail locally with
BACKUP_BINARY_ROUTE_USE_WEB_CONSOLE_OR_HTTP, before opening a connection;
use the console or these HTTP routes for archive bytes.
Download responses declare exact length, so truncated bodies fail.
nginx disables request/response buffering for these routes and retains
authenticated certificate forwarding.
Cancellation closes network streams; domain work retains quotas until it returns.
Failed, consumed and expired artifacts are cleaned on startup and subsequent
admission/status/download cleanup. Expiry prevents new use immediately; active
leases pin their files. Legacy JSON verification is capped at 2 MiB decoded
archive data and reports BACKUP_LEGACY_LIMIT_USE_STREAMING above that bound.
The ordinary HTTP body limit may reject the request earlier. The existing JSON
value limit (21,846 characters, about 16 KiB of Base64 archive data) is also
preserved. Direct legacy backup requests report BACKUP_LEGACY_LIMIT_USE_STREAMING
when that parser bound is exceeded; unrelated and approval-envelope parse failures
retain their existing generic validation response. Legacy export uses the same
backup-specific code above 2 MiB.
Local staged restoration¶
BackupService.stageRestore(source) and PkiStore.stageRestore(source) verify
the archive and publish a durable STAGED request while the current store remains
open. Close the store and reopen it to activate. Inspect latestRestoreStatus():
only ACTIVATED acknowledges completed activation. FAILED means the old root
was retained or restored. UNCERTAIN means a publication could not be acknowledged;
do not resubmit it. Close and reopen for deterministic reconciliation. The legacy
filesystem-store adapter returns success=false plus RESTORE_RESTART_REQUIRED
when staged. The legacy service adapter remains disabled so older store plugins
cannot bypass the new protocol.
Restoration rejects any regression of known live safety authority: signing namespace, metadata values and record revisions, transaction outcomes, revocations, and retained workflow records must agree. Store revision and signing-time watermark must not decrease. This conservative comparison also rejects harmless metadata differences. Missing or corrupt live authority is not evidence that rollback is safe. Writes after staging are checked again at cold activation. A historical backup therefore normally cannot replace a PKI that has issued, revoked or advanced durable workflow state since that backup. Carry-forward merging is not implemented. Authentication of the archive remains the operator's responsibility.
The comparison also includes immutable certification requests in
requests/by-id. A backup predating an imported request is rejected even when
that request has not yet been issued or referenced by an enrollment transaction.
Importing a request after staging can likewise prevent cold activation. This
conservative rule preserves enrollment inputs held outside the server-control
records; it does not merge newer requests into an older backup.
The store parent must be writable on the same filesystem as the store and support
atomic directory moves and directory fsync. The live root must be a normal child
directory, not a volume mount point. The external .store.restore-control
directory owns the process lock, journal and recovery intent. Unique stage,
rollback and failed roots are siblings. Rollback roots are retained for operator
inspection; never delete them while reconciliation is pending. Every new-version
open holds the external lock throughout its lifetime and retains compatible root
locks during activation. Existing legacy owners are excluded, but no legacy
binary may be launched concurrently with cold activation, including bootstrap
helpers. Colocated keyrings, control logs, audit state and unknown files cause
restore rejection rather than being silently stranded in the rollback root.
Archive streaming uses fixed-size buffers. Safety comparison replays one metadata recovery graph at a time, capped by the existing checkpoint-entry limit, and hashes bounded file inventories. It is not constant-memory recovery: logical records and transaction outcomes require memory proportional to their counts. File inventory ordering costs O(E log E); replay and hashing scale with retained ledger and file bytes, including bounded repeated reads of shared transaction frames. The inherited transaction-outcome map can have quadratic worst-case collision costs; no constant-heap or throughput claim is made for recovery.
Verification proves internal consistency and detects corruption. It does not
authenticate a backup: an attacker can recompute its manifest. Compare the returned
whole-archive SHA-256 with an independently trusted commitment or signature before
trusting its provenance. Old unversioned ZIP backups, including VERSION-only
archives, are rejected. See Backup format and limits.
This page is the operator-facing procedure for backing up and restoring the PKI server. Background on the platform's persistent volumes is in Deployment → Operations.
What to back up¶
| Volume | In-container path | Priority | Notes |
|---|---|---|---|
| Store | /var/lib/zeroecho/store-container |
Critical | PKI root plus external server-control state; not a complete backup of keys, CT, audit or configuration. |
| Keyring | /var/lib/zeroecho/keyring |
Critical | CA private keys. Back up separately with stricter access control. |
| Audit | /var/lib/zeroecho/audit |
High | Append-heavy. Retain per your audit-retention policy. |
| Artifacts | /var/lib/zeroecho/artifacts |
Lower | Reconstructible by republishing from the store after a restore. |
Always back up the store and keyring together. Restoring a store without a matching keyring means the CA keys are gone; restoring a keyring without its store means there is nothing to sign against.
Before you start¶
- Record the current value of
ZEROECHO_KEYRING_PASSWORD. The keyring backup is useless without the correct password. - Store the keyring backup and its unlock password under separate access control — but ensure both are retained so you can recover.
Consistent backup procedure¶
Use the logical snapshot API described above for a running store. A physical file-by-file copy requires every writer to be stopped. Storage snapshots need deployment-specific consistency across all related roots; a snapshot's ability to recover after a crash does not make rollback safe. Retain independent signing authority outside the snapshot domain.
Restore procedure¶
The supported local path is verification, stageRestore, a STAGED receipt,
store close, then cold reopen and confirmation of ACTIVATED. See
Local staged restoration. Preserve the current root,
external control state and rollback roots while reconciling. There is no generic
unpack-and-start procedure that safely replaces those checks.
Disaster recovery¶
Recovery after losing the live safety baseline requires an operator-specific assessment of issuance, revocation, consumed identifiers, signing state and external effects. Verification of an archive alone does not prove that it is safe to resume signing. Preserve all surviving authority and retain an offline copy before changing storage. Do not overwrite the live root or initialize fresh stateful authority to bypass a restore rejection. Carry-forward merging is a future extension, not an available recovery mode.
Verification¶
After any restore, verify a representative set of operations through the admin console or CLI before putting the instance back into service:
ca.inspecton each authority — confirm its expected lifecycle state.publication.liston each authority — confirm artifacts are present.- Issue a test certificate using the restored authority and verify it with the authority's certificate chain.
Stateful signing authority¶
Applications using LMS/XMSS durable signing must preserve their local authority outside every backup/restore and snapshot domain. Never restore or clone it alongside a keyring. A stale keyring is rejected against the surviving authority; loss or uncertain history of that authority retires its signing keys. Restoring key material onto fresh infrastructure does not restore this permission. See the stateful-signing contract.