Anvil Cloud / Deployment
AWS preview adapter
AWS is the first Anvil Cloud deployment adapter. It is not the core application contract.
Cell code should stay provider-neutral. The AWS adapter consumes a manifest, creates a deployment plan, synthesizes CloudFormation, packages artifacts, and can provision preview resources when configured.
Current implementation
The @anvil-cloud/aws package currently includes:
- AWS HTTP event to
RuntimeRequesttranslation - Lambda runtime handler creation around the shared Anvil Runtime
- CloudFormation template synthesis from Cell manifests
- AWS resource name generation
- deploy artifact packaging for server bundle, client assets, manifest, and template
- optional AWS SDK preview provisioner
- DynamoDB-backed Cell table adapter
- S3-backed file adapter
- SQS-backed
ctx.jobs.enqueue - EventBridge scheduled job rules invoking the shared Lambda runtime
- Lambda-side workflow step execution through Step Functions task events
- Step Functions state machine template synthesis for declared workflow topology
- Lambda runtime enforcement for
capabilities.outboundFetch.allow - adapter-owned ECS/Fargate preview service resource synthesis
- Lambda environment values through
ctx.env - OIDC bearer token verification when auth environment is configured
- structured JSON logs through Lambda and CloudWatch
- remote inspect and logs through deployment metadata and CloudWatch Logs
Preview resource mapping
| Anvil concept | AWS backing |
|---|---|
| Cell runtime | Lambda |
| Query and mutation API | Lambda Function URL or API Gateway style HTTP event bridge |
| Custom endpoints | Runtime endpoint routing through the Lambda handler |
| Client bundle | S3 client asset bucket |
| Database | DynamoDB when capabilities.database is declared |
| Files | S3 when capabilities.files is declared |
| Queued jobs | SQS |
| Scheduled jobs | EventBridge rules |
| Events | A dedicated EventBridge bus when capabilities.events is declared; ctx.events.publish maps to PutEvents and reports structured EventBridge failure details |
| Workflows | Step Functions state machines invoking the shared Lambda runtime |
| Services | Deploy-gated; intended ECS/Fargate shape exists, but the Cell service runner is not implemented |
| Agent inference | AWS Bedrock through the @anvil-cloud/aws inference provider |
| Agent sandboxes | Lambda MicroVM sessions for sandbox-required agent work when a sandbox image is configured |
| Outbound fetch | Lambda runtime allow-list guard from capabilities.outboundFetch.allow |
| Environment | Lambda environment values for alpha |
| Logs | CloudWatch Logs |
| Deployment metadata | DynamoDB |
Deployment plans include an events change when capabilities.events is
declared, so anvil cloud deploy --preview --json reports the EventBridge resource
before provisioning.
Deployment plans also include an operations block with preview rollback
guidance, cleanup commands, and cost drivers. This is intentionally not a cost
calculator. It tells you which AWS surfaces can produce usage-based charges and
how to back out of a preview deploy while rollback commands are still future
work.
For workflow-bearing manifests, the plan reports a workflows change with
Step Functions topology, includes Step Functions state transitions in cost
drivers, and adds a workflow-preview-review approval gate. Preview deploy no
longer blocks workflows before provisioning.
For service-bearing manifests, the plan reports the intended services change
and ECS/Fargate cost drivers, then blocks provisioning with
AWS_PREVIEW_UNSUPPORTED_FEATURE. The current task scaffold does not execute
the declared Cell service handler, so Anvil does not create billable idle
resources.
For outbound fetch, Guard still fails closed for undeclared or dynamic targets,
and AWS preview writes the declared allow-list into the generated Lambda runtime.
Calls to hosts outside capabilities.outboundFetch.allow fail with
OUTBOUND_FETCH_NOT_ALLOWED.
Generated S3 buckets for client assets and Cell files use CloudFormation
generated names to avoid global bucket-name collisions, and the adapter reads
the physical names from stack outputs. They block public access and use
S3-managed server-side encryption. Declaring capabilities.files gives Cell
code object storage through ctx.files; it does not make uploaded files public.
Preview artifact uploads and runtime ctx.files.put writes request S3-managed
encryption explicitly, and browser client assets use Cache-Control: no-cache
so alpha redeploys do not depend on stale cached files.
Generated DynamoDB tables use pay-per-request billing and point-in-time recovery for both Cell data and deployment metadata.
Preview Lambdas use explicit alpha defaults: 256 MB memory and a 30 second timeout. Queued job Cells get an SQS queue with a 60 second visibility timeout so job messages are not immediately retried while the Lambda is still running. The generated queue redrives messages to a Cell-owned dead-letter queue after three failed receives, with 14 day retention for alpha debugging.
Agent sandbox target
The AWS preview adapter keeps normal Cell traffic on Lambda and can use Lambda MicroVMs as the backing for Agent Sandboxes.
The split is deliberate:
- Lambda handles query, mutation, endpoint, job, auth, approval, and capability broker traffic.
- MicroVM-backed sandboxes handle sessionful agent work such as repository operations, shell commands, package installs, browser automation, generated code execution, scanners, and long-running tool processes.
Cell authors should not see a MicroVM API. They declare:
runtime: {
sandbox: "required",
durability: "optional",
humanApproval: "required",
}
The AWS adapter maps that to agent-sandboxes plan entries when a mounted agent
requires a sandbox. Set ANVIL_AWS_AGENT_SANDBOX_IMAGE to enable AWS support
for those agents. durability: "required" remains a separate problem; MicroVM
suspend/resume helps session continuity, but it is not durable workflow
execution.
Deploy flow
anvil cloud deploy --preview --json:
- builds the Cell with preview target
- reads the generated manifest
- creates a provider-neutral deployment plan with AWS detail fields
- synthesizes CloudFormation
- packages deploy artifacts
- provisions resources only if the AWS provisioner is configured
- returns the deployment URL and next inspection commands when provisioning succeeds
Lambda bundle artifacts use content-addressed S3 keys, and deploy JSON includes
artifact SHA-256 digests. That gives CloudFormation a changed
ServerBundleKey parameter when server code changes instead of depending on a
same-key S3 overwrite to behave like a fresh deployment. Technically legal,
operationally rude.
Plan without provisioning
If no provisioner is configured, the adapter returns:
{
"ok": false,
"code": "AWS_PROVISIONER_NOT_CONFIGURED",
"message": "AWS preview provisioning needs a provisioner implementation or AWS client configuration.",
"hint": "The adapter produced a stable deployment plan, CloudFormation template, and deploy artifacts for this Cell."
}
That is expected in local review. It means a developer can inspect the deploy plan before mutating AWS.
Provisioning configuration
The AWS SDK preview provisioner requires:
ANVIL_AWS_ARTIFACT_BUCKET=<bucket-for-uploaded-artifacts>
Optional environment:
AWS_REGION=eu-west-2
ANVIL_AWS_STACK_PREFIX=anvil
Remote inspection and logs require:
ANVIL_AWS_DEPLOYMENT_METADATA_TABLE=<metadata-table-name>
Then:
anvil cloud deploy --preview --json
anvil cloud deploy --preview --name branch --json
anvil cloud inspect --app notes --env preview --json
anvil cloud inspect --app notes --env preview --name branch --json
anvil cloud logs --app notes --env preview --json
anvil cloud destroy --preview --app notes --name branch --yes --json
Local preview data can be isolated with database branches before deploying:
anvil cloud db branch branch --from main --ttl 3600 --json
anvil cloud dev --db-branch branch --json
anvil cloud db diff branch --against main --json
The AWS alpha adapter currently provisions DynamoDB for preview Cells, but it does not yet map local JSON branches to provider-native database branches. Use local branches for safe preview/test data states; provider-backed branch promotion remains an adapter roadmap item.
For the checked-in AWS-compatible smoke Cell, run the repeatable verifier from
the anvil-cloud workspace:
ANVIL_AWS_ARTIFACT_BUCKET=<artifact-bucket> \
AWS_REGION=eu-west-2 \
pnpm verify:aws-preview
The verifier builds the workspace, deploys examples/aws-preview with
--wait, checks /_anvil/health, calls the public status query, runs remote
inspect and logs, and destroys the stack in finally. Set
ANVIL_AWS_SMOKE_TOKEN to also exercise authenticated createNote and
listNotes calls against an OIDC-configured runtime. Set
ANVIL_AWS_SMOKE_KEEP_STACK=1 to leave the stack running for manual inspection.
The verifier normalizes Lambda Function URLs before calling runtime routes, so
provider-returned trailing slashes do not turn health checks into accidental
404s. If deploy returns a deployment URL but the health wait fails, the
verifier still attempts preview cleanup before exiting.
Remote inspect reads the latest deployment record from the metadata table and
returns the manifest, deployment id, update timestamp when present, runtime URL,
and resource ids including runtime, assets, logs, database, files, EventBridge,
SQS, workflow state machines, and deployment metadata when present. New
deployment records also include the artifact summary, including the Lambda
bundle key and SHA-256 digest.
Missing or malformed records return stable AWS_DEPLOYMENT_METADATA_NOT_FOUND
or AWS_DEPLOYMENT_METADATA_INVALID errors. DynamoDB or CloudWatch read
failures return AWS_REMOTE_READ_FAILED with the failed operation and provider
error cause.
Remote logs page through CloudWatch events until the requested limit is reached
or CloudWatch has no more pages.
Named previews use --name <preview> and write a separate normalized stack and
metadata key. Omit --name for the default preview.
Destroy empties stack-owned S3 buckets before deleting the stack and deletes the
matching metadata record when the table is configured, so inspect and logs stop
advertising a runtime after preview cleanup. Delete failures return
AWS_DESTROY_FAILED; cleanup polling timeouts return AWS_DESTROY_TIMEOUT.
AWS SDK cleanup failures return AWS_DESTROY_OPERATION_FAILED with the failed
operation and provider error cause.
Authenticated smoke path
The AWS verifier always checks that anonymous listNotes calls are rejected
with 401 AUTH_REQUIRED. To also exercise authenticated mutation/query calls,
configure the deployed runtime with OIDC verification and pass a token from that
issuer:
ANVIL_AUTH_ISSUER=https://issuer.example.test \
ANVIL_AUTH_AUDIENCE=anvil-preview \
ANVIL_AUTH_JWKS_URI=https://issuer.example.test/.well-known/jwks.json \
ANVIL_AWS_SMOKE_TOKEN=<bearer-token-from-that-issuer> \
ANVIL_AWS_ARTIFACT_BUCKET=<artifact-bucket> \
AWS_REGION=eu-west-2 \
pnpm verify:aws-preview
ANVIL_AUTH_JWKS_URI is optional when the issuer supports OIDC discovery at
/.well-known/openid-configuration. Use the claim mapping variables from
Authentication when your provider uses non-default claim
names for user id, email, or roles.
The smoke token should be short-lived and scoped to the preview app. The verifier does not create provider users or run a hosted login flow; it only proves that the deployed runtime can verify a bearer token and enforce handler auth policy.
Runtime Lambda flow
API Gateway or Lambda Function URL event
-> AWS adapter translates to RuntimeRequest
-> Anvil Runtime executes handler through AWS RuntimeHost
-> AWS adapter translates RuntimeResponse
-> HTTP response returns to caller
The adapter maps:
POST /_anvil/query/:nameto query runtime requestsPOST /_anvil/mutation/:nameto mutation runtime requests/api/*to declared endpoint runtime requestsGET /_anvil/healthto a runtime health response used byanvil cloud deploy --preview --wait
The AWS bridge handles OPTIONS preflight requests and adds CORS headers to
runtime responses so generated browser clients can call the preview runtime.
Endpoint request bodies preserve Lambda base64 decoding, and non-textual
endpoint responses are returned as base64 so binary/file payloads survive the
Function URL bridge. Runtime responses include x-anvil-request-id, which
matches the request id written to structured runtime logs for CloudWatch
correlation. Malformed JSON query or mutation bodies return a stable
400 INVALID_JSON response.
Current limits
- Service-bearing preview deploys are blocked until the adapter-owned Fargate runner executes the exact Cell service handler.
- Remote workflow run inspection is not yet mirrored through
anvil cloud workflows list/show --app. - AWS Bedrock inference and Lambda MicroVM sandbox lifecycle calls are available
through provider interfaces. Preview deploys with sandbox-required agents are
gated until
ANVIL_AWS_AGENT_SANDBOX_IMAGEis configured. Streamed tool transport, policy brokering, workspace snapshots, sandbox-aware Lens views, and remote sandbox inspect are still future work. - Production use needs wider operational validation beyond the preview verifier.
- CloudFormation stack failures return
AWS_STACK_FAILEDwith recent failing stack events. Stack polling timeouts returnAWS_STACK_TIMEOUTwith the last observed status. Missing required stack outputs returnAWS_STACK_OUTPUT_MISSING. AWS SDK provisioning failures returnAWS_PROVISIONING_OPERATION_FAILEDwith the failed operation and provider error cause. - If a deployed runtime is missing adapter environment values for a declared capability, it returns
CAPABILITY_NOT_DECLAREDdiagnostics naming the missing variable, such asANVIL_EVENT_BUS_NAMEfor events. - Auth provider lookup is alpha-scoped. Current preview verifies bearer tokens through OIDC when
ANVIL_AUTH_ISSUERis configured; otherwise authenticated handlers require a future auth integration or a smoke token setup. - Multi-region, custom domains, hosted control plane, marketplace, production policy packs, automated artifact rollback, signed artifacts, and real cost reporting are future work.
Safety posture
The Cell author should not author CloudFormation, CDK, SST, IAM policy, or AWS SDK calls directly. The adapter owns that translation.
If app code imports provider SDKs, Guard should reject it. Provider glue belongs in the adapter because app code is supposed to stay inspectable.