Skip to content

Realm settings (realm.yaml)

Configure authentication, sign-up, sessions, passwords, CORS and roles for a realm.

Every realm directory must contain a realm.yaml. It holds the settings that apply to the whole realm: its users, how they sign in, and which browser origins may call it.

$CONFIG_DIR/<realm>/realm.yaml

If a realm has no realm.yaml, backd refuses to start and names the realm. Create a commented starter file with:

CONFIG_DIR=./config backd template realm --realm <realm>

Example

auth: enabled
signup: invite
sessions:
  idle_timeout: 14d
  max_lifetime: 60d
password:
  min_length: 14
cors:
  origins:
    - https://app.example.com
    - http://localhost:5173
roles:
  admin:
    description: Manages users and invitations
    admin: true
    users:
      - ops@example.com
      - email: lead@example.com
  editor:
    description: Can publish articles

An empty file, or one with only comments, is valid: every key has a default.

Keys

KeyDefaultMeaning
authenabledenabled or disabled. With enabled, data routes need credentials (see who can access data) and provisioning creates the realm’s system database. A realm with disabled has no users, keys or access rules, so anyone who can reach backd can read and modify its data; backd logs a warning for it at startup.
signupclosedWho can create accounts: open (anyone), invite (needs an invitation), closed (only operators, with the CLI or the admin API).
sessions.idle_timeout30dA session expires after this long without use.
sessions.max_lifetime90dA session never lives longer than this. Must be at least idle_timeout.
sessions.cookie.enabledfalsetrue: browser apps can keep a session in an HttpOnly cookie, when their login asks for one. Needs explicit cors.origins (no *). See Session cookies
sessions.cookie.same_sitelaxlax, strict or none (an app on another site than the API)
sessions.admin_idle_timeoutidle_timeoutThe idle timeout for users who hold an admin role. Can only shorten idle_timeout.
sessions.admin_max_lifetimemax_lifetimeThe longest an admin’s session lives. Can only shorten max_lifetime; must be at least admin_idle_timeout.
password.min_length12Minimum password length, from 8 to 128.
login_throttle.account_threshold5Failed logins an email address gets before each new attempt must wait (1 or more). See Brute-force protection.
login_throttle.ip_threshold50The same per client address (an IPv6 /64), higher because a network can hide many people.
login_throttle.window15mHow long a failure counts: a counter expires this long after its last failure. At least 1m.
login_throttle.max_delay15mThe longest anyone waits; the wait starts at 1 second and doubles with each failure. At least 1s, and not more than window.
cors.originsnone (CORS off)Browser origins allowed to call this realm, as scheme://host[:port]. * allows any origin and must be the only entry. See CORS.
rolesnoneRoles that access rules can check. See below.
openapi.publicfalsetrue serves the realm’s OpenAPI document at /v1/<realm>/_openapi.json to everyone, not only to its administrators.
app_config.cache_ttl5mHow long clients may use the app configuration without asking again, from 1s to 1d.
audit.retention365dHow long audit records are kept, at least 1d.
admin.read_access.usersfalsetrue: read-only administrators (admin: read) can also read users (emails, roles, sessions). Users hold personal data, so a realm opts in.
admin.read_access.datafalsetrue: read-only administrators can read documents through the admin data route.
admin.allowed_networksnone (any network)IP addresses or CIDR networks, IPv4 or IPv6, allowed to use the admin API. See Network restrictions.
default_localeenThe language used for a user who has none and for email and push templates that lack the user’s. Must be in locales. See Languages
locales[default_locale]The languages the realm supports: each user’s locale is one of them, and every listed language needs every email template and page. Shared by email and push notifications
emailnone (no email)How the realm sends email: the delivery function, the sender, backd’s public address for links, the pages the links open, where users go afterwards, and limits. backd never sends mail itself. See Email
email.customizable, email.overrides.permissionnone, manage_orgThe kinds of email an organization may write its own template of, and the capability that edits them. See company email templates
pages.data_hooknoneAn internal, mode: sync function (<database>/<function>) that fills in {{.App}} in the hosted pages with a company’s branding; email.data_hook does it for emails. See company context
providersnoneThe external identity providers users may sign in with (google, microsoft, apple), each with its client id and secret:NAME references. See Sign in with providers
pushnone (no push)Push notifications: the platforms and their providers, the web subscription hosts and the device limits. Needs BACKD_SECRETS_KEY. See Push notifications
sign_in.allowed_redirectsemail.allowed_redirectsOrigins and app schemes a provider sign-in may send the user back to
sign_in.app_urlnoneWhere a sign-in goes back to when it has no redirect_to; within the allowed redirects
mfa.enabledtruefalse: users can’t turn a second factor on (users who have one are still asked for it). See Multi-factor authentication
mfa.issuerthe realm’s nameThe name authenticator apps show for the account
magic_link.enabledfalseLet users sign in with a link or a code emailed to them (needs email: and BACKD_SECRETS_KEY). See Magic links
magic_link.signupfalseAn address with no account may make one (verified) by using a link or code, when the realm’s signup is open
consentsnoneThe consents the realm asks for, each with required, current and per-version reaccept; texts live in consents/<name>/<version>/<locale>.md. See Consents
compliance.consent_proof[ip, user_agent]What each consent record keeps besides time, method and text hash
compliance.consent_retention3yHow long consent records are kept after their user was erased; 0 deletes them at erasure
reauth.ttl5mHow long a re-authentication token works (1m to 1h)
mfa.required_rolesnoneRoles that must use a second factor, or all. A sign-in without one sets it up first. See Required by role
account.require_verified_emailfalsetrue: no session until the address is verified. See Account
account.allow_email_changefalsetrue: users may change their own address
account.welcome_emailfalsetrue: send the welcome email once the address is verified
account.purge_unverified_afteroffDelete accounts that never verified after this long (at least 1m; never accounts with roles)
account.on_signupnoneAn internal, async function (<database>/<function>) called for every new user, with {user_id, provider, profile?}, to create the app’s profile document. See The profile and the sign-up hook
account.tokens.verify_email48hHow long the verification link works
account.tokens.reset_password1hHow long a password reset link works
account.tokens.change_email24hHow long the confirmation link of an email change works
account.tokens.revert_email_change7dHow long the link to undo one works
functions.max_concurrencynone (unlimited)Caps how many of the realm’s functions may run at once on this backd instance; with several instances, up to that many times this value run at once.
functions.log_retention7dHow long function invocation records are kept, at least 1h.
functions.job_retention24hHow long an async job’s result is kept once done, at least 1h.

