Skip to content

12. Running it

API keys for machines, the audit trail in the app, and the checklist before any of this faces the internet.

The shelf works. The last chapter is about operating it: who acts without a session, who did what, and what to review before a real deployment.

API keys

(This chapter needs the Admin view, so sign in as operator@shelf.example.)

Sessions are for people; API keys are for machines — a CI job that reads the gallery, a monitor that calls a function. In the Admin view, type a name (say ci-reader) and press Create read:main key: it makes one scoped to read-only on main, expiring in 90 days:

backd.admin.apiKeys.create({ name: 'ci-reader', expiresIn: '90d', scopes: ['read:main'] })

The full key (bdk_…) shows once, in the creation response — store it in a secret store; backd keeps only its prefix. scopes limit what a key reaches (read, write, call, optionally :database or :database/name); revoke stops it at once (API keys). With a key, an unattended script uses the same client: createClient({ url, realm, apiKey }) — no signup, no session.

The audit trail

Every admin-visible act is recorded — invites created and sent, keys created and revoked, functions invoked by hand, logins, erasures. backd.admin.audit.list() pages it newest first; the Admin view shows who (user:<id>, key:<name>, cli:bootstrap), what (apikey.create, function.invoke_manual, …) and the target. It never holds secrets or email bodies (Audit).

Try it: create a key, revoke it, refresh the feed — apikey.create and apikey.revoke both appear, attributed to you.

Before the internet sees it

This tutorial ran on a dev stack: BACKD_DEV on, email-capture storing mail, demo accounts in realm.yaml, dev-secret webhook keys, localhost redirects. The hardening checklist is the page to walk before a realm goes real — for this app specifically:

  • Swap email.function to mail/postmark (or your provider), set its secrets — and email-capture’s dev_only already guards you: backd won’t start with it outside dev mode.
  • Set real function secrets (IMPORT_WEBHOOK_SECRET, Postmark’s) — dev-secret signs nothing an attacker can’t forge.
  • Remove the seeded roles.*.users demo accounts and create your own administrator with backd bootstrap (chapter 4). Removing a seed from realm.yaml does not take the role away from the account that already has it — remove it from that user with the admin API (DELETE /_admin/users/{id}/roles/{role}, see Admin API).
  • Keep signup: invite: it is the right setting for a team library. Anything else puts the door back.
  • Give every API key a scope and an expiry — the app already creates them that way.
  • Review the rules: of each collection.yaml and run backd rules test in CI.

You should see

  • The Admin view lists real audit entries for everything you did in chapters 4–11.
  • A new key authenticates GET /v1/shelf/main/assets but not a write — 403, scoped.
  • Revoking the key fails the same read with 401, and apikey.revoke is in the feed.

What Shelf is now

CollectionWho readsWho writes
assetsmembers: published + their own drafts; curators: every draftmembers create/edit/delete their own drafts; publish stamps published_at
sharesthe creatoronly share creates; the creator deletes
notificationsthe recipient (may set read_at)only notify
membersevery membereach member their own row
importsnobodyonly import
mail/outboxanyone (dev only)only email-capture
FunctionModeWho calls it
publishsynccurators (idempotency key required)
sharesyncany member (idempotency key required)
share-opensyncanyone, rate-limited
previewsync, network:any member
notifysync, internalother functions only
digestasync, scheduled daily, sends emailoperators, and the schedule
cleanupasync, internal, scheduled 03:00 UTCthe schedule, or by hand
importwebhookthe signed feed

Where next

  • Deploy it: Deploying and Production (TLS, separate networks for the executor and egress, secrets), then the checklist above.
  • Operate it: Backups, Metrics (Prometheus, with dashboards in the repository), and the audit feed you saw.
  • Go deeper on functions: the cookbook (payments, exports, retries) and Testing functions.
  • Use it from your own app: the JavaScript client and the API reference (api/openapi.yaml).
  • Test the tutorial itself: on a checkout, make shelf-tour replays everything in this tutorial against a running stack (clients/js/examples/shelf/tour.js), and make shelf-tour-ci builds the starter folder and the images from the checkout first — the check CI runs on every push, so this tutorial cannot drift from backd unnoticed.
Edit this page · Last updated 08 Oct 2026