Anvil Cloud / Architecture
Cell contract
An Anvil Cell is a small TypeScript app unit. It default-exports an app() definition from the server entrypoint.
The contract is intentionally smaller than a cloud provider. Cell code declares application behavior and capabilities; deployment adapters translate those concepts into provider resources.
Project shape
notes/
AGENTS.md
anvil.json
package.json
tsconfig.json
index.html
vite.config.ts
src/
cell.server.ts
client/
App.tsx
main.tsx
styles.css
anvil.json points at the server and client entrypoints:
{
"name": "notes",
"entrypoints": {
"server": "src/cell.server.ts",
"client": "src/client/main.tsx"
},
"runtime": "nodejs20",
"region": "eu-west-2"
}
Server definition
import {
app,
boolean,
mutation,
query,
table,
text,
userId,
} from "@anvil-cloud/runtime";
export default app({
schema: {
todos: table({
text: text().min(1).max(500),
done: boolean().default(false),
ownerId: userId(),
}),
},
capabilities: {
database: true,
},
queries: {
listTodos: query({
handler: async (ctx) => {
return ctx.db.todos.where("ownerId", "=", ctx.auth.requireUser()).all();
},
}),
},
mutations: {
addTodo: mutation<{ text: string }>({
handler: async (ctx, input) => {
return ctx.db.todos.insert({
text: input.text,
done: false,
ownerId: ctx.auth.requireUser(),
});
},
}),
},
});
Supported definition types
| Definition | Purpose |
|---|---|
app |
Root Cell definition containing schema, capabilities, handlers, endpoints, jobs, workflows, and services. |
table |
Declares a table in the Cell schema. |
text, boolean, userId |
Current field builders. |
query |
Read-oriented named server function. |
mutation |
Write-oriented named server function. |
endpoint |
Declared HTTP route with method, path, optional auth mode, and handler. |
job |
Named background handler, optionally scheduled. |
workflow |
Durable ordered steps with retries, timeouts, and persisted run state. |
service |
Supervised long-running local handler with restart policy. |
Capabilities
Capabilities declare what the Cell expects from the runtime host and deployment adapter.
Current examples include:
capabilities: {
database: true,
files: { publicRead: false },
outboundFetch: {
allow: ["api.example.com"]
},
scheduledJobs: true,
jobs: true,
events: true,
workflows: true,
services: true
}
Guard checks use these declarations to reject direct provider access and undeclared effects where alpha can detect them.
Manifest output
The builder imports the server bundle and extracts a manifest.
{
"schemaVersion": "0.1",
"cell": {
"name": "notes",
"runtime": "nodejs20",
"target": "local"
},
"entrypoints": {
"server": "dist/server/index.mjs",
"client": "dist/client/index.html"
},
"schema": {
"tables": []
},
"queries": ["listTodos"],
"mutations": ["addTodo"],
"endpoints": [],
"jobs": [],
"workflows": [],
"services": [],
"capabilities": {
"database": true
}
}
Adapters should consume the manifest rather than crawling arbitrary source.
Contract rules
- App code should use
ctx, not provider SDKs. - Server code should stay statically inspectable.
- Dynamic import is forbidden in Cell server code.
- Direct
process.envaccess is forbidden. Usectx.env. fsandnode:fsare forbidden. Usectx.files.child_processis forbidden. Move background work into declared jobs.@aws-sdk/*,aws-cdk-lib,sst,cdktf, andpulumiare forbidden in Cell server code.- Global
fetchrequirescapabilities.outboundFetch, and static absolute URL hosts must be listed incapabilities.outboundFetch.allow. Local runtime request handlers and workflow steps, plus AWS preview, reject undeclared hosts withOUTBOUND_FETCH_NOT_ALLOWED. - Handler use of
ctx.db,ctx.files,ctx.events,ctx.jobs, andctx.workflowsmust match declared capabilities where Guard can inspect it.
The restrictions are not there to be fancy. They keep the app contract small enough for people and agents to reason about.