Durations are a whole number of days (30d) or Go durations (12h, 90m), with a minimum of one minute.

signup, sessions, password, login_throttle, roles, admin, audit, email, providers, sign_in, account, functions.log_retention and functions.job_retention only apply when auth is enabled. Setting them in a realm with auth: disabled is an error, so a realm can’t carry settings that do nothing. cors and functions.max_concurrency apply either way.

CORS

Browsers only let a web page call backd from another origin if the realm allows that origin. List the origins of your web apps under cors.origins:

cors:
  origins:
    - https://app.example.com
    - http://localhost:5173
  • The list applies to every route of the realm: data, /_auth and /_admin.
  • Preflight requests (OPTIONS) from a listed origin get 204 with the allowed methods (GET, POST, PUT, PATCH, DELETE) and request headers (Authorization, Content-Type, If-Match, If-None-Match, Idempotency-Key, Prefer, X-Backd-Reauth, X-Backd-Types, X-Request-ID). The answer may be cached for 10 minutes.
  • Every response under /v1/ carries Vary: Origin, so caches keep answers for different origins apart (see Caching).
  • Responses to listed origins, errors included, carry Access-Control-Allow-Origin and expose ETag, Idempotent-Replayed, Location, Preference-Applied, Retry-After, WWW-Authenticate, X-Backd-Types-Stale and X-Request-ID.
  • Credentials travel in the Authorization header, so responses don’t allow credentials, except in a realm with session cookies (sessions.cookie): there, responses to the listed origins carry Access-Control-Allow-Credentials: true, and a wildcard origin is refused.
  • X-Backd-On-Behalf-Of isn’t allowed from browsers: only server-side services with API keys may use it.
  • * allows any origin. That’s reasonable for public, read-only data; for apps with users, list the origins explicitly.

