- Rust 72.5%
- Vue 8.5%
- Shell 7.4%
- JavaScript 6.1%
- Python 3%
- Other 2.3%
Validate the selected commit through ConfigurationFile::from_str after app ownership checks and before billing, component loading, or build admission. Wait for config validation before opening the deployment stream so invalid HCL, including TLS without a domain, returns HTTP 400 and releases its slot. Report only server failures to Sentry and relay client errors through the gateway to the SSH client. Preserve HCL error source diagnostics, escaping terminal controls and omitting raw source excerpts from syntax errors. Cover the HTTP status, reservation cleanup, valid-config streaming, fallback behavior, and error chain diagnostics with parser and PostgreSQL deployment regression tests. |
||
|---|---|---|
| .cargo | ||
| containerfiles | ||
| dist/cli | ||
| docs | ||
| frontend | ||
| LICENSES | ||
| scripts | ||
| src | ||
| systemd | ||
| terraform/modules/aws/nitro-enclave | ||
| tests | ||
| utils | ||
| .dockerignore | ||
| .editorconfig | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLA.md | ||
| config.json | ||
| config.json.example | ||
| env.example | ||
| LICENSE | ||
| Makefile | ||
| prices.json.example | ||
| README.md | ||
This repository may be mirrored on other Git forges. The canonical repository is hosted on git.caution.co. Please open issues and PRs there; a forge account is required.
Hosted version: A hosted deployment is available at dashboard.caution.co.
Caution Platform
This software is a cloud hosting management platform that builds and deploys verifiable secure enclaves using the AWS Nitro System, based on EnclaveOS.
Verifiable Enclaves
An enclave is verifiable when you can independently confirm that the code running inside it matches the source code you expect. This is achieved through:
- Reproducible builds — Reproducible builds force software to be bit-for-bit identical when built from the same source code, and eliminate certain categories of supply chain attacks. It allows for integrity verification, without which software is opaque and difficult to verify.
- Cryptographic attestation — The enclave hardware generates a signed attestation document containing measurements (PCR hashes) of the running code.
- Independent verification — You can compare your locally-built measurements against the attestation from a running enclave to prove they match.
Getting Started
Prerequisites
- Docker with BuildKit/buildx and containerd enabled; the supplied units use
/usr/bin/dockerand require daemon access withoutsudo - GNU Make
- Bash
- Git, wget, coreutils, and OpenSSL; Python 3 for the setup regression tests
- Linux/x86_64 with a working systemd user session for the platform services; the CLI also supports macOS on Apple silicon
1. Bootstrap AWS infrastructure
AWS account infrastructure is managed in the
Caution infrastructure repository,
not this repository. For a new dedicated development account, follow its
AWS Platform bootstrap runbook.
That workflow provisions the host, storage, IAM, DNS and builder resources and
installs a pinned Platform checkout using the make setup / make up workflow below.
Existing deployments should keep their current configuration and infrastructure
state. Removing the former infra-bootstrap/ source does not migrate or
destroy AWS resources. Preserve any old local state, plans and credential exports
securely; do not apply the new-account bootstrap to an existing deployment or run
Terraform against the retired directory. Adopting existing resources requires a
separate reviewed state migration. The application/BYOC provisioning code under
src/ remains part of Platform.
2. Run the platform
Prepare configuration and install the supplied systemd user units:
make setup
This creates missing .env, prices.json, and config.json files under
$HOME/.config/caution from the examples, preserving existing files and their
permissions. It installs/refreshes the units under
${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user and reloads the user manager;
it does not enable or start services, build images, or provision AWS resources.
Before continuing, review all three configuration files. In .env, set the AWS
credentials, bucket names, and builder settings from bootstrapping, and review
database credentials, service secrets, and public URLs for your environment.
Uncomment and set CSRF_SECRET, and set INTERNAL_SERVICE_SECRET to a separate
nonempty random value (generate each with openssl rand -hex 32). Both are
required even in development; the internal secret must match across services.
Keep CAUTION_DATA_DIR=/var/cache/caution for the supplied container units so
the API and gateway share repositories and other persistent data.
Migrations use POSTGRES_PASSWORD, POSTGRES_USER, and POSTGRES_DB by default;
explicit PGPASSWORD and MIGRATION_DB_* overrides remain supported.
DATABASE_URL must use matching database credentials.
The copied examples are development defaults, not production-ready configuration.
Run setup and startup as the same user, not with sudo. On remote hosts, use a
login session with a working systemd user bus. To keep services running after
logout, an administrator may need to run loginctl enable-linger <user>.
If Docker access was just granted through group membership, the systemd user
manager also needs a fresh login with that membership; opening another shell
alone does not update an already-running user manager.
Install the CLI with the local host toolchain. This is the default on every supported platform and supports local PC/SC for Locksmith shard submission:
make install-cli
To explicitly build and install the reproducible StageX CLI on Linux/x86_64:
make install-cli-stagex
See src/cli/README.md for additional installation options, native dependencies, signature verification, and the current StageX PC/SC limitation.
Start the platform services after reviewing configuration:
make up
make up first runs make audit: npm audit checks the frontend lockfile
(including development dependencies), and cargo audit checks the workspace and
tap-framer lockfiles. High or critical vulnerabilities block deployment; Rust
vulnerabilities without a severity score also block it. Audit errors, including
unavailable advisory services or a five-minute scan timeout, stop deployment
before service builds, migrations, or restarts. The audits run in Docker, require
network access on every invocation, and do not modify dependencies.
After the audits pass, make up builds the API, gateway (including frontend
assets), email, and metering images, prepares enclave components, starts
PostgreSQL, runs migrations, and restarts the services. No separate
make build-all is needed; the first run requires network access for image pulls
and build dependencies.
Run the local setup and migration-script regression tests with make test-setup;
these also run as part of make test and use temporary directories and stubbed
Docker, systemd and database commands.
The opt-in make test-e2e-builder requires the four BUILDER_* values listed in
env.example exported into the test environment, alongside the AWS credentials
and bucket configuration. These must match the configuration used by make up-test.
Provision them through the infrastructure repository first; this test no longer
reads local bootstrap state and still creates real AWS resources.
For Key Services setup, quorum creation, and secret recovery, see the secrets operator runbook.
The experimental development-server admin explorer is documented in docs/admin.md.
Blue-green app deployments (feature flag, lifecycle, recovery, rollout) are documented in docs/app-deployments.md.
Frontend development
For the normal frontend development server:
cd frontend
npm ci --allow-git=root
npm run dev
allow-git=root permits only the commit-pinned git dependencies declared by
this package; npm 12 blocks git dependencies by default. Project sources are
fetched from git.caution.co/public. Keyfork and the private _cargo-index intentionally
remain on git.distrust.co.
Open http://localhost:3000. This remains authentication-gated and proxies API,
authentication, health, and build-input requests to VITE_PROXY_TARGET (default
http://localhost:8000), so the platform stack must be running for a real login.
To inspect the populated dashboard without creating an account:
npm run dev:dashboard
This command binds to 127.0.0.1 and enables a Vite-server-only, read-only fixture.
The page displays a preview banner, state-changing requests return 405, and unknown
API or authentication requests return 404 instead of reaching a backend. It does
not add a frontend authentication bypass, cookie, test account, gateway route, or
production behavior. A production build always disables preview mode.
The provider-neutral app detail fixture covers complete and sparse deployment states;
provider-specific identifiers are intentionally not shown.
The applications fixture covers active, failed, stopped, terminating, and terminated
states. Its sortable columns and Hide terminated control persist browser-local choices
under caution-apps-list-preferences; search text is intentionally not persisted.
The fixture is UI evidence only. For an authenticated smoke test, use a Linux/x86_64
host, run npm run build, rebuild the gateway image (which bakes in the frontend),
start the real stack with make up, generate an access code with
bash utils/generate-beta-codes.sh 1, and register at http://localhost:8000.
3. Deploy an app
-
Register using Passkey (via terminal or web browser):
caution register -
Add an SSH key (can be done in browser as well):
caution ssh-keys add --title <name_of_key> --key <pub_key_string> -
Initialize a project within a Dockerized repo:
caution initThis writes a
caution.hcltemplate when one does not already exist and configures thecautiongit remote. Successful app creation also records the current app ID in.caution/deployment.jsonand prints the managed DNS target when one is available, including for BYOC initialization. Caution remote builds run Docker from the repository root, usingbuild.containerfilewhen configured, then a repo-rootContainerfile, then a repo-rootDockerfile. For an active app, rerunningcaution initverifies the app ID in deployment state or thecautionSSH remote and restores both local links. Ambiguous or unverifiable identity fails closed rather than creating a successor app. Put setup, compilation, asset builds, and runtime packaging in that Containerfile or Dockerfile so the build inputs are explicit and reproducible. Application images may use the conventional relative merged-/usr links/bin -> usr/binand/lib -> usr/lib; other/binor/libsymlink layouts are rejected during EIF assembly.The hello-world-enclave repo is a good test app to deploy.
-
Build and deploy:
git push caution mainLong-running builds use protocol-level SSH keepalives so quiet deployment phases do not require client-side keepalive configuration.
Caution reports the selected capacity, AWS account, and region before enclave launch. A running or stopped app cannot be redeployed in place. Run
caution apps destroy <app-id>, wait for teardown, then rungit push caution HEAD:mainfrom the existing checkout. Teardown causes downtime and temporarily withdraws managed DNS, but retains the app ID, repository, managed hostname, and any BYOC linkage. Do not runapps createor plaininit; BYOC apps must also not runteardown --byocas part of this redeploy sequence.
Enclave-terminated HTTPS (TLS mode, implemented by Caddy)
To terminate standard HTTPS inside the enclave without changing clients, select TLS mode on the HTTP ingress. The enclave currently implements this mode with Caddy:
network {
ingress {
cidr_ipv4 = "0.0.0.0/0"
port = 8080
ip_protocol = "tcp"
}
egress { cidr_ipv4 = "0.0.0.0/0" }
http {
domain = "app.example.com"
port = 8080
e2e_encryption { mode = "tls" }
}
}
Point the subdomain's CNAME record at the app's DNS target shown by
the CLI or dashboard. Zone-apex domains are not supported. The HTTP port must have
an ingress rule, and outbound egress is required for Let's Encrypt. Disable any
CDN or proxy TLS termination. Caddy publishes the verified leaf certificate
SHA-256 fingerprint in authenticated Nitro user_data. For a source-backed
mode = "tls" deployment, caution verify validates the fresh Nitro evidence
and expected PCRs, compares that fingerprint with the live WebPKI-validated
leaf, and saves the verified PCRs and TLS binding to
.caution/trusted_hashes.json. Certificate changes can take up to 60 seconds
to appear in new attestations.
Run the opt-in live binding test against any production-mode Caddy deployment:
make CADDY_E2E_URL=https://app.example.com test-live-caddy-nitro
This checks HTTPS, redirects, health, Nitro authenticity, and the signed
certificate binding. It does not establish workload identity; use
caution verify with independently trusted source or PCRs for that.
For STEVE-protected HTTP deployments, plaintext application routing is denied by default. Legacy fallback requires an explicit opt-in:
e2e_encryption {
enabled = true
key_exchange = "xwing-draft10"
allow_plaintext_fallback = true
}
The platform health and attestation endpoints remain available outside the application route. In fail-closed mode the application HTTP port is not exposed through a host or enclave VSOCK proxy; STEVE reaches it over enclave-local TCP.
Browser E2E verifier
Open /verify-e2ee, enter the application's bare HTTPS origin, and select the
exact key-exchange suite used by its deployment (X25519 or
XWING-DRAFT10). Choose the initial browser trust policy: None permits only
acknowledged non-sensitive testing, Pinned requires an approved profile before
connecting, and TOFU requires reviewing the first authenticated PCRs before
the page installs them as a pinned SDK policy. The page then establishes an
attestation-bound STEVE session and provides a small GET/POST tester. Request
paths must begin with one /;
attestation and STEVE protocol paths are blocked. POST bodies may be JSON or text
up to 64 KiB. The tester rejects redirects and does not retry requests.
The target card is a compact control center for session state, the active worker-enforced PCR policy, Nitro evidence, and session operations. The request tester uses the pinned SDK's explicit protected-send API; it does not depend on ordinary page fetch interception. Each result includes an initially collapsed SDK-recorded exchange disclosure with the protocol, suite, PCR trust result, session and sequence, outer status, response headers, and record-size breakdown. It never exposes raw records or cryptographic secrets. The result labels browser-observed round-trip time explicitly, and preserves open response disclosures across successive requests. Application body sizes and elapsed time are browser observations; the outer status is transport metadata.
The deployment must allow the verifier's exact browser origin on the STEVE
protocol endpoints. The landing page shows the exact configuration before it
attempts a connection. For the hosted verifier, configure caution.hcl with:
e2e_encryption {
mode = "steve"
key_exchange = "xwing-draft10"
cors_origins = ["https://dashboard.caution.co"]
}
Use key_exchange = "x25519" when selecting X25519. A self-hosted verifier
needs its own exact origin, including any non-default port; do not include
/verify-e2ee in the origin. The STEVE target must use a different origin.
Wildcard origins are rejected.
The page can pin multiple complete PCR profiles for the exact target and suite.
Each profile requires nonzero PCR0, PCR1, and PCR2 and may also pin authenticated
nonzero PCR3 through PCR255. Authenticated zero-valued optional PCRs are grouped
behind a compact count disclosure and remain non-pinnable. Profiles are
alternatives: the SDK accepts one whole match and never combines values across
profiles. Profiles may
be imported, entered manually, or explicitly accepted after reviewing a current
authenticated session. This reviewed first use is browser continuity, not
independent deployment identity; observed PCRs are never enrolled automatically.
The SDK stores and enforces the complete pinned policy inside the worker on every
session and rotation. Platform local storage mirrors the profiles only to retain
their source and timestamp for the UI; the worker policy remains authoritative.
When restored worker profiles override a None or TOFU URL choice, the page
reflects the effective Pinned mode in the URL and target chooser. Removing a
profile requires inline confirmation before the worker policy changes.
A pinned mismatch blocks requests and opens the approved-profile controls; add
an independently approved complete profile rather than reconnecting. Any
zero-valued required PCR also blocks requests. Without a pinned profile, the page
requires explicitly enabling non-sensitive test-data mode. A successful session
proves fresh Nitro-backed session and suite binding, not that this is the expected
workload.
The browser does not reproduce source, and its initial page, worker, and WASM
delivery are not an independent trust root. Use caution verify for source
reproduction and expected-PCR enforcement.
4. Verify a deployed app
Use the public /verify page to authenticate fresh nonce-bound Nitro evidence
and inspect its PCR0, PCR1, and PCR2 values without an account. Browser targets
must use an HTTPS domain, and their /attestation endpoint must permit
cross-origin POST requests. HTTP and raw-IP endpoints require the CLI.
Every first-party frontend route follows the operating-system light or dark
preference by default and provides a header control for an explicit override. A
choice is stored as caution-theme in browser localStorage, applies before first
paint, and synchronizes across routes, reloads, and other open tabs. Without a
valid stored choice, the theme follows operating-system changes and falls back to
light when the system preference is unavailable. The preference is browser-local;
it is not stored in a cookie or synchronized with an account.
The browser does not authenticate the sibling response manifest, reproduce the application source, establish a STEVE encrypted session, or automatically identify the expected deployment. Compare PCRs only with values reviewed through an independent trusted source.
The verification summary marks authenticated Nitro evidence in green and keeps the expected deployment neutral until independently reviewed PCRs are supplied. It turns green when PCR0, PCR1, and PCR2 all match and red for a mismatch. Attestations with all-zero PCR0, PCR1, and PCR2 are identified as debug enclaves and cannot be used for workload identity. The page reports when fresh nonce-bound evidence was verified in the browser, supports explicit re-verification, and reports each PCR comparison separately.
Expected PCRs can be imported from an enclave.pcrs produced by
caution apps build --no-cache from a reviewed checkout, or from the
.caution/trusted_hashes.json written by a successful CLI verification. Files
are read only in the browser and are not uploaded; both formats remain unsigned
and editable, so importing one does not authenticate its source. Manual entry is
available under the advanced control.
After an exact match, the page saves PCR0, PCR1, and PCR2 for that exact attestation endpoint and compares them with fresh evidence on later visits. The page labels saved values as browser continuity and provides explicit replace and forget controls. This browser storage is not an independent trust root: same-origin code or the local user can change or clear it.
The default Bootproof pin includes the required attestation CORS policy. An
operator override through BOOTPROOF_COMMIT takes precedence. Pin changes apply
only to newly built EIFs; existing applications must be rebuilt and redeployed,
and their expected PCRs reviewed again.
For source reproduction or expected-PCR enforcement, use the CLI:
Option A: Reproduce and verify (recommended)
Fetches the attestation from the endpoint, rebuilds the enclave locally, and verifies the PCR hashes match. The attestation endpoint is available at https://<app-url>/attestation.
caution verify --attestation-url <attestation-url>
Local source is the default. If the deployment manifest has no app commit, the
CLI uses the current checkout at HEAD.
Source-archive preflights use five-second HEAD requests. New Platform framework
and EnclaveOS archives are checked on git.caution.co first, then on the
configured GitHub mirror. Historical Codeberg archive URLs try Codeberg, the
corresponding new-forge archive, then GitHub. Each candidate is retried once for
transient failures; missing archives advance immediately. The measured manifest
and approved commit remain unchanged during reproduction. Unconfigured
repositories are not remapped. If every candidate is unavailable, verification
stops before the reproduction build. Mirrors improve availability but do not
independently prove that an archive matches its claimed Git commit.
New builds record the new canonical source locations and migration-only STEVE and Locksmith revisions; rebuild and verify their PCRs before approving them. Existing manifests and trusted PCR policies are not migrated automatically. The BYOC provisioner image remains in the Codeberg registry until its separate registry migration.
Option B: Verify against known PCR hashes
PCRs (Platform Configuration Registers) are cryptographic measurements of the enclave's code and configuration. If you already have the expected PCR hashes, you can verify against a file:
# Create a file with expected PCR hashes
cat pcrs.txt
PCR0: 3c07ec536432532f86b8c735b740f0d67a8b115e4a5e20cc8ecbb4e6a8335fe016bf42693b18e8560e299636afa8dc84
PCR1: 3c07ec536432532f86b8c735b740f0d67a8b115e4a5e20cc8ecbb4e6a8335fe016bf42693b18e8560e299636afa8dc84
PCR2: 21b9efbc184807662e966d34f390821309eeac6802309798826296bf3e8bec7c10edb30948c90ba67310f7b964fc500a
caution verify --pcrs pcrs.txt
This is explicitly PCR-only: it does not verify TLS certificate binding, and
the persisted trusted state contains no tls object.
Credit suspension recovery
Resuming a fully managed app starts the same instance and reattaches the Elastic IP tagged to that app. Redeployment is not required.
Reference
Limitations
- AWS Nitro Enclaves only
- Requires x86_64 architecture for enclave builds
- Docker BuildKit required for reproducible builds
- Attestation verification requires network access to the enclave endpoint
Coming soon
- Other TEE platforms (TDX, SEV)
- Other major cloud platforms and baremetal
- Alternate OCI runtime support (Podman)
License
Dual-licensed under AGPL-3.0 and a commercial license. See LICENSE for details. Contact info@caution.co to obtain a commercial license.