ENCLAVR DOCS

OpenBao in Enclavr

OpenBao is the source of truth for normal Enclavr configuration and secret reads. It stores complete namespace documents in KV v2 and gives each consumer an accepted, narrowly scoped identity.

OpenBao lifecycle showing cluster custody, consumer reads, writer edits, and verification.

Open the OpenBao lifecycle full size

The initialization and recovery runbook contains the CLI commands for first installation, lost local state, scoped identity recovery, snapshots, leadership changes, and quorum repair. Examples use <namespace> and a backup node named backup-host; replace placeholders with accepted inventory values.

Paths and custody

These paths have different owners and lifetimes. Deployment selects the node root explicitly. The administrator TUI discovers that deployed root from the running container's mounts; it does not search old checkouts or operator custody. Routine TUI authentication uses the accepted connection and human recovery share, without an initialization checkpoint path.

Location Contents and owner
Repository .env Protected runtime selectors. configr_openbao_config_file must name an absolute, accepted connection file. The selected file takes precedence over exported values per key.
$HOME/.local/state/enclavr-openbao-consumer/connection.json Conventional host consumer location; mode 0600. There is no automatic default or fallback if the selected path is missing.
$HOME/.local/state/enclavr-openbao-writer/connection.json Conventional separate namespace writer identity on namespace_writer_host.
$HOME/.local/state/enclavr/openbao-admin/ Example private administrator directory: cluster specification, initialization checkpoint, identity issuance checkpoints, migration and acceptance receipts. Preserve the directory actually selected during initialization.
$HOME/.local/share/enclavr/openbao/ Conventional explicitly configured node root: compose.json, config/ and persistent audit/. Linux and macOS have different home prefixes.
Repository .env-mwc-openbao Operator-owned JSON custody: namespace, cluster ID, pinned seal key, TLS CA and node material, recovery share and its initialization digest. Mode 0600; excluded from Git and Docker.
Node config/seal.json Only the namespace and existing pinned seal key; mounted read-only at /etc/enclavr-openbao/seal.json. No recovery share, CA signing key or external-store credentials.
Node config/server/server.json, config/ca.pem, config/server.pem, config/server.key Go-rendered server configuration and accepted TLS identity, mounted read-only under /etc/enclavr-openbao.
Docker volume enclavr-openbao-raft Raft database mounted at /var/lib/enclavr-openbao. It is outside the Git checkout; copying a repository does not back it up.
Node audit/ Mounted at /var/log/enclavr-openbao; HMAC-redacted audit log and rotation state. Docker diagnostic logs do not replace audit retention.
Container /run/enclavr-openbao Private memory-backed static-seal material; populated at startup, not a persistent backup.
backup-host:/srv/enclavr-openbao/snapshots/ Example restricted snapshot destination: identity.json, snapshot-status.json, daily/YYYY-MM-DD.snap, monthly/YYYY-MM.snap.
backup-host:/srv/enclavr-openbao/audit/ Example off-node audit archive, where an accepted collector is configured. See the collector limitation in the runbook.

Normal runtime omits configr_secret_store_provider or selects openbao. Missing, invalid or unavailable OpenBao connections fail closed.

Operator-owned custody

The operator supplies and retains .env-mwc-openbao as one protected JSON object:

{
  "version": 1,
  "namespace": "<namespace>",
  "cluster_id": "<accepted-cluster-id>",
  "seal_key_id": "<existing-pinned-identifier>",
  "seal_key": "<existing-32-byte-key-as-hex>",
  "cluster_tls": {"version": 1, "spec": {}, "ca": "<PEM>", "ca_key": "<PEM>", "nodes": {}},
  "recovery_share": "<existing-recovery-share>",
  "recovery_sha256": "<digest-pinned-during-initialization>"
}

This is a shape example, not usable key material or a complete topology. Existing clusters must retain their original seal, CA and recovery share. Enclavr validates the pinned identities and does not regenerate missing material for an initialized cluster. The file's parents must not be writable by other accounts; links are rejected. The operator may manually copy this JSON into their chosen backup, including 1Password. Enclavr performs no external custody synchronization or sign-in.

cluster_id is the initialized OpenBao UUID. The recovery share authorizes attended administration; the seal decrypts cluster state and snapshots. Neither substitutes for the other. Only the minimal seal JSON belongs on each voter. The combined operator file, recovery share and CA signing key do not belong on ordinary consumers, DNS publishers or backup collectors.

Logical references such as op://<namespace>/assets.<namespace>/document retain their spelling in Enclavr configuration even when OpenBao resolves them. They do not imply a runtime read from 1Password. OpenBao stores namespace items at KV v2 mount enclavr, with API paths enclavr/data/<namespace>/items/<item> and payloads under document. An Enclavr namespace here is the application's namespace prefix, not an instruction to create an OpenBao HTTP namespace.

Roles and authority

openbao_roles.roles[] in authoritative assets records each declared AppRole's purpose, owner host, exact protected connection path and scope. dns_topology.publication references these role names. Start with ./enclavr namespace status to inspect declarations and live leadership separately. Changing a declaration does not issue or rotate a credential. DNS publication verifies that the selected connection authenticates as the declared AppRole.