CORS only tells browsers which pages may read responses. It isn’t access control: requests from other origins, and from anything that isn’t a browser, still go through authentication and access rules.

Account

account:
  require_verified_email: false
  welcome_email: false
  allow_email_change: false
  purge_unverified_after: 30d
  tokens:
    verify_email: 48h
    reset_password: 1h
    change_email: 24h
    revert_email_change: 7d

Each of require_verified_email, welcome_email, allow_email_change and purge_unverified_after needs the email section: without one, startup fails naming the key. What they do is described in Email verification.

Roles

Each key under roles declares a role name. Role names follow the same rules as realm names: lowercase letters and digits, optionally separated by single - or _.

  • description is optional and only for readers.
  • admin makes it an admin role: sessions of users holding it can use the admin API, as admin API keys can. admin: true opens every area of it to change; admin: read is a read-only level for developers (every area to read, users and data only if admin.read_access grants them, nothing to change); a list opens only those areas: admin: [users, invitations], and may include read: admin: [read, secrets] reads everything and changes secrets. The areas are users, invitations, apikeys, secrets, audit, functions, data and config; a user’s roles add up. See Admin rights for what each opens and the rules that stop an administrator from handing out more than they hold. backd bootstrap gives a realm’s first administrator a role with admin: true. Several roles can be admin roles. At startup, backd warns about a realm that declares no admin role, one with no role that has admin: true, or whose admin roles nobody holds (in realm.yaml or the database).
  • users lists the users who get the role: plain email addresses, or objects with an email key (- email: lead@example.com). These are seed assignments: they are applied by backd provision and every time backd serve starts, and to a listed email as soon as that user signs up or is created. They add the role and never remove it.

Other assignments can be made later with backd user add-role or the admin API, without editing the file; those live only in the database. At startup, backd logs how many users have assignments that realm.yaml doesn’t list, and warns about users holding roles that realm.yaml no longer declares.

Access rules check roles with hasRole(user, 'admin') or 'admin' in user.roles. Changes to a user’s roles apply from their next request.

Emails are trimmed and lowercased, and each can appear only once per role.

Organizations

Organizations are groups of users that documents can belong to, such as your customers’ companies. The section is optional; without it the realm has none. Organization roles are separate from the realm roles above: they mean something only inside one organization, and what they allow is declared here.

organizations:
  roles:
    owner:  { manage: [org, members, roles, teams] }
    admin:  { manage: [members, teams] }
    editor: {}
    member: {}
  default_roles: [member]
  create: user.email_verified
  creator_roles: [owner]
  max_created_per_user: 5
  invitation_link: "https://app.acme.example/join?token={token}&org={org}"
KeyWhat it saysDefault
enabledfalse switches the section off without removing ittrue
rolesThe organization roles: a name (same rules as realm roles), an optional description, manage (a capability or a list of them) and require_mfa (the role counts only for sessions that used a second factor)required
hooksMembership hooks: on_member_joined, on_member_left and on_roles_changed, each an internal, async function <database>/<function>none
team_rolesRoles held within one team: a name, an optional description, grants (team roles its holders may give, in their own team) and require_mfanone
default_rolesRoles every member holds without being granted one. They can’t have manage capabilitiesnone
createAn expression that decides who may create organizations; it sees user. "false" leaves it to realm administratorsuser.email_verified
creator_rolesThe roles the creator of an organization holds. One of them needs manage: org[owner]
max_created_per_userHow many organizations a user may create, deleted ones included5
max_memberships_per_userHow many organizations a user may belong to (at most 100)100
max_teams_per_org, max_teams_per_member, max_pending_invitationsCaps for teams and invitations100, 20, 500
allow_require_mfaWhether an organization may require a second factor of its memberstrue
members_visible_toWho sees other members’ emails: members, or managers (holders of any role with a manage capability)members
invitation_linkWhere invitation links point: an absolute URL with {token} once (and {org} if you want it) within sign_in.allowed_redirectsnone

