The Caution verifiable compute hosting platform https://caution.co
  • Rust 72.5%
  • Vue 8.5%
  • Shell 7.4%
  • JavaScript 6.1%
  • Python 3%
  • Other 2.3%
Find a file
Anton Livaja 1fad412707
fix: reject invalid deployment config before streaming
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.
2026-10-20 18:52:57 -07:00
.cargo feat: gate make up on npm and cargo security audits 2026-10-20 12:19:01 -07:00
containerfiles Merge dependency security updates from main into audit branch 2026-10-20 16:31:01 -07:00
dist/cli cli: initial release 2026-05-29 13:54:58 -07:00
docs fix: reject invalid deployment config before streaming 2026-10-20 18:52:57 -07:00
frontend frontend: let an organization remove a member who would be orphaned 2026-10-20 19:12:50 -04:00
LICENSES Initial commit 2025-11-29 07:42:39 -08:00
scripts fix: make pricing configuration readable by metering 2026-10-19 14:45:12 -07:00
src fix: reject invalid deployment config before streaming 2026-10-20 18:52:57 -07:00
systemd feat: reuse digest-pinned enclave components from S3 2026-10-16 22:57:01 -07:00
terraform/modules/aws/nitro-enclave Merge commit '3485f577cc' into merge/blue-green-main 2026-10-17 09:37:53 -07:00
tests api, frontend: let a user without an organization create one 2026-10-20 19:12:48 -04:00
utils fix(bootstrap): make fresh platform setup repeatable 2026-10-15 13:12:35 -07:00
.dockerignore tests: parallelize e2e tests 2026-10-18 23:08:45 -07:00
.editorconfig Initial commit 2025-11-29 07:42:39 -08:00
.gitignore chore: retire legacy AWS bootstrap in favor of infrastructure 2026-10-20 15:12:24 -07:00
Cargo.lock fix: update S3 SDK to remove vulnerable lru dependency 2026-10-20 16:41:05 -07:00
Cargo.toml chore: retire legacy AWS bootstrap in favor of infrastructure 2026-10-20 15:12:24 -07:00
CLA.md add CLA 2025-12-08 12:49:17 -08:00
config.json add deploy billing gates, resource limits, and test mode guards 2026-03-31 11:28:13 -07:00
config.json.example add deploy billing gates, resource limits, and test mode guards 2026-03-31 11:28:13 -07:00
env.example chore: retire legacy AWS bootstrap in favor of infrastructure 2026-10-20 15:12:24 -07:00
LICENSE Initial commit 2025-11-29 07:42:39 -08:00
Makefile test: remove audit gate test 2026-10-20 16:52:21 -07:00
prices.json.example merge 337, #365, #340, #378 2026-07-17 16:13:44 -04:00
README.md Merge dependency security updates from main into audit branch 2026-10-20 16:31:01 -07:00

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:

  1. 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.
  2. Cryptographic attestation — The enclave hardware generates a signed attestation document containing measurements (PCR hashes) of the running code.
  3. 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/docker and require daemon access without sudo
  • 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

  1. Register using Passkey (via terminal or web browser):

    caution register
    
  2. Add an SSH key (can be done in browser as well):

    caution ssh-keys add --title <name_of_key> --key <pub_key_string>
    
  3. Initialize a project within a Dockerized repo:

    caution init
    

    This writes a caution.hcl template when one does not already exist and configures the caution git remote. Successful app creation also records the current app ID in .caution/deployment.json and prints the managed DNS target when one is available, including for BYOC initialization. Caution remote builds run Docker from the repository root, using build.containerfile when configured, then a repo-root Containerfile, then a repo-root Dockerfile. For an active app, rerunning caution init verifies the app ID in deployment state or the caution SSH 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/bin and /lib -> usr/lib; other /bin or /lib symlink layouts are rejected during EIF assembly.

    The hello-world-enclave repo is a good test app to deploy.

  4. Build and deploy:

    git push caution main
    

    Long-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 run git push caution HEAD:main from 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 run apps create or plain init; BYOC apps must also not run teardown --byoc as 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.