Backbone
Backbone is your slice's encrypted data layer. Your functions reach it through the SDK with a single call: no connection strings, no ORM, no migrations. Collections, queues, buckets and the rest spring into existence the first time you use them, and you can seed and inspect them from the CLI.
The primitives
- NoSQLJSON collections, upserted on your own _id.
- SQLPer-slice SQLite, with schemas and transactions.
- BlobsFiles and uploads, organised by bucket.
- SecretsEncrypted config, injected into your functions.
- QueuesFIFO messages that can trigger a function.
- CacheStrings with an optional TTL.
- LocksStop two workers doing the same job at once.
- RealtimeIn-slice pub/sub, fanned out over WebSocket.
Each primitive has a drift backbone <name> command group of its own, and the whole surface is in the CLI reference. Inspect your slice's data and usage any time with drift backbone status.
Calling Backbone from a function
Each SDK spells the namespace the way its language does. The same insert, six ways:
| Language | Call |
|---|---|
| Go | drift.Backbone.NoSQL.Collection("users").Insert(doc) |
| Python | drift.backbone.nosql.collection("users").insert(doc) |
| Node | await drift.backbone.nosql.collection("users").insert(doc) |
| Ruby | Drift::Backbone::Nosql.collection("users").insert(doc) |
| PHP | \Drift\Backbone\Nosql::collection("users")->insert($doc) |
| Rust | drift::backbone::nosql::collection("users").insert(json) |
Every Node Backbone call returns a promise, so await it. The examples on each primitive's page are Go; the table above translates them.
Limits you cannot declare
The quotas you buy and declare in your Driftfile (collection and bucket sizes, queue depth, realtime connections) run inside a set of fixed caps built into the slice. These are the same on every tier and there is no key for them.
| Primitive | Fixed cap |
|---|---|
| Cache | 1 MiB per value · 10,000 entries · 64 MiB of total value bytes |
| NoSQL | list returns 100 by default, 1,000 maximum |
| SQL | 64 KiB per statement · a transaction idle for 30 s is rolled back |
| Realtime | 512 KiB per message · 128 connections per channel · 2,048 channels per slice |
| Queue triggers | polled every 500 ms · 3 attempts, then <queue>-dlq |
Names are validated on the way in, and the charset differs by what the name becomes:
| Name | Charset |
|---|---|
Collection, queue, bucket, NoSQL _key | First character alphanumeric, then alphanumerics . _ -, up to 255 bytes. |
| Blob key | The same plus /, up to 513 bytes, no ... |
| Indexed field name and value | First character alphanumeric or _, then alphanumerics . _ -, up to 255 bytes, no ... |
| Realtime channel | Alphanumerics and : . _ ~ -, up to 256 bytes. |
| Lock | Any non-empty string. |
Status codes
The primitives share one status contract, so a code tells you what went wrong without reading the message. The SDKs carry it into the error they return; the CLI prints it.
| Code | Meaning |
|---|---|
200 | Done. A read carries the value; queue push carries the message id. |
204 | Nothing to return: an empty queue on pop or peek, a successful NoSQL delete, a successful SQL commit or rollback. |
400 | Bad request: malformed JSON, a missing parameter, a name outside its charset, an unindexable filter, an unknown transaction token, or an error from SQLite. |
401 | The route is internal. A function reaching for a secret it did not declare, or calling Secret.Set, lands here. |
404 | No such key, blob, queue or lock. |
409 | The lock is held by someone else. |
413 | Over a quota you declared: a collection or bucket at its size, or a SQL statement over 64 KiB. |
429 | Over a platform cap: cache full, queue at queue_max_depth, blob count cap, concurrent-lock cap. |
503 | The slice is at its realtime connection cap, or draining. |
Backbone under drift atomic run
Local dev has no slice behind it. Every SDK answers Backbone calls from an in-memory store inside your own process, which behaves differently from a deployed slice in ways worth knowing before you trust a green local run:
| Local | Deployed |
|---|---|
| State lives in the process and is lost on exit. Nothing is encrypted. | Encrypted at rest with the slice's own key, and durable. |
Insert returns a key. | Insert returns an empty string. |
| No quotas, no TTL expiry, none of the caps above. | All of them enforced. |
Secrets are read from the environment; the CLI loads .env from your source directory. | Declared secrets arrive as DRIFT_SECRET_<NAME> and in req.secrets. |
| SQL has no local implementation. | SQLite per database, encrypted at rest. |
Blobs live in a per-SDK scratch map, and Go's Blob.Put stores nothing at all. | Stores the bytes, encrypted. |
No realtime hub: Publish and Presence report 0. | Fans out to live subscribers. |
Anything that depends on those differences needs a deployed slice to test against.
What a snapshot captures
drift slice snapshot create archives secrets, NoSQL collections, blobs, queues and SQL databases. Cache and locks are not in the archive, because both hold data that is recomputable or short-lived by design, so restoring a snapshot gives you a cold cache and no held locks.
Blobs are captured from the same top-level bucket listing the CLI uses, so a key containing a slash is absent from the archive as well as from blob list.