The capabilities of manage:

CapabilityLets holders
orgrename the organization and change its slug, and delete it
membersremove members and change their roles, within what they hold themselves
rolesgive and take away any role, including roles with capabilities
teamscreate and change teams

Startup checks the section and names what is wrong: unknown capabilities, no role with manage: org, a create rule that doesn’t compile, an invitation_link outside the allowed redirects. Organizations need auth: enabled. A role removed from this section stays in the stored memberships and grants nothing; backd logs it at startup. Which collections belong to organizations is declared in collection.yaml. Realm administrators with the users area manage organizations with backd orgs and the admin API; people use the organizations API.

Network restrictions

Three settings limit where people may act from. Client addresses come from TRUSTED_PROXIES, so every one of these depends on it being exact: behind a proxy without it, every request seems to come from the proxy.

admin:
  allowed_networks: [10.20.0.0/16, "2001:db8:20::/48"]
roles:
  ops:
    admin: true
    users:
      - email: ada@example.com
        admin_networks: [10.20.1.0/24]     # her admin API requests
        login_networks: [10.20.0.0/16]     # her login and her session
      - bob@example.com                    # no restrictions of his own
SettingLimitsOutside it
admin.allowed_networksEvery admin API request, with an admin key or an admin session404 not_found, as if the admin API didn’t exist; credentials aren’t even checked. Logged as a WARN with the client address
admin_networks (in a role’s user entry)That user’s admin API requests. Must lie within admin.allowed_networks, or backd doesn’t start404 not_found, logged
login_networks (in a role’s user entry)That user’s login, and every request made with their sessionLogin answers 401 invalid_credentials, as for a wrong password, and counts as a failed attempt; requests with the session answer 401 unauthenticated. The session isn’t revoked: it works again from an allowed network
  • An empty or missing list means no restriction. At startup, backd logs (at info) each realm whose admin API is reachable from any network.
  • A user’s networks set in realm.yaml are applied like role seeds, at startup and when the user is created, and they win over changes made elsewhere. Networks set for users realm.yaml doesn’t cover live only in the database; startup logs how many users have them.
  • An email listed in several roles must have the same networks everywhere.
  • API keys can be limited too, when they’re created.

Realtime

The realtime: section lowers, for this realm, the instance’s limits on realtime streams. It can’t raise them.

realtime:
  max_subscriptions_per_stream: 10   # default: the instance's (20)
  max_streams_per_user: 2            # default: the instance's (5)

Triggers

The triggers: section sets the realm’s limits for collection triggers. A realm may lower the instance’s values and never raise them.

triggers:
  max_per_collection: 20       # triggers one collection may declare (default 20)
  max_chain: 16                # how deep a chain of trigger runs may go (default 16, at most BACKD_TRIGGER_MAX_CHAIN)
  max_chained_per_minute: 60   # chained events one trigger may cause per minute (default 60)
  max_backlog: 100000          # unfinished trigger jobs before events are recorded as gaps (default 100000)

Triggers need auth enabled in the realm. Operating them: lease, gaps, backd triggers status and rescan.

Storage

A realm that lets documents carry files connects its own S3-compatible bucket with a storage: section: the provider, bucket, a required prefix, and the access keys as secret:NAME references to realm secrets. See Connecting storage and the page of your provider (AWS S3, MinIO, Cloudflare R2, DigitalOcean Spaces). backd template realm writes it commented, with an example for each.

Validation

realm.yaml is read strictly. backd refuses to start, naming the file, when:

  • the realm has no realm.yaml;
  • the YAML is invalid or has an unknown key (a typo such as idle_timout is an error, not ignored);
  • a value is out of range or not one of the allowed values;
  • a user setting is present while auth is disabled;
  • a role name or email address is invalid, or an email is listed twice for a role;
  • a network is invalid, a user’s admin_networks isn’t within admin.allowed_networks, or an email has different networks in two roles.
Edit this page · Last updated 19 Oct 2026