Backbone data & state
The backbone section says what to seed and configure: which collections
to fill, which databases to build a schema in, which queues to create, and the secrets and cache
entries to set.
Sizes live in the slice's shape.
A collection, bucket or database has to exist before anything can be written to it. The
slice refuses a write naming one it does not have with 400 rather than creating it, so the
set is declared where the shape lives. What an entry below does is name a slot the slice already holds
and say what to put in it.
NoSQL collections
nosql is a list of maps, one per collection. slot names the collection this
project uses; the long form also accepts a seed, a JSONL file (one JSON document per line)
that the deploy upserts by each document's _id, and/or a ttl.
nosql:
- slot: submissions # used, nothing to seed
- slot: permit-types
seed: ./backbone/permit-types.jsonl # seeded from JSONL
- slot: sessions
ttl: 30d # reaped 30 days after last writename is the old spelling of slot.
# permit-types.jsonl: one JSON document per line, each with an _id
{"_id": "residential-extension", "name": "Residential extension"}
{"_id": "new-build-residential", "name": "New-build residential"}
Seeding is idempotent (re-deploying re-upserts changed rows) and
non-destructive (documents your app added at runtime are left alone, because their
_ids aren't in the seed file). Every line must be valid JSON with a non-empty
_id.
ttl (<int>s/m/h/d) deletes a document
once its last write is older than the TTL. The clock resets on every update, so only
genuinely stale, untouched documents are reaped. There's no undo: documents aren't soft-deleted or archived
anywhere. Omit it and documents are kept forever. Applies per-collection, not per-document.
SQL databases
sql is a list of per-slice SQLite databases, addressed by name. Each entry
materialises as one .db file and is reached from the SDK as drift.Backbone.SQL("name")
for relational queries and transactions. An entry names a database the slice already holds and says
what to build in it: a schema and/or a seed. A database with neither needs no
entry at all.
sql:
- name: app
schema: ./backbone/app.sql # idempotent DDL, runs on every deploy
seed: ./backbone/app-seed.sql # runs only on the first deploy
schema is applied on every deploy, so keep it idempotent
(CREATE TABLE IF NOT EXISTS …). seed runs only when the database
has no user tables yet, which is the first deploy, so later deploys never re-run it, even
if you change the file. To re-run a seed, start from an empty database again: drop it with
drift backbone sql drop <name> and redeploy, and the deploy recreates the database
and re-applies the schema and seed. A database name is 1–64 characters
([a-z0-9], then [a-z0-9_-], then [a-z0-9]) and becomes both the
.db filename and the SDK lookup key.
- Type
- string
- Default
- required
- Meaning
- Database identifier and SDK lookup key, as in
drift.Backbone.SQL("name"). It references a database the slice holds.
- Type
- path
- Default
- none
- Meaning
- SQL file of idempotent DDL, applied on every deploy.
- Type
- path
- Default
- none
- Meaning
- SQL file run only on the first deploy (when the database has no user tables yet).
Read the Backbone SQL guide → for querying and transactions from your functions.
Blob buckets
blobs is a list of the buckets this project uses. A bucket has no seed or schema, so an
entry does nothing but record that the project writes to it — and buckets are sized in the
slice's shape, so a project can leave the section out entirely. Code doesn't reach a bucket through a
handle the way SQL("name") does; drift.Backbone.Blob.Put/.Get
take one path-shaped name (e.g. "uploads/receipt-42.pdf"), where everything before the
first / is the bucket, and a name with no / goes to the default
bucket.
blobs:
- name: uploads- Type
- string
- Default
- required
- Meaning
- Bucket name: the prefix before the
/in a blob's path-shaped name. It references a bucket the slice holds.
A bucket has to be declared before anything can be written to it. A put naming a bucket
this slice does not declare — default included — is refused with
400, and the same holds for an undeclared collection. See
Blobs and NoSQL.
Queues
A list of the queues this project uses. Queues can't be seeded, because seeding messages on every deploy would re-fire work, so an entry is a name and nothing more. Each queue's depth is set in the slice's shape, which also carries a default for one that names no depth of its own.
queues:
- validate # bare name
- name: notify # long form: same thing, room to grow
The map form takes name and nothing else. It exists so per-queue options can arrive without
breaking manifests; until they do, any other key inside it is rejected rather than
ignored, so a typo fails the deploy instead of silently doing nothing.
Secrets
A map of KEY: value. A value of $NAME (unquoted) is a reference resolved when
drift file apply runs. The literal never touches the Driftfile or the wire, only
the resolved string travels. Quote a value to force a literal that starts with $.
secrets:
MUNICIPALITY_NAME: "Amsterdam" # hardcoded literal
RESEND_API_KEY: $RESEND_API_KEY # resolved at deploy time
A $NAME reference (and any ${VAR} placeholder elsewhere in the Driftfile)
resolves through a variable origin hierarchy, highest precedence first:
- Hardcoded: a literal value written in the Driftfile. Absolute.
- Environment: a variable exported in your shell session.
- Override flags:
--secret KEY=value(repeatable), and--env <name>(same as the positional environment; also setsENV). An override yields to a variable the environment set; it beats the env files. .env.<env>file: when an environment is selected, the per-environment secrets file (e.g..env.staging) next to the Driftfile. Out-ranks the base.env..envfile: the baseKEY=valuefile next to the Driftfile, sourced automatically (lowest precedence; fills only what nothing above provided). Pass--no-env-fileto skip the env files.
So an exported DB_PASSWORD beats --secret DB_PASSWORD=…, which beats a
DB_PASSWORD line in .env.staging, which beats one in .env. A
deploy prints what it loaded from which env file, and what it ignored because the environment supplied
it, so the layering is never silent. Config dials that differ per environment belong in
environments:; only secrets belong in
.env.<env>.
Cache
A map from key to a value to pre-warm at deploy. The short form is a file path whose
contents become the cached value. The long form takes an inline value (or a
file) plus an optional ttl in seconds, after which the entry expires
and a Cache.Get returns empty until your code re-populates it. Omit ttl (or
set 0) for an entry that never expires.
cache:
menu: ./backbone/menu.json # short form: file contents become the value
banner: # long form: inline value with a TTL
value: "Closed for maintenance"
ttl: 3600 # seconds; 0 / omitted = never expires
pricelist: # long form: file + TTL
file: ./backbone/prices.json
ttl: 86400At runtime the cache is read/write from your functions, so these entries are just the initial warm set. Read the Backbone Cache guide →