This directory is a fail-closed, one-warden-per-host Docker Compose deployment. It is not the fault-injection topology in the repository root. Run one independently provisioned instance on each failure domain and connect nodes only through the HTTPS endpoints in the signed manifest.
The manifest deliberately refuses to start with the built-in raw-seed/static-token runtime. A
production image must contain an installed lets.runtime_providers entry point that supplies a
managed Ed25519 signer, a production identity authenticator, an independent monotonic authority
anchor, and a durable external audit sink, and that provider must return production_capable=True.
The vendor-neutral generic-production adapter can call an operator-controlled signer helper and
use short-lived EdDSA identity tokens; cloud KMS, PKCS#11, OIDC, SPIFFE, PKI, and managed audit
integrations remain deployment packages. Derive and lock an operator image containing the selected
provider and signer helper, publish it by digest, and set LETS_RUNTIME_PROVIDER to its entry-point
name. Provider-specific non-secret options belong in the node configuration or an audited Compose
override; provider credentials belong in the platform secret/identity system, never this
environment file. See the provider contract.
Before starting a node, create and protect:
LETS_STATE_DIR, containing the SQLite database, node lock, and any schema-specific replay state. Put it on a dedicated filesystem or an actually enforced quota with no unrelated writers. The directory must be writable only by container UID/GID 10001 and its operator backup principal. Production admission requires explicit positivemin_free_disk_bytes,max_database_bytes, andreserve_pagesvalues inconfig.json. Declare the independently enforced limit asLETS_STATE_CAPACITY_BYTESand its mechanism asLETS_STATE_STORAGE_BOUNDARY; the validator refuses a limit too small for the logical main database cap, one full worst-case transaction WAL, its WAL-index, and the emergency floor. A quota can enforce that capacity limit, but it does not replace the separately mounted state filesystem required for rollback/failure isolation.LETS_CONFIG_FILE, an operator-reviewed copy of the generated configuration outside every writable runtime domain. Stage it with the repository helper below. The runtime bind-mounts this file read-only over/var/lib/lets/config.json; it must identify the state database by the absolute container path/var/lib/lets/warden.sqlite3. Never run from the writable generated copy left inLETS_STATE_DIR.LETS_AUTHORITY_DIR, on an independent monotonic/fenced storage domain. It is mounted at/var/lib/lets-authorityand must not be restored from the same snapshot as the state database. The production runtime/provider integration must bind its authority anchor here.LETS_AUDIT_DIR, containing the provider's independently durable audit archive. It is mounted at/var/lib/lets-audit; initialize it with the selected provider's provisioning command before node initialization, back it up independently, and retain it for the system audit lifetime. Declare a dedicated filesystem or enforced quota, its byte capacity, an evidence-based expected daily growth rate, the commissioned forecast duration, and an emergency free-space floor through theLETS_AUDIT_*values. The validator rejects a boundary that cannot hold the current archive plus that complete append-only lifecycle forecast.LETS_BACKUP_DIR, on a protected backup domain separate from state, authority, audit, and trust. Only the networkless one-shot maintenance service mounts it at/var/lib/lets-backup; the live warden has no backup-domain access. Recovery bundles are written here and then copied to immutable off-host storage. A backup on the state filesystem is not a disaster-recovery copy.LETS_TRUST_DIR, containing the operator-signed manifest and immutable trust material. It is mounted read-only at/etc/lets/trust; paths stored inconfig.jsonmust use that mount.- A server certificate/key, inbound client CA, outbound peer CA, and outbound peer certificate/key.
The server certificate must include
LETS_SERVER_NAME, peer certificates must chain to the configured client CA, and private-key host ACLs must exclude other users. All certificate, CA, config, and trust parents must be controlled by a host identity other than runtime UID 10001 and must not be group/world writable; otherwise that UID could stage a replacement for recreation. - An immutable multi-architecture image reference of the form
image@sha256:<digest>.
Keeping the authority anchor outside the database backup domain is a correctness requirement, not merely hardening. Restoring an older database together with its anchor can resurrect already-spent authority.
The environment therefore requires LETS_AUTHORITY_STORAGE_BOUNDARY=fenced-filesystem,
LETS_BACKUP_STORAGE_BOUNDARY=dedicated-filesystem, and four pairwise-distinct
LETS_*_ROLLBACK_DOMAIN identifiers. Each identifier must name the actual controller plus
volume/dataset, for example zfs://authority-pool/warden-a; it is not a free-form role label.
Validation rejects a missing, malformed, or reused identity. Device numbers and bind-mount paths
cannot prove full control-plane independence because coordinated snapshots can span otherwise
distinct host devices. The validator nevertheless requires all four directories to report distinct
mounted-filesystem device identities; an enforced quota never waives that minimum. Before
commissioning, attach evidence that the declared identifiers are governed by separately scoped
snapshot/restore operations and credentials, and demonstrate that restoring each domain cannot
roll back any other. A false identifier defeats the stale-restore guarantee and is an unsupported
deployment, even if the validator passes.
Use dedicated Linux filesystems whose documented guarantees include local POSIX advisory locks,
SQLite WAL shared-memory locking, atomic same-directory rename/link, and durable file plus directory
fsync. Do not place the state database, generic file anchor, or generic SQLite audit archive on
NFS, SMB/CIFS, a desktop file share, or an eventually consistent object mount. Never attach one
state directory to two containers or hosts. Each declared local storage directory must be the exact
Linux mountpoint, must contain no nested mount, and must not use a network, overlay, RAM, or desktop
share filesystem. All Compose binds use private propagation. Run the validator on the Linux Docker
host; Docker Desktop storage is deliberately not admitted as a production durability boundary.
The validator proves path and mounted-device
separation, not storage-controller grouping, snapshot policy, quota enforcement, or filesystem
semantics; record those properties in deployment evidence. Sites that cannot provide them must
supply remote linearizable anchor/audit provider
bindings and a supported database storage boundary rather than declaring the generic file profile
safe.
For the bundled generic-production adapter, put the executable signer bridge in the immutable
operator image and identity verification keys in /etc/lets/trust/identity-keys.json. Initialize
the independent audit archive, then run provider-backed node initialization inside the same mounted
container boundary (replace public IDs/keys and capacity values):
docker compose --env-file /etc/lets/warden-a/compose.env \
-f deploy/production/provision-compose.yaml run --rm --no-deps provision \
lets-provider audit-init --path /var/lib/lets-audit/audit.sqlite3
docker compose --env-file /etc/lets/warden-a/compose.env \
-f deploy/production/provision-compose.yaml run --rm --no-deps provision \
lets --config /var/lib/lets/config.json init --production \
--warden-id warden-a \
--manifest /etc/lets/trust/manifest.json \
--operator-key operator-2026=REPLACE_WITH_BASE64URL_PUBLIC_KEY \
--min-free-disk-bytes 1073741824 \
--max-database-bytes 10737418240 \
--reserve-pages 256 \
--runtime-provider generic-production \
--runtime-option 'signer_command_json=["/usr/local/bin/lets-hsm-sign"]' \
--runtime-option signer_key_id=REPLACE_WITH_MANIFEST_KEY_ID \
--runtime-option signer_public_key=REPLACE_WITH_BASE64URL_PUBLIC_KEY \
--runtime-option identity_keys_file=/etc/lets/trust/identity-keys.json \
--runtime-option identity_issuer=https://identity.example \
--runtime-option identity_audience=lets-warden \
--runtime-option authority_anchor_path=/var/lib/lets-authority/warden.anchor.json \
--runtime-option audit_archive_path=/var/lib/lets-audit/audit.sqlite3
sudo .venv/bin/python deploy/production/stage_config.py \
--source /srv/lets-state/warden-a/config.json \
--destination /etc/lets/warden-a/config.jsonThe signer helper receives bytes on standard input and must emit only the unpadded base64url
Ed25519 signature. Production initialization proves the signer, verifies it is a current local key
in the signed manifest, creates no raw key file or bootstrap bearer, and persists these non-secret
provider references in config.json. Protect the audit archive and anchor before this step; a
partially initialized directory is deliberately not overwritten on retry. The provisioning
Compose service has no network and does not mount the staged runtime config. Providers that need a
remote KMS during provisioning require an operator-reviewed network override. stage_config.py
creates its destination exclusively, canonicalizes the JSON, rewrites the state database (and the
legacy replay database when present) to absolute container paths, rejects development trust,
fsyncs the result, and removes all write bits. Run it through the repository-local virtual
environment as a trusted host operator; never stage into LETS_STATE_DIR.
The production validator treats the exact OCI digest as the signer-helper code identity. It rejects shells, interpreters, environment launchers, dynamic loaders, parent traversal, and command arguments that load from writable or ephemeral mounts, then proves inside a CPU/memory/PID-bounded, networkless container that the selected provider entry point loads and the dedicated helper is a regular executable in that image. Put fixed code and public configuration in immutable image/trust paths; pass secrets through the platform identity/secret boundary, never command-line arguments.
When deploying from release assets rather than a source checkout, extract the verified
lets-deployment-X.Y.Z.tar.gz, create a dedicated virtual environment, and install the matching
verified wheel into that environment before running the staging helper. Do not install the wheel
into the system interpreter:
python3 -m venv /opt/lets-release/X.Y.Z/venv
/opt/lets-release/X.Y.Z/venv/bin/python -m pip install --no-deps \
./lets_agent-X.Y.Z-py3-none-any.whl
sudo /opt/lets-release/X.Y.Z/venv/bin/python \
lets-deployment-X.Y.Z/deploy/production/stage_config.py \
--source /srv/lets-state/warden-a/config.json \
--destination /etc/lets/warden-a/config.jsonCopy .env.example to a protected file outside the repository and replace every placeholder.
After provisioning and config staging, pull the exact image, then run the repository-local
validator and render Compose before changing live state. The validator executes the image itself
with no network, a read-only root, no capabilities, and the production SQLite admission check:
docker compose --env-file /etc/lets/warden-a/compose.env \
-f deploy/production/compose.yaml pull
python deploy/production/validate.py --env-file /etc/lets/warden-a/compose.env
docker compose --env-file /etc/lets/warden-a/compose.env \
-f deploy/production/compose.yaml config --quiet
docker compose --env-file /etc/lets/warden-a/compose.env \
-f deploy/production/maintenance-compose.yaml config --quiet
docker compose --env-file /etc/lets/warden-a/compose.env \
-f deploy/production/compose.yaml up -d --waitThe service runs as UID/GID 10001 with a read-only root filesystem, no Linux capabilities,
no-new-privileges, bounded processes/memory/CPU/file descriptors, a no-exec tmpfs, bounded JSON
logs, bounded request concurrency/body receipt/keep-alive/shutdown, and a 120-second container stop
window. That window covers the maximum admitted 40-second server graceful shutdown, the
audit-exporter stop, and the peer dispatcher's maximum 60-second request plus SQLite/poll/join
allowance before Docker can kill the process. Do not shorten it without proving the complete
configured shutdown inequality. The separately staged config is a nested read-only bind over the
writable state directory, so the runtime UID cannot replace it across a restart. It publishes TLS
only. The readiness probe performs hostname-verifying TLS with a client certificate and accepts
only the exact LETS ready document.
The request concurrency limit defaults to 64 and the TCP accept backlog to 128 in both the core
server and this profile. Override LETS_LIMIT_CONCURRENCY or LETS_BACKLOG only after measuring
the complete TLS/provider/SQLite path under the configured CPU, memory, file-descriptor, and PID
limits; increasing either value can raise memory and shutdown pressure.
The total pre-authentication request-body deadline defaults to 30 seconds. Set
LETS_REQUEST_BODY_TIMEOUT_SECONDS from 1 through 300 only after measuring legitimate uploads over
the complete TLS path. A client that does not finish its body within that deadline receives a
408 request_body_timeout problem and its connection is closed; the keep-alive timeout does not
replace this body-read deadline.
The supplied profile uses a 60-second outbound peer request deadline. Production validation
requires LETS_PEER_REQUEST_TIMEOUT_SECONDS, admits only 30 through 60 seconds, and still performs
one HTTP attempt per durable dispatcher attempt. A timeout therefore leaves the authoritative
transfer PREPARED and records a failed delivery for bounded retry; it never implies that a
response was not committed remotely. For the bundled generic-production provider, admission also
requires the peer deadline to cover four authority-anchor calls, four signer calls, two SQLite busy
allowances, and a fixed scheduling/TLS margin. The exact floor is
4 * authority_timeout_s + 4 * signer_timeout_s + 15 seconds. Increasing signer or authority
timeouts without increasing this deadline is rejected, as is a combination whose derived bound
exceeds the 60-second production ceiling. The peer timestamp is authenticated on arrival before
target-side storage work; do not route peers through an intermediary that can hold a signed
request beyond the accepted clock-skew window.
Startup deliberately performs full SQLite integrity, foreign-key, authority-anchor, and audit
admission before serving. The healthcheck start period defaults to ten minutes; it does not weaken
or skip those scans. Benchmark a worst-case cold start against a restored database at the planned
logical cap on the actual storage class, then set LETS_HEALTH_START_PERIOD_SECONDS above that
measured bound (up to the validator's 24-hour ceiling). An orchestrator that kills or replaces the
process before this period expires can turn safe admission into a restart loop.
Process-file authority admission also performs an exact durable checkpoint confirmation. If helper
transport fails during construction or reopen, the database and anchor remain preserved but that
store instance is not admitted; close it and perform a fresh open after repair. After successful
admission, only a typed, well-formed helper-transport fault may recover on a later explicit
transaction after its monotonic cooldown. The original operation is not retried inside the store.
The helper's start, correlation, lock/I/O, response, and reset share one absolute deadline, and an
uncertain mutating reply must be reconciled and durably confirmed before the store becomes healthy.
Anchor semantic or protocol rejection, malformed transport metadata, and other provider failures
remain sticky until operator repair and a fresh process/store lifetime. These rules are identical
for core and protected-executor anchors. The helper wire introduced in 1.0.5 and retained in 1.0.6
adds exact request correlation and confirm; never pair a pre-1.0.5 helper executable with a
1.0.5-or-later parent, and use the matched parent/helper pair from one release artifact.
Authenticated metrics include the bounded authority_anchor state and counters. Operators with
lets.admin, lets.warden.admin, or lets.metrics.read may read the same snapshot from
GET /v1/maintenance/authority-status. That endpoint performs no SQLite or authority-admission
work: it deep-copies the most recently completed status published through a separate snapshot
lock. While a reconcile is in progress it may show the prior complete state. The admin-only
POST /v1/maintenance/authority-fence remains the exact terminal barrier: it atomically publishes
the fenced warden/PID/lifetime status and permanently stops new authority transactions for that
process lifetime. It is idempotent only for the same restart_id and expected_lifetime_id; a
different identity conflicts. This endpoint is for host-orchestrated replacement after traffic is
already removed, not a general un-fence mechanism.
The supplied 1 GiB profile defaults to 64 concurrent requests. Raise that only after a representative TLS, signer, SQLite, audit-export, and partition-recovery load test demonstrates bounded memory and shutdown latency within the configured cgroup. A larger number is not a throughput guarantee.
The maintenance Compose service mounts the backup domain, but has no network, port, restart policy, or long-running command. Use it only after stopping the warden; the shared node lock rejects concurrent authority access. Providers that require a remote KMS need an operator-reviewed network override for that one command. Remove the one-shot container after every invocation and copy completed bundles to immutable off-host retention promptly.
The 16 MiB /tmp mounts are only for bounded library temporaries. Recovery verification,
migration copies, and restore quarantine use the existing bundle or backup parent under
/var/lib/lets-backup; preflight that separate filesystem for the documented peak. No full
database scratch copy is written to the runtime tmpfs.
Compose file-backed secrets are an interoperability floor. On platforms where Compose cannot honor secret UID/GID/mode metadata, secure the host files with ACLs and confirm UID 10001 can read only the intended files before start. Prefer the orchestrator's native secret mechanism for a managed deployment.
The published runtime targets Linux amd64 and arm64. This manifest's UID/GID, capability,
tmpfs, signal, and bind-mount controls require a Linux container runtime. Docker Desktop on Windows
or macOS is useful for render/smoke testing, but its VM and desktop lifecycle are not an independent
production failure domain; use Linux hosts or translate the same controls into a supported
orchestrator. The Python package remains cross-platform for client/tooling use.
The repository keeps the cleartext Toxiproxy topology at the root as a fast development fault
harness. The separate production-profile acceptance builds three hardened wardens with required
TLS/mTLS, the bundled generic-production provider, executable subprocess signers, short-lived
EdDSA JWT identities, independent state/anchor/audit volumes, and a TLS-preserving network
partition. It also runs a protected executor with its replay database and monotonic anchor on
independent volumes, verifies and claims a live signed receipt, rejects that receipt after reopen,
and proves that restoring the pre-claim executor database is rejected against the advanced anchor.
The cluster checks reject missing and untrusted client certificates plus expired JWTs, kill and
restart a warden during the partition, and require durable transfer/checkpoint convergence.
Run it directly from the repository-local environment:
python deploy/production/run_acceptance.pyOr include it through pytest by setting LETS_RUN_PRODUCTION_ACCEPTANCE=1 and selecting the e2e
marker. The runner generates test-only PKI and seed-backed signer material inside ephemeral Docker
volumes, validates the exact project volume set before cleanup, and writes sanitized evidence to
results/generated/production-profile-acceptance.json. Set
LETS_KEEP_PRODUCTION_ACCEPTANCE=1 only while diagnosing a failed run; the material is not suitable
for any deployment and must be removed afterward.
Release automation publishes a unique multi-architecture candidate first and sets
LETS_PRODUCTION_ACCEPTANCE_IMAGE to that exact name@sha256:index-digest. The harness then proves
that all three wardens ran that candidate while keeping only material generation and the scenario
driver in the local test image. Evidence records both the configured registry digest and the one
platform image ID; a mismatch fails the release before scan, signing, or tag promotion.
The warden authorizes effects but deliberately does not run them. Every production executor must
own a filesystem-backed SQLiteReceiptReplayStore and an ExecutorAuthorityAnchor in separate
failure and rollback domains. Initialize the store once with the exact audience, tenant, envelope,
and configuration epoch accepted by its ExecutorPolicy. Derive the identity with
executor_replay_identity(policy, registry) from the complete executor policy and the exact
operator-authenticated manifest key registry, including key validity bounds; never substitute the
unauthenticated discovery response from /v1/keys. Subsequent opens read the identity and its
policy/trust fingerprints from the database and fail if any accepted authority differs. Do not use
allow_unanchored=True outside a
development test: it protects ordinary restart only and cannot detect restoration of older bytes.
The generic file executor anchor has the same local-filesystem requirements as the warden file anchor and must not be included in a replay-database snapshot. A managed deployment should replace it with a remote linearizable CAS binding when the executor state and anchor cannot be placed in independent storage domains. Back up the replay database while the executor is fenced, retain the anchor independently, and require a successful anchored reopen before routing effects after a restore. Missing, divergent, forked, or stale replay state is a fail-closed recovery incident, not permission to initialize a new store.
ReceiptVerifier.verify_and_claim advances the durable receipt/nonces/lease-watermark history and
the external claim anchor before the protected effect may begin. A post-COMMIT lost reply can burn
the claim even though the caller received an error; recovery confirms the head, and retry raises
ReplayError with no effect. The application must still bind that claim to its own effect
transaction or make the effect idempotent; LETS cannot atomically commit an arbitrary external side
effect. Monitor
executor anchor latency/errors, replay-store integrity and capacity, clock uncertainty, rejected
duplicates, and the age of the oldest retained receipt. Fence effects immediately if the anchor
becomes unavailable or the store reports an authority fault.
The core signed audit chain is intentionally immutable and is not deleted after external export.
max_database_bytes is a logical main-database ceiling, not a physical combined-file hard cap or
an automatic retention target. LETS applies PRAGMA max_page_count on every connection. The main
database, live WAL, and transient WAL-index remain separate physical files, and a pinned reader can
keep committed WAL frames live. Every write therefore requires free filesystem space for all
remaining main-file growth, one transaction that could touch every configured database page, any
additional WAL-index growth, and the emergency floor. This also leaves enough room for a checkpoint
to grow the main file while the WAL remains live. A subsequent write fails closed until checkpoint
progress restores that complete reserve.
For SQLite page size P and N = floor(max_database_bytes / P), LETS reports the conservative WAL
reserve 32 + N * (P + 24). The standard SQLite WAL-index uses one 32,768-byte block for the first
4,062 frames and another block per 4,096 frames. The deployment validator consequently requires:
LETS_STATE_CAPACITY_BYTES >=
floor(max_database_bytes / P) * P
+ WAL-index bytes
+ min_free_disk_bytes
+ worst_case_transaction_wal_bytes
At runtime the corresponding free-space admission is
min_free_disk_bytes + remaining_main_growth_bytes + worst_case_transaction_wal_bytes + additional_shared_memory_bytes. Logical reserve_pages is enforced separately inside the main
database page ceiling; it is not a substitute for physical checkpoint headroom.
This declaration must correspond to a real dedicated filesystem or enforced quota; the validator
can compare sizes but cannot prove storage-controller policy or the absence of unrelated writers.
Record findmnt/quota evidence at commissioning. See SQLite's official
WAL-index format for the physical index layout. Before
commissioning, measure worst-case logical database growth with the real policy and mutation mix,
calculate the time remaining to the configured cap, and make that horizon longer than the approved
node lifecycle. Alert on storage_capacity.logical_live_bytes, main_database_bytes, wal_bytes,
shared_memory_bytes, worst_case_transaction_wal_bytes,
worst_case_shared_memory_bytes, additional_shared_memory_bytes,
remaining_main_growth_bytes, required_filesystem_free_bytes, actual filesystem free bytes,
audit_exporter.pending/stalled_for_s, peer pending/prepared counts,
peer_dispatcher.durable_retry attempt/delay/kind/target metadata, and readiness. The status uses a
bounded exception-class token and never returns raw transport error text; each new attempt also
replaces a legacy pending row's raw error with that class-only form. Recalculate the horizon after
every policy or traffic change.
The provider audit archive has a separate append-only growth curve and does not share the core
state cap. Alert on archive main/WAL/SHM bytes, filesystem/quota free bytes, exported head sequence,
core-to-archive lag, audit_exporter.pending, archive_reconciled, publish_blocked, and
last_error. Recalculate LETS_AUDIT_EXPECTED_DAILY_BYTES after every workload/payload change and
extend or retire the archive before its commissioned LETS_AUDIT_FORECAST_DAYS horizon. A full
sink makes readiness false; deleting archive rows or resetting its head is not recovery.
Production WAL mode is admitted only on SQLite 3.53 or newer, the 3.51.3 patch line, or the
maintained 3.50.7/3.44.6 backports. Earlier affected builds and the withdrawn 3.52.x line are
refused. The validator checks the loaded library inside the exact image digest, the server rechecks
it before opening production state, and /v1/info records sqlite_version for evidence.
LETS v1 has no in-place core-audit deletion or live configuration-epoch rollover. Do not delete SQLite rows, WAL/SHM files, audit records, receipts, or idempotency state by hand. A deployment whose measured growth cannot fit its planned lifecycle is not ready for that workload; drain and retire it under an explicitly reconciled new envelope/epoch before the capacity reserve is reached.
On a capacity or SQLITE_FULL incident, remove the node from routing and fence mutation traffic.
Extend the state filesystem or remove only unrelated host data; never manipulate LETS database,
anchor, audit, or outbox files. An actual SQLite-full error is sticky in the running process, so
after restoring headroom stop the container, prove the node lock is free, run
lets info --production from maintenance-compose.yaml, drain the node, and create a verified
recovery bundle before deciding whether to reactivate it. Repeated crash-loop restarts are not a
recovery procedure.
TLS material and the generic provider's JWT verification-key set are loaded when the process starts; they are not live-reloaded. Rotate them with an overlap window: provision CA bundles and identity-key JSON containing both old and new trust, recreate one drained warden at a time, verify mTLS/JWT canaries, rotate every peer/client, and remove old trust only after the maximum token lifetime, clock skew, connection lifetime, and certificate rollout window have elapsed. Compose file-backed secrets require container recreation to pick up new material.
The Python TLS boundary does not perform online OCSP fetching. Use short-lived server and client certificates, restrict CA scope to LETS, and make emergency CA/certificate distrust a recreate-all incident with network fencing until every affected process has restarted. If the platform requires real-time workload-certificate revocation, terminate mTLS in an approved proxy or service-mesh identity layer and preserve equivalent authenticated peer identity end to end.
The warden signing identity is bound to the signed manifest, database, and authority checkpoint;
replacing it without an admitted manifest/epoch transition is rejected. Never edit the staged
config.json in place. Use the release runbook's exclusive staging and recreate procedure for an
approved non-authority configuration change. Follow the new-manifest/new-epoch drain procedure for
identity changes, and retain old verification keys for every still-valid receipt and queued peer
record.
Follow the release runbook. Never move a mutable tag into production. LETS v1 does not support a live mixed-version cluster. Drain every warden and remove external mutation traffic while keeping the internal peer/audit paths available; settle those queues, then fence the cluster, stop every old process, and create a separately verified recovery bundle and authority checkpoint for every node before starting any replacement. Stage the same verified digest on all nodes, start the entire cluster in its durable drained state, prove uniform version/config, invariants, capacity, audit state, and peer reachability, then activate and canary the uniform cluster before restoring traffic. Do not restore a database behind its monotonic anchor. The signed v1.0.1 through v1.0.9 tags were never promoted and are not rollback artifacts; v1.0.0 lacks later production defenses. The v1.0.10 GitHub release published package and local acceptance assets but did not promote an OCI image. Therefore v1.0.11 has no approved earlier binary rollback target. Recover forward with a patch release unless the release notes explicitly name a compatible published digest; the v1.0.12 notes name the promoted v1.0.11 digest. A restore is admitted only while fenced and only when the live anchor proves the bundle cannot resurrect spent authority.
The bundled generic-production provider makes the deployment runnable without coupling LETS to
one cloud or HSM vendor. Operators must still supply the signer bridge, identity trust keys, signed
cluster manifest, and PKI material described above; those are security-domain inputs, not missing
runtime components. LETS fails closed if the provider cannot sign, the external audit archive is
unavailable, the monotonic anchor cannot advance, identity keys are invalid, or TLS/mTLS material is
absent. A cloud KMS, PKCS#11, SPIFFE, or OIDC integration can replace this provider through the same
entry-point contract without changing the core runtime or Compose hardening.