Security & trust What runs where, what Cumulus can touch, and how a deploy is protected
Cumulus is built so the sensitive things stay in your control: your code, your data, and your AWS credentials. Your applications run in your own AWS account, not ours. This page explains what lives where, exactly what access you grant us, and how each deploy is protected from build to run.
Your cloud, your data
Everything you deploy runs in your AWS account: the EC2 instances, the Lambda functions, the S3 buckets, the databases. You pay AWS directly for what you use, and the resources are yours. Cumulus orchestrates them; it does not host them.
Some things never live in Cumulus's infrastructure:
- Your application code. Your builds run in your own AWS account on ephemeral infrastructure, and the source is staged in your own S3 bucket. Cumulus never stores your source: the control plane checks out your repository only to hand it to that build, holding it in temporary storage for the moment it takes and discarding it immediately.
- Your production data. It lives in your databases, in your account.
- Your environment variable values. They are written to AWS Systems Manager (SSM) in your account, not ours.
- Your AWS resources. Created in your account, tagged
ManagedBy: cumulus, visible and auditable by you at any time.
What Cumulus does store is the control-plane metadata needed to orchestrate deploys: your projects and apps, deployment history, and your AWS connection (the role reference and its identifier, held encrypted, see below).
What Cumulus can access in your account
You connect your AWS account once, by deploying a CloudFormation template that creates a single cross-account role. Cumulus assumes that role to act on your behalf. The access it grants is deliberately narrow:
- Scoped to Cumulus-managed resources. The role's policy targets
cumulus-*resources; it is not account-wide admin. - Protected against confused-deputy attacks. Each connection gets a unique, random external ID that is required on every role assumption, so no one else can assume your role even if they learn its name.
- Short-lived sessions. Each assumption lasts 15 minutes, not indefinitely.
- Stored encrypted. The role reference and external ID are held in encrypted database columns.
cumulus aws connect # walks you through creating the scoped role
Secrets
Secrets (database URLs, API keys) are treated as runtime configuration, never as part of what gets built:
- Never in build artifacts. A secret is injected only at run time, never compiled or packaged into the binary or zip.
- Encrypted at rest. Values are stored as SSM SecureStrings encrypted with your own KMS key (CMK).
- Least-privilege on the box. On EC2, the agent writes them to a
.envfile at mode0600(owner-only), placed atomically. On Lambda, they are set as function environment encrypted with KMS. - Masked in the dashboard. Env var values are hidden by default (shown as
••••); an authorized member can reveal a value when they need it.
# stored encrypted with your CMK; takes effect on the next deploy
cumulus env set my-app DATABASE_URL "postgres://..." --secret
cumulus env pull is for), so treat env access as secret access.
Tenant isolation
Cumulus is a multi-tenant platform, and every request is scoped to your organization. Another customer cannot see your projects, deployments, logs, or even your environment variable names. A request for a resource that is not yours returns the same "not found" response as one that does not exist, so nothing about other tenants is revealed, not even that a resource exists.
Your AWS connection details (the role reference and its external ID) are stored in encrypted columns, isolated per organization.
Build integrity
When you push, Cumulus does not run your code directly. It builds it into a finished package (a compiled binary or a zip), stores that package in your AWS account (S3), and later hands it to a server or Lambda to run. Build integrity makes sure the package that runs is exactly the one that was built, and that you can prove where it came from. None of it needs configuration; it is on for every deploy.
A fingerprint on every package
At build time Cumulus takes a cryptographic fingerprint of the exact bytes (a SHA-256 hash). Change a single byte and the fingerprint changes completely, and it cannot be forged from the content. That fingerprint is recorded with the deployment, and before anything runs the package Cumulus re-checks the fingerprint of what is actually in storage against the one from the build. S3 stores its own copy at upload time, so the check is fast and needs no re-download.
A signed record of origin
A fingerprint proves a package was not changed. It does not prove where it came from. So Cumulus also writes a short signed statement for each build, its provenance: this exact package, with this fingerprint, was built from this git commit, by this build environment, at this time. The statement is digitally signed by the control plane, so anyone can later verify it really came from Cumulus and was not edited afterward.
You can see it on a deployment's page in the dashboard, or from the CLI:
cumulus app provenance <deployment-id>
A Verified result ties the running artifact back to the exact commit and builder that produced it.
An alert if something is tampered with
A failed fingerprint check is not a routine build error; it means corruption or a swap. Beyond stopping the deploy, Cumulus emails your organization's owners and admins a security alert so a person knows an artifact was touched.
What it protects against
- Corruption in transit. A package mangled between build and run is never deployed.
- A swapped or malicious artifact. Replace the stored package and its fingerprint no longer matches, so the deploy is refused.
- A forged history. You cannot misrepresent which commit or builder produced a running artifact, because the provenance is signed and independently verifiable.
- Silent tampering. Even when a bad artifact is blocked, a human is notified.
Encryption
Cumulus uses customer-managed KMS keys (CMKs) rather than the default AWS-managed keys, so every use of a key is visible in your CloudTrail and access can be revoked independently.
- At rest. Artifact buckets, SSM SecureStrings, RDS databases, and EBS volumes are encrypted with a CMK.
- In transit. Traffic is served over TLS.
- Deploy commands are authenticated. The agent on each server proves its identity to the control plane using the EC2 instance identity document (no shared passwords), and every deploy instruction is cryptographically signed and verified before the agent will act on it.
No lock-in
If Cumulus went away tomorrow, your app would keep serving traffic. A deployed EC2 app is a plain systemd service on an instance you own, with no runtime dependency on our control plane. Lambda functions are ordinary functions in your account.
Because everything lives in your AWS account, you can always drop down to raw AWS and manage the resources yourself. There is no proprietary runtime to escape and no data to extract from us, it is already yours.
Platform hardening
Beyond the guarantees above, the control-plane API and dashboard are hardened against common web attacks:
- OAuth sign-in is protected against cross-site request forgery, with secure, same-site session cookies.
- The API uses a strict cross-origin allowlist and sends defensive HTTP headers (HSTS, no-sniff, framing and referrer policies).
- Authentication endpoints are rate-limited to blunt credential stuffing and account enumeration.
- GitHub webhooks are signature-verified and replay-protected, so a captured delivery cannot be replayed to trigger a deploy.
- User-supplied names are validated against a strict allowlist before they are used in any path or identifier.
- Your deployment history and audit trail are append-only at the database layer, not just in application code, so past events cannot be quietly rewritten.