<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Configuration on backd</title><link>https://fernandezvara.github.io/backd/docs/configuration/</link><description>Recent content in Configuration on backd</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://fernandezvara.github.io/backd/docs/configuration/index.xml" rel="self" type="application/rss+xml"/><item><title>Data model (CONFIG_DIR)</title><link>https://fernandezvara.github.io/backd/docs/configuration/config-dir/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/configuration/config-dir/</guid><description>&lt;p&gt;The data model is a directory tree under &lt;code&gt;CONFIG_DIR&lt;/code&gt;, loaded once at startup. Directories define realms and databases, and a &lt;code&gt;schema.json&lt;/code&gt; defines a collection.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$CONFIG_DIR/&amp;lt;realm&amp;gt;/
 realm.yaml # required — realm settings
 email/&amp;lt;kind&amp;gt;/ # optional — email templates (see Functions → Email)
 &amp;lt;database&amp;gt;/&amp;lt;collection&amp;gt;/
 schema.json # required — JSON Schema draft 2020-12
 indexes.json # optional — index declarations
 collection.yaml # optional — access rules, file fields, soft delete, erase policy
 rules.test.yaml # optional — tests of the rules (`backd rules test`)
 &amp;lt;database&amp;gt;/_functions/ # optional — server-side functions
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;Each directory under &lt;code&gt;CONFIG_DIR&lt;/code&gt; is a &lt;strong&gt;realm&lt;/strong&gt;. It must contain a &lt;a href="../realm/"&gt;&lt;code&gt;realm.yaml&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Each directory inside a realm is a &lt;strong&gt;database&lt;/strong&gt;, stored in the MongoDB database &lt;code&gt;&amp;lt;realm&amp;gt;__&amp;lt;database&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Each directory inside a database is a &lt;strong&gt;collection&lt;/strong&gt;, stored in a MongoDB collection of the same name. It must contain a &lt;code&gt;schema.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A realm&amp;rsquo;s &lt;code&gt;email&lt;/code&gt; folder holds its &lt;a href="../../functions/email/#templates"&gt;email templates&lt;/a&gt;, and &lt;code&gt;pages&lt;/code&gt; is reserved for the pages backd will serve; neither is a database, so they are never read as one and can&amp;rsquo;t be database names.&lt;/li&gt;
&lt;li&gt;A database&amp;rsquo;s &lt;code&gt;_functions&lt;/code&gt; directory holds its &lt;a href="../../functions/"&gt;functions&lt;/a&gt;, not a collection.&lt;/li&gt;
&lt;li&gt;Other plain files at the realm and database levels are ignored, as are hidden entries (names starting with &lt;code&gt;.&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Changing the config requires a restart. There is no hot-reload.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="starting-from-a-template"&gt;Starting from a template&lt;a class="hd-anchor" href="#starting-from-a-template" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;backd template&lt;/code&gt; writes commented starter files, so you don&amp;rsquo;t have to start from an empty directory. It only needs &lt;code&gt;CONFIG_DIR&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Realm settings (realm.yaml)</title><link>https://fernandezvara.github.io/backd/docs/configuration/realm/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/configuration/realm/</guid><description>&lt;p&gt;Every realm directory must contain a &lt;code&gt;realm.yaml&lt;/code&gt;. It holds the settings that apply to the whole realm: its users, how they sign in, and which browser origins may call it.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$CONFIG_DIR/&amp;lt;realm&amp;gt;/realm.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If a realm has no &lt;code&gt;realm.yaml&lt;/code&gt;, &lt;code&gt;backd&lt;/code&gt; refuses to start and names the realm. Create a commented starter file with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./config backd template realm --realm &amp;lt;realm&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="example"&gt;Example&lt;a class="hd-anchor" href="#example" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;enabled&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;invite&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;idle_timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;14d&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;max_lifetime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;60d&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;min_length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;https://app.example.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;http://localhost:5173&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Manages users and invitations&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ops@example.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;lead@example.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;editor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Can publish articles&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;An empty file, or one with only comments, is valid: every key has a default.&lt;/p&gt;</description></item><item><title>Schema validation</title><link>https://fernandezvara.github.io/backd/docs/configuration/validation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/configuration/validation/</guid><description>&lt;p&gt;Every collection&amp;rsquo;s &lt;code&gt;schema.json&lt;/code&gt; is enforced at two levels.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;API level (authoritative).&lt;/strong&gt; Every write is validated against the full JSON Schema draft 2020-12 before it reaches the database. Failures return &lt;code&gt;400&lt;/code&gt; with one entry per failing field.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database level (safety net).&lt;/strong&gt; The same schema is translated into a MongoDB &lt;code&gt;$jsonSchema&lt;/code&gt; collection validator. This protects the data from writes that bypass the API. MongoDB&amp;rsquo;s dialect is older and smaller, so the translation covers a subset of the schema.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="translation-to-mongodb"&gt;Translation to MongoDB&lt;a class="hd-anchor" href="#translation-to-mongodb" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;JSON Schema&lt;/th&gt;
 &lt;th&gt;MongoDB &lt;code&gt;$jsonSchema&lt;/code&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;type: string&lt;/code&gt; / &lt;code&gt;boolean&lt;/code&gt; / &lt;code&gt;object&lt;/code&gt; / &lt;code&gt;array&lt;/code&gt; / &lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;bsonType: string&lt;/code&gt; / &lt;code&gt;bool&lt;/code&gt; / &lt;code&gt;object&lt;/code&gt; / &lt;code&gt;array&lt;/code&gt; / &lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;type: integer&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;bsonType: [int, long, double]&lt;/code&gt; plus &lt;code&gt;multipleOf: 1&lt;/code&gt;, unless the schema sets its own &lt;code&gt;multipleOf&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;type: number&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;bsonType: [int, long, double, decimal]&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;const: v&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;enum: [v]&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;exclusiveMinimum: n&lt;/code&gt; / &lt;code&gt;exclusiveMaximum: n&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;minimum&lt;/code&gt; / &lt;code&gt;maximum: n&lt;/code&gt; plus &lt;code&gt;exclusiveMinimum&lt;/code&gt; / &lt;code&gt;exclusiveMaximum: true&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;properties&lt;/code&gt;, &lt;code&gt;required&lt;/code&gt;, &lt;code&gt;enum&lt;/code&gt;, &lt;code&gt;minimum&lt;/code&gt;, &lt;code&gt;maximum&lt;/code&gt;, &lt;code&gt;multipleOf&lt;/code&gt;, &lt;code&gt;minLength&lt;/code&gt;, &lt;code&gt;maxLength&lt;/code&gt;, &lt;code&gt;pattern&lt;/code&gt;, &lt;code&gt;items&lt;/code&gt;, &lt;code&gt;minItems&lt;/code&gt;, &lt;code&gt;maxItems&lt;/code&gt;, &lt;code&gt;uniqueItems&lt;/code&gt;, &lt;code&gt;additionalProperties&lt;/code&gt;, &lt;code&gt;minProperties&lt;/code&gt;, &lt;code&gt;maxProperties&lt;/code&gt;, &lt;code&gt;allOf&lt;/code&gt;, &lt;code&gt;anyOf&lt;/code&gt;, &lt;code&gt;oneOf&lt;/code&gt;, &lt;code&gt;not&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;copied as-is (subschemas translated recursively)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;Annotations (&lt;code&gt;title&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;default&lt;/code&gt;, &lt;code&gt;examples&lt;/code&gt;, &lt;code&gt;$comment&lt;/code&gt;, …) are dropped silently.&lt;/li&gt;
&lt;li&gt;Any other keyword (for example &lt;code&gt;format&lt;/code&gt;, &lt;code&gt;$ref&lt;/code&gt;/&lt;code&gt;$defs&lt;/code&gt;, &lt;code&gt;prefixItems&lt;/code&gt;, &lt;code&gt;unevaluatedProperties&lt;/code&gt; or &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;then&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt;) is left out of the database validator and logged at &lt;code&gt;info&lt;/code&gt; level with its location. The API level still enforces it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="finding-documents-that-no-longer-match"&gt;Finding documents that no longer match&lt;a class="hd-anchor" href="#finding-documents-that-no-longer-match" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A schema is enforced when a document is &lt;strong&gt;written&lt;/strong&gt;. Documents stored before you changed the schema are not touched, and MongoDB is set to leave them alone (&lt;code&gt;validationLevel: moderate&lt;/code&gt;): a collection can hold documents that would be refused today. A &lt;em&gt;schema check&lt;/em&gt; lists them. It reads every stored document, validates it with the same full validator the API uses on a write (so it also sees the keywords MongoDB&amp;rsquo;s translation leaves out, such as &lt;code&gt;format&lt;/code&gt;), and reports the ones that fail. It &lt;strong&gt;never writes&lt;/strong&gt; anything.&lt;/p&gt;</description></item></channel></rss>