Private host A/AAAA records come from configured Tailscale-enabled hosts and their assigned tailnet addresses, including hosts without local Unbound. Unmanaged peers and transport endpoints are excluded. A partial or ambiguous discovery preserves the accepted resolver documents. Public authoritative zones retain explicitly declared public records; they do not discover hosts or add private listener addresses to an explicitly declared public NS set.

dns_topology.publication.standby_publishers declares additional publishers with their own accepted source and per-resolver writer identities. Each runs the same five-minute job independently and must cover the complete primary resolver set. Equivalent passes make no write; a changed candidate is bound to the resolver generation read before discovery and published with CAS and read-back. Publication continues when another publisher is offline.

Each resolver's existing DNS-fetch identity must also be declared in openbao_roles. Deployment uses that exact host-owned identity to read an accepted private manifest matching the current assets, preserving discovered host records during reinstall. Ordinary runtime credentials are unchanged.

Role Allowed scope Excluded authority
Host consumer Read the exact issued core, personal and service item list Namespace writes, identity issuance, cluster administration
Namespace writer Read/create/update the exact declared items; soft deletion only when explicitly granted Root generation, arbitrary policy changes, Raft administration
DNS reader / publisher Read / publish one hostname's enclavr/data/<namespace>/dns/<hostname> document Other hosts and namespace items
Caddy storage Caddy objects and locks under enclavr/{data,metadata,delete}/<namespace>/caddy-storage/ Namespace documents and Caddy document publication
Snapshot collector Read sys/storage/raft/snapshot Snapshot restore, namespace reads and administration
Cluster observer Read sys/storage/raft/configuration Leadership step-down and membership changes
Local audit identity Local self-inspection, capability checks and token revocation Namespace reads and cluster administration
Initial administrator root Bootstrap engine, policies, identities and migration Retained runtime use; revoke after acceptance
Attended recovery custodian Access recovery material; issue narrowly scoped identities through the recovery ceremony Distribution of recovery shares or root tokens to automation

Caddy needs both a published, version-bound configuration snapshot and its own storage connection. Restoring an old checkout supplies neither missing publication nor an unissued identity. A namespace writer cannot provision the storage AppRole.

What a normal host receives

A host receives an accepted 0600 connection file containing:

Enclavr checks transport and cluster identity before credentials are sent, authenticates for a short-lived token in memory, and ignores ambient OpenBao address, token, proxy, or CA variables as authority.

Do not author credentials manually or reuse another host's connection. Provisioning and consumer acceptance bind generated policy and connection state to one host. Routine reads and DNS publication use these persisted identities without attended custody.

Routine read path

  1. The protected .env explicitly selects openbao and the absolute connection path.
  2. Enclavr validates the connection and authenticates to the pinned cluster.
  3. It reads the four canonical runtime fields from the namespace's documents.
  4. It materializes and validates assets.<namespace>.
  5. Planning selects the exact current host without resolving action secrets.
  6. Deployment reads only the protected values needed by the selected actions.

Use these read-only checks first:

./enclavr namespace inspect --namespace '<namespace>'
./enclavr config validate
./enclavr host plan

Routine write path

Normal consumer credentials cannot update namespace documents. Changes run on the designated namespace_writer_host with a separate accepted writer connection:

./enclavr priv edit --namespace '<namespace>' --component assets

The workflow pulls a fresh snapshot, opens the selected protected components in Vim, validates the complete namespace, presents a value-free plan, asks for confirmation, applies check-and-set updates, and reads the result back. A concurrent change stops the write rather than overwriting it.

See Edit private data safely for the complete operator flow. For an attended handover to another host, follow Move the namespace writer.

Browser trust and the OpenBao web interface

Use Enclavr's role and permission forms for namespace-managed changes so the declared role registry and live ACLs remain consistent. The native OpenBao web interface is useful for inspection and attended administration, but direct browser changes do not run Enclavr's writer-host checks or update its assets registry. Browser sign-in remains manual: use a token belonging to the existing declared namespace-writer role for document management. On the exact writer, Enclavr's TUI inspection, browsing, editing and web-endpoint management select and verify the writer's persisted connection on every run. Ordinary host runtime connections retain their scoped consumer roles. A rejected writer connection stops the management workflow; it does not fall back to a consumer.

The native browser UI maintains its own session. Opening the private endpoint does not replace an existing browser login or automatically select its role; check the current identity before managing documents. Enclavr does not prepare, display or export browser tokens. The endpoint's four-hour shutdown does not determine the browser token's own expiry. The isolated proxy continues to carry opaque TLS without receiving credentials or private keys.

The required browser endpoint is https://openbao.<namespace>/ui/, hosted on the declared namespace writer. Its on-demand TLS passthrough proxy must have its own private virtual interface/address, network namespace and container workload, separated from internet-facing workloads. Port 443 belongs to that private network namespace; it must not share a host listener or Docker network with public ingress. The proxy uses OpenBao's existing server certificate and CA. It does not terminate TLS or need access to the server private key.

