Atomic functions
The atomic section is where your functions are declared, plus the runtime limits that
apply to all of them. functions[] is the whole declaration: what
the slice exposes, which callable serves it, what it books and what guards it. Your source carries
none of that. It is ordinary source, and the handlers sit in atomic/ as flat files
(that flat set is the default element, one language, one backend).
The section carries two keys. Timeouts, the rate limit, deploy history, the storage budget and every function's memory booking belong to the slice and are set in the slice's shape.
- Type
- map
- Default
{ mode: open }- Meaning
- Outbound network posture; see Outbound egress.
- Type
- list of maps (required if you run any)
- Default
- none
- Meaning
- Every function to deploy. Never inferred: a callable the file does not name is ordinary source and gets no route.
A function's memory is chosen when the slice is shaped, not here.
atomic.functions[].memory still parses and is ignored.
drift file benchmark measures what each function
actually costs and prints what it should book; --apply opens the form with those figures
filled in.
Declaring functions[]
Every entry is a map, and three of its keys are required: route, method and
handler. The rest are optional, and they are how a function gets a gate, its own config,
its secrets, a reply shape, a schedule or an alert. There is nowhere else to put any of it.
- Type
- path
- Default
- required
- Meaning
- The path this function answers on, under
/api/:ping,auth/challenge,groups/:id. For a queue-triggered function it is the queue's name instead.
- Type
getpostputpatchdeleteheadoptionsqueue- Default
- required
- Meaning
- Part of the identity, not a detail:
getandposton one route are two functions with two slots.queuemeans the slice invokes it per message and it has no URL.
- Type
- callable name
- Default
- required
- Meaning
- The function in your source that serves this. Looked for in the element's folder only, so two elements may each have a same-named
handler. It has to be reachable from outside its file: exported in Go,pubin Rust, a namedfunctionin Node.
- Type
none|apikey- Default
none- Meaning
- The gate in front of it. Setting an API key on the route forces
apikeywhatever this says, since the key is the stronger statement. See authentication.
- Type
sse|ws- Default
- buffered
- Meaning
- Hold the connection open instead of buffering one response. See streaming.
- Type
envelope|json|raw- Default
envelope- Meaning
- The shape this function's own reply takes.
envelopewraps your payload as{status, message, payload}, which is what the SDKs expect.jsonwrites your payload as the whole body, for an endpoint a third party parses directly: a webhook receiver, an OAuth token endpoint.rawwrites it as bytes, base64-decoded, for a body that isn't text at all; set your own Content-Type with it.
- Type
- map of strings
- Default
- none
- Meaning
- Plain configuration for this function, injected into its process environment verbatim: a base URL, a bucket name, a feature flag, anything that is not a credential. Kept apart from
secrets[]on purpose, since a value written here sits in your repository in plain text. A short list of names,DRIFT_*and a few others such asBACKBONE_URL, is reserved and refused bydrift file lint.
- Type
- list of names
- Default
- none
- Meaning
- The Backbone secrets this one function may read. A secret not named here is unreachable from it even though the slice holds it.
- Type
- path
- Default
- the element's folder
- Meaning
- Source directory, relative to the Driftfile, for the odd function that lives outside its element's folder.
- Type
- string
- Default
- the flat element
- Meaning
- The sub-app this function belongs to: the group that shares one language, one dependency manifest, one runtime.
- Type
- 5-field cron
- Default
- none
- Meaning
- Also run this function on a schedule. It keeps its own trigger; the deploy ships a schedule alongside it.
- Type
- list of maps
- Default
- none
- Meaning
- Fire a notification when this function errors. Reconciled against the live alert registry on every deploy.
functions:
- route: digest
method: get
handler: GetDigest
- route: charge
method: post
handler: PostCharge
auth: apikey # demands a key this slice issued
env:
STRIPE_API_BASE: https://api.stripe.com # config, not a credential
secrets:
- STRIPE_KEY # the only secret this one may read
- route: webhooks/stripe
method: post
handler: PostStripeWebhook
response: json # the whole body is the payload; Stripe parses it directly
- route: orders # no URL; the slice invokes it per message
method: queue
handler: HandleOrder
- route: reconcile
method: post
handler: PostReconcile
element: billing # lives with the other billing functions
cron: "0 3 * * *" # 03:00 daily, 5 POSIX fields
alerts:
- on: errors # errors is the only accepted trigger
threshold: 5 # errors within the window
window: 5m # minimum 60s
notify: webhook=https://hooks.example.com/drift
cron is a 5-field POSIX expression; the parser checks the field count and the scheduler
validates the grammar, so a malformed expression surfaces at deploy. An alert needs
window and notify, and both are errors if missing. threshold
defaults to 1, on to errors, and window must be at
least 60 seconds. The only notify form is webhook=<http(s) URL>.
The deploy writes every alert the file declares. One that is live and no longer declared is
left in force and named in the report apply prints at the end of a run. A slice
can serve more than one project, so a deploy cannot tell an alert that is not yours from one you
dropped. Take it away with drift atomic alert remove <name>.
Outbound egress
atomic.egress declares which hosts your functions may reach, and the deploy enforces it: the
platform resolves every declared host to its current IPs and renders them into the slice's own outbound
rule, so a call to an undeclared host is refused at the network rather than merely undocumented.
The Driftfile is the source of truth for the allowlist; drift atomic egress list/refresh/test
inspect what the slice currently has, and there is no CLI verb to add a single host. Edit
hosts and deploy again.
atomic:
egress:
mode: allowlist # open (default) | allowlist
hosts:
- api.stripe.com
- smtp.sendgrid.net:587 # host:port; port defaults to 443Hosts are concrete, never wildcards.
drift file lint refuses one rather than accepting it and
enforcing nothing. An allowlist with no hosts denies every outbound request.
Private ranges are a separate matter and are blocked regardless of mode: RFC-1918,
link-local and CGNAT egress is refused from a slice whatever it says.
Read the Atomic guide → for how to write a function.