Appearance
Architecture
Keel is a single Go binary. It uses OpenTofu for infrastructure changes and the AWS SDK for runtime operations. An optional account-level control plane provides shared reads and narrowly brokered operations without replacing either direct path.
keel.yml
|
+-------------+-------------+
| |
Infrastructure Runtime operations
HCL → OpenTofu AWS SDK (direct)
| |
S3 state + lock optional Keel API in your account
|
served reads + capability brokerTwo planes
Infrastructure provisioning (keel up, keel infra plan/apply) runs through OpenTofu. Keel generates HCL from your config and invokes tofu with an S3 backend and DynamoDB locking. State is keyed by environment (<app>/<env>/terraform.tfstate), and credentials are passed through environment variables instead of written to disk.
Runtime operations (keel deploy, keel logs, keel exec, keel scale) use the AWS SDK directly. For example, a deploy builds an image, registers a task definition, and updates a service without planning the VPC again.
The optional control plane is a Lambda Function URL protected by AWS_IAM. IAM sessions prefer it for cacheable reads and fall back to the direct SDK path when it is absent or unhealthy. Federated operators can invoke only this Lambda. Served endpoints compute read-only answers; brokered endpoints mint short-lived credentials for one operation without turning the Lambda into a log or terminal data pipe. See The Keel Control Plane.
The app registry
A DynamoDB table tracks every Keel app and environment in the account. Team members can use keel apps to discover deployments regardless of which machine ran keel up.
Internal layout
For contributors, the codebase splits along the same lines:
| Package | Responsibility |
|---|---|
internal/cli | Cobra commands — thin glue that delegates to domain packages |
internal/config | keel.yml parsing, validation, defaults, environment merging |
internal/auth | Access levels, credential caching, IAM bootstrap, team management |
internal/apiserver, internal/apiclient | Control-plane routes and client transport |
internal/capability, internal/grants | Brokered credential ceilings and the operator authorization grid |
internal/aws | AWS SDK v2 wrappers (ECS, ECR, CloudWatch, SSM, STS) |
internal/infra | OpenTofu orchestration and HCL generation |
internal/deploy | Docker/CodeBuild builds, ECS deploys, release commands, rollback |
internal/project | Workspace detection and the DynamoDB app registry |
internal/tui | The Bubbletea dashboard |
internal/util | Logging, error types, terminal detection |
Access control
Every privileged command declares the access level it requires (viewer, developer, or admin) and checks it before doing anything on the IAM path. A federated operator instead needs the capability and scope named by the action. The dashboard presents both models: IAM actions by level and operator actions by grant. See The Auth Model.