caddy-mask-proxy and AWS Global Accelerator must never receive, terminate or forward OpenBao web/API ingress. A Caddy route, shared SNI dispatcher, public listener with an ACL, or attachment to the public application network does not meet this boundary. Ordinary tailnet routing may carry traffic to the dedicated private endpoint; Tailscale Services and Let's Encrypt are not used. Activation requires an explicitly verified private route and isolation from public ingress. Private DNS and endpoint ownership must follow namespace_writer_host when the writer moves, with the old endpoint retired.

Launch ./enclavr → Config → Start OpenBao web interface. Review the current writer, private address and four-hour limit, then press y. Enclavr starts the endpoint on that exact writer, using existing SSH access when the TUI runs elsewhere, and displays the browser URL and automatic shutdown time in UTC. The equivalent CLI command is ./enclavr namespace web --yes.

The four-hour deadline is enforced inside the proxy, independently of the TUI. Repeated starts return the running endpoint's original deadline. Retrying a partial start or reconciling changed placement during that session cannot extend it. Expiry closes active connections and the listener; the OpenBao voter continues running. A fresh invocation after expiry starts a new session. There is no automatic restart at boot. A writer-side user systemd service renews a short ownership lease. Ownership changes, failed ownership checks, or loss of the supervisor stop the endpoint, with lease expiry bounded to 90 seconds. The supervisor uses the writer's existing accepted connection; the proxy receives only public routing/CA metadata and the lease.

Activation requires a provisioned private network and route. Declare hosts[].openbao_web on each eligible native Linux writer with network (existing Docker network name), address (dedicated private IPv4 address), and allowed_clients (explicit private source CIDRs). The Docker network must carry labels io.enclavr.purpose=openbao-management and io.enclavr.namespace=<namespace>, contain only the existing OpenBao voter and the temporary proxy, and be the voter's only Docker network. A label alone does not establish routing or firewall isolation: provision and verify those before activation. The proxy publishes no host ports and refuses sources outside the declared CIDRs. It reuses the installed voter's image with a standalone Go passthrough executable, without mounting voter data, credentials or keys. The writer needs Go, Docker access and an available systemd user manager.

The private Unbound record is derived from the current writer's declaration. Explicit public or private records for that name conflict with managed placement and fail validation. Publish and adopt the derived DNS manifests through the existing DNS workflow after declaring or moving the writer. Startup verifies that private DNS resolves only to the selected address, checks the isolated workload configuration and probes the accepted cluster over its existing TLS identity. It does not create Docker networks, subnet routes, DNS publications, ACL grants or OpenBao identities. Missing setup is reported before activation; production routing and isolation acceptance remain separate from automated tests. Certificate export below is independently available and does not establish that setup or grant browser login authority.

To retrieve the existing public CA certificate, launch ./enclavr → Config → OpenBao CA certificate. Inspect the namespace, cluster ID, CA subject, SHA-256 fingerprint and expiry. Select Certificate export path, enter an absolute filename under a private directory, accept it with Ctrl+S, then press r to review and y to export. This works on an enrolled host using its accepted local connection and requires no administrator session.

The file is saved on the host running Enclavr. Import it into the browser or operating system's trusted certificate authorities after checking the displayed SHA-256 fingerprint. The form does not install trust automatically. It exports only the pinned public root certificate, never connection credentials, CA signing keys or recovery material. It preserves an identical existing file and refuses to replace different content. No CA or server certificate is generated.

The equivalent CLI command is ./enclavr namespace ca --output /absolute/private/directory/openbao-ca.crt --yes. Omit --yes to inspect the export plan. Endpoint deployment and network access are separate from certificate export; exporting trust does not open a listener or grant OpenBao permissions. OpenBao documents the browser trust requirements in its web UI guide.

Cluster administration boundary

Use the OpenBao operations runbook for cluster administration. Enclavr operation requests use protected files, value-free plans and explicit --apply; initialization and recovery provisioning also require an attended terminal. Native snapshot restore and emergency Raft membership repair are separate upstream procedures, with no Enclavr restore command.

Production uses three or five explicit Raft voters. The isolated QA fixture is the only supported single-node mode and does not establish production acceptance.

Availability and recovery model

Failure guide

Symptom Correct response
Connection file missing or wrong mode Restore the accepted protected file; do not create a replacement by hand.
Cluster identity mismatch Stop. Verify the intended cluster and re-run the attended acceptance workflow.
No accepted endpoint available Diagnose cluster/network health. Do not fall back to 1Password.
Required item outside connection scope Reconcile the host identity through the administrator workflow; do not broaden policy ad hoc.
Namespace write denied Run on the designated writer host and verify its separate writer connection.
Concurrent document change Pull a fresh snapshot, reapply the intended edit, and review a new plan.

The upstream references page links to OpenBao's Integrated Storage, KV v2, AppRole, policy, health, and static-seal documentation.