<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Organizations on backd</title><link>https://fernandezvara.github.io/backd/docs/organizations/</link><description>Recent content in Organizations on backd</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://fernandezvara.github.io/backd/docs/organizations/index.xml" rel="self" type="application/rss+xml"/><item><title>Build a B2B app</title><link>https://fernandezvara.github.io/backd/docs/organizations/build-a-b2b-app/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/organizations/build-a-b2b-app/</guid><description>&lt;p&gt;This recipe builds the backend of a small project tracker where each customer is an organization. It uses a realm called &lt;code&gt;acme&lt;/code&gt; with a &lt;code&gt;main&lt;/code&gt; database, and the &lt;a href="../../clients/js/#organizations"&gt;JavaScript client&lt;/a&gt;. The finished version, with a UI, is the &lt;a href="../../tutorial/20-teams/"&gt;Shelf tutorial&amp;rsquo;s chapter 20&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="../"&gt;concepts&lt;/a&gt; first if &amp;ldquo;effective role&amp;rdquo; or &lt;code&gt;_meta.org&lt;/code&gt; are new.&lt;/p&gt;
&lt;h2 id="1-declare-the-roles"&gt;1. Declare the roles&lt;a class="hd-anchor" href="#1-declare-the-roles" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;In &lt;code&gt;realm.yaml&lt;/code&gt;:&lt;/p&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="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;organizations&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;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;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description: Runs the company, manage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;org, members, roles, teams] }&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description: Invites and removes people, manage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;members, teams] }&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 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;Creates and edits projects }&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;member&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&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;Reads }&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;default_roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;member]&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;creator_roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;owner]&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;invitation_link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://app.acme.example/join?token={token}&amp;#34;&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;ul&gt;
&lt;li&gt;The &lt;code&gt;manage&lt;/code&gt; capabilities decide who administers the organization; &lt;code&gt;owner&lt;/code&gt; needs &lt;code&gt;org&lt;/code&gt;, since an organization always keeps someone who can manage it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default_roles&lt;/code&gt; are held by every member, so &lt;code&gt;member&lt;/code&gt; is never granted one by one.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;invitation_link&lt;/code&gt; must be inside the realm&amp;rsquo;s &lt;a href="../../configuration/realm/"&gt;&lt;code&gt;sign_in.allowed_redirects&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Creating organizations is allowed to anyone with a verified email by default (&lt;code&gt;create: user.email_verified&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-mark-the-collections"&gt;2. Mark the collections&lt;a class="hd-anchor" href="#2-mark-the-collections" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;In &lt;code&gt;main/projects/collection.yaml&lt;/code&gt;:&lt;/p&gt;</description></item><item><title>Rules cookbook</title><link>https://fernandezvara.github.io/backd/docs/organizations/rules-cookbook/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/organizations/rules-cookbook/</guid><description>&lt;p&gt;Rules for collections with an &lt;a href="../../configuration/config-dir/#organizations"&gt;&lt;code&gt;organization:&lt;/code&gt;&lt;/a&gt;. The helpers are &lt;code&gt;inOrg(org)&lt;/code&gt;, &lt;code&gt;hasOrgRole(org, role)&lt;/code&gt; and &lt;code&gt;orgClaims(org).claim&lt;/code&gt;, described in &lt;a href="../../auth/rules/#organizations"&gt;access rules&lt;/a&gt;; &lt;code&gt;document._meta.org&lt;/code&gt; is the organization of the document and &lt;code&gt;data._meta.org&lt;/code&gt; the one a create names (&lt;code&gt;?org=&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id="members-read"&gt;Members read&lt;a class="hd-anchor" href="#members-read" 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;rules&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;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;inOrg(document._meta.org)&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;Everyone who belongs to the organization reads its documents, and nobody else sees them, counts or lists. In a read rule this is a database filter.&lt;/p&gt;
&lt;h2 id="editors-write-admins-delete"&gt;Editors write, admins delete&lt;a class="hd-anchor" href="#editors-write-admins-delete" 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;rules&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;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;inOrg(document._meta.org)&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;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hasOrgRole(data._meta.org, &amp;#39;editor&amp;#39;) || hasOrgRole(data._meta.org, &amp;#39;admin&amp;#39;)&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;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hasOrgRole(document._meta.org, &amp;#39;editor&amp;#39;) || hasOrgRole(document._meta.org, &amp;#39;admin&amp;#39;)&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;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hasOrgRole(document._meta.org, &amp;#39;admin&amp;#39;)&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;Roles are checked in the document&amp;rsquo;s organization only: an &lt;code&gt;admin&lt;/code&gt; of one customer holds nothing in another. Effective roles include the ones from teams and &lt;code&gt;default_roles&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Team roles</title><link>https://fernandezvara.github.io/backd/docs/organizations/team-roles/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/organizations/team-roles/</guid><description>&lt;p&gt;&lt;a href="../../api/organizations/#teams"&gt;Teams&lt;/a&gt; carry organization roles to everyone in them. That is enough for &amp;ldquo;the editors&amp;rdquo;, but not for &amp;ldquo;Marta manages &lt;strong&gt;Sales&lt;/strong&gt;&amp;rdquo;: a manager of Sales must not manage Support. &lt;strong&gt;Team roles&lt;/strong&gt; are roles held &lt;em&gt;within one team&lt;/em&gt;, by a member of the organization, and they mean nothing outside it.&lt;/p&gt;
&lt;h2 id="organization-roles-teams-and-team-roles"&gt;Organization roles, teams and team roles&lt;a class="hd-anchor" href="#organization-roles-teams-and-team-roles" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;Organization role&lt;/th&gt;
 &lt;th&gt;Team&lt;/th&gt;
 &lt;th&gt;Team role&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Declared in&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;organizations.roles&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;created through the API&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;organizations.team_roles&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Holds&lt;/td&gt;
 &lt;td&gt;what the role allows in the organization&lt;/td&gt;
 &lt;td&gt;a group of members&lt;/td&gt;
 &lt;td&gt;a role in one team&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Given by&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;manage: members&lt;/code&gt; or &lt;code&gt;manage: roles&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;manage: teams&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;manage: teams&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Gives access to&lt;/td&gt;
 &lt;td&gt;everything the role is checked for&lt;/td&gt;
 &lt;td&gt;the roles it carries&lt;/td&gt;
 &lt;td&gt;only what checks that team&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A team role never gives an organization or a realm role, and an organization role never gives a team role.&lt;/p&gt;</description></item><item><title>What happens when…</title><link>https://fernandezvara.github.io/backd/docs/organizations/what-happens-when/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/organizations/what-happens-when/</guid><description>&lt;h2 id="what-happens-when"&gt;What happens when…&lt;a class="hd-anchor" href="#what-happens-when" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Event&lt;/th&gt;
 &lt;th&gt;Access&lt;/th&gt;
 &lt;th&gt;Documents and data&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;A member leaves or is removed&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Ends at the next request. They leave their teams&lt;/td&gt;
 &lt;td&gt;Documents they created stay with the organization. The last manager can&amp;rsquo;t leave: &lt;code&gt;409 last_org_manager&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;A member is erased&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Memberships, team memberships and bound invitations are removed&lt;/td&gt;
 &lt;td&gt;Documents stay, owner cleared (or erased, with &lt;code&gt;in_org: apply&lt;/code&gt;). An organization left without a manager becomes &lt;code&gt;orphaned&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;The last manager is erased&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;The organization has nobody to manage it&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;orphaned&lt;/code&gt;: a realm administrator assigns an owner (&lt;code&gt;backd orgs assign-owner&lt;/code&gt;). The startup log and the erase report name it&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;An organization is deleted&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Ends at once for everybody; the slug is free&lt;/td&gt;
 &lt;td&gt;Each collection&amp;rsquo;s &lt;code&gt;on_org_delete&lt;/code&gt; applies in a resumable job: &lt;code&gt;delete&lt;/code&gt; removes the documents and queues their files, &lt;code&gt;keep&lt;/code&gt; leaves them readable only by administrators&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;A document is transferred&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;The new organization&amp;rsquo;s members see it, the old one&amp;rsquo;s don&amp;rsquo;t, at once&lt;/td&gt;
 &lt;td&gt;Only realm administrators transfer (&lt;a href="../../api/documents/#fixing-data-as-an-administrator"&gt;&lt;code&gt;_transfer&lt;/code&gt;&lt;/a&gt;); its &lt;code&gt;_meta.org&lt;/code&gt; is the only change&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;&lt;code&gt;require_mfa&lt;/code&gt; is turned on&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Sessions without a second factor stop counting as members there; its routes answer &lt;code&gt;403 org_requires_mfa&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Nothing changes. The manager&amp;rsquo;s own session needs a second factor, or they would lock themselves out&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;A role is removed from a team&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Members lose it at the next request, unless they hold it directly or through another team&lt;/td&gt;
 &lt;td&gt;The members list shows what remains. A team can&amp;rsquo;t lose its last managing role if that leaves nobody who can manage&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;A role is no longer declared in &lt;code&gt;realm.yaml&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;The stored grants stay, and grant nothing; startup logs them&lt;/td&gt;
 &lt;td&gt;Declare the role again to restore it, or give another one&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;A member&amp;rsquo;s roles change&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;At the next request: there is no cache&lt;/td&gt;
 &lt;td&gt;Nothing&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;An invitation is revoked or its link reissued&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;The old token is invalid at once&lt;/td&gt;
 &lt;td&gt;People who already accepted keep their membership&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;See also &lt;a href="../#direct-roles-or-teams"&gt;teams or direct roles&lt;/a&gt; for why the same role should be given one way.&lt;/p&gt;</description></item><item><title>Membership hooks</title><link>https://fernandezvara.github.io/backd/docs/organizations/membership-hooks/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/organizations/membership-hooks/</guid><description>&lt;p&gt;Apps often need to react when someone joins a company: create a profile, add them to a channel, send a welcome. Polling for new members is slow and misses changes; &lt;strong&gt;membership hooks&lt;/strong&gt; call an &lt;a href="../../functions/internal/"&gt;internal function&lt;/a&gt; for you, in the background, when it happens.&lt;/p&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="c"&gt;# realm.yaml&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;organizations&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;hooks&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;on_member_joined&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core/members-joined &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# &amp;lt;database&amp;gt;/&amp;lt;function&amp;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;on_member_left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core/members-left&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;on_roles_changed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;core/roles-changed&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;Every hook is optional, and each names a function that is &lt;strong&gt;&lt;code&gt;internal: true&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;mode: async&lt;/code&gt;&lt;/strong&gt;; startup refuses anything else. They are run through the job queue like &lt;a href="../../auth/providers/#the-profile-and-the-sign-up-hook"&gt;&lt;code&gt;account.on_signup&lt;/code&gt;&lt;/a&gt;: there is &lt;strong&gt;no user in &lt;code&gt;ctx&lt;/code&gt;&lt;/strong&gt;, the function&amp;rsquo;s &lt;code&gt;retry:&lt;/code&gt; applies, and a failure never undoes the change that caused it.&lt;/p&gt;</description></item><item><title>Company context</title><link>https://fernandezvara.github.io/backd/docs/organizations/company-context/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/organizations/company-context/</guid><description>&lt;p&gt;backd&amp;rsquo;s own emails (verification, password reset, magic links, MFA and consent notices, organization invitations) and its &lt;a href="../../functions/email/#links-and-pages"&gt;hosted pages&lt;/a&gt; are realm-wide. An app that serves many companies wants them to look like each company&amp;rsquo;s. Two things give them &lt;strong&gt;company context&lt;/strong&gt;: the request says which organization the person comes from, and an internal function supplies the data, so backd never reads your collections by configuration.&lt;/p&gt;
&lt;h2 id="say-where-the-person-comes-from"&gt;Say where the person comes from&lt;a class="hd-anchor" href="#say-where-the-person-comes-from" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Sign-up, sign-in, &lt;code&gt;POST /_auth/magic-link&lt;/code&gt; and &lt;code&gt;POST /_auth/reset-password/request&lt;/code&gt; take an optional &lt;code&gt;org&lt;/code&gt;:&lt;/p&gt;</description></item><item><title>Company email templates</title><link>https://fernandezvara.github.io/backd/docs/organizations/email-templates/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/organizations/email-templates/</guid><description>&lt;p&gt;With &lt;a href="../company-context/"&gt;company context&lt;/a&gt; an email can look like the company&amp;rsquo;s. &lt;strong&gt;Company templates&lt;/strong&gt; go one step further: the owners of an organization write the text themselves, for the kinds of email you allow, and backd keeps your own files as the fallback.&lt;/p&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="c"&gt;# realm.yaml&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;email&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;customizable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;org-invitation, magic-link, reset-password, verify-email, absence-requested]&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;overrides&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;permission: manage_org # who edits: holders of manage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;org (the default)&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;Every listed kind must have a template in the realm&amp;rsquo;s &lt;a href="../../functions/email/#templates"&gt;&lt;code&gt;email/&lt;/code&gt; folder&lt;/a&gt;, which is what a company&amp;rsquo;s version replaces; startup checks it, and that the realm has organizations. &lt;code&gt;permission&lt;/code&gt; is &lt;code&gt;manage_org&lt;/code&gt;, &lt;code&gt;manage_members&lt;/code&gt;, &lt;code&gt;manage_roles&lt;/code&gt; or &lt;code&gt;manage_teams&lt;/code&gt;: the &lt;a href="../../configuration/realm/#organizations"&gt;capability&lt;/a&gt; that edits them.&lt;/p&gt;</description></item><item><title>Team access recipes</title><link>https://fernandezvara.github.io/backd/docs/organizations/team-access-recipes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/organizations/team-access-recipes/</guid><description>&lt;p&gt;Recipes for &lt;a href="../team-roles/"&gt;team roles&lt;/a&gt; and team-scoped documents. Each one is small and says what it relies on; the &lt;a href="../hr-absences/"&gt;HR absences&lt;/a&gt; page puts them together in a tested example.&lt;/p&gt;
&lt;h2 id="concepts"&gt;Concepts&lt;a class="hd-anchor" href="#concepts" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;What it is&lt;/th&gt;
 &lt;th&gt;Rules ask&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Organization role&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;what a person is in the company (&lt;code&gt;owner&lt;/code&gt;, &lt;code&gt;hr&lt;/code&gt;, &lt;code&gt;member&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;hasOrgRole(document._meta.org, &amp;quot;hr&amp;quot;)&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Team&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;a group of members. As a &lt;em&gt;role group&lt;/em&gt; it carries organization roles to everyone in it; as a &lt;em&gt;document owner&lt;/em&gt; it is where documents live (&lt;code&gt;_meta.team&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;inTeam(document._meta.team)&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Team role&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;what a person is &lt;em&gt;within one team&lt;/em&gt; (&lt;code&gt;manager&lt;/code&gt; of Sales)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;hasTeamRole(document._meta.team, &amp;quot;manager&amp;quot;)&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A team can be both.&lt;/strong&gt; The roles a team &lt;em&gt;carries&lt;/em&gt; (&lt;a href="../../api/organizations/#teams"&gt;teams&lt;/a&gt;) are organization roles, given to everyone in it. Team roles are separate: held by a person in one team, giving nothing outside it. A team that carries roles and a person who holds a team role in it don&amp;rsquo;t interact.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;inTeam&lt;/code&gt; vs &lt;code&gt;hasTeamRole&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;inTeam&lt;/code&gt; is &amp;ldquo;they belong to the team&amp;rdquo;: everyone in Sales. &lt;code&gt;hasTeamRole(…, &amp;quot;manager&amp;quot;)&lt;/code&gt; is &amp;ldquo;they hold that role there, now&amp;rdquo;: the manager, whether or not they belong. Combine them with &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt;: &lt;code&gt;inTeam(document._meta.team) || hasTeamRole(document._meta.team, &amp;quot;manager&amp;quot;)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A role without membership.&lt;/strong&gt; A team role is held by a member of the &lt;em&gt;organization&lt;/em&gt;, not necessarily of the team: a delegate from Support approves Sales&amp;rsquo; requests without seeing Sales&amp;rsquo; calendar or having its roles.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="departments-with-managers"&gt;Departments with managers&lt;a class="hd-anchor" href="#departments-with-managers" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Documents carry the department (&lt;code&gt;team: required&lt;/code&gt;), employees create in their own, managers read and decide for theirs:&lt;/p&gt;</description></item><item><title>Example: HR absences</title><link>https://fernandezvara.github.io/backd/docs/organizations/hr-absences/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fernandezvara.github.io/backd/docs/organizations/hr-absences/</guid><description>&lt;p&gt;An HR module for a B2B app: each company is an organization, each department a team, and people ask for absences that their manager (or someone covering) approves. The whole realm is &lt;code&gt;examples/config/hr&lt;/code&gt;, and its rules are tested with &lt;a href="../../auth/rules/#testing-rules"&gt;&lt;code&gt;backd rules test&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="who-is-who"&gt;Who is who&lt;a class="hd-anchor" href="#who-is-who" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Who&lt;/th&gt;
 &lt;th&gt;Is&lt;/th&gt;
 &lt;th&gt;Can&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Ana&lt;/td&gt;
 &lt;td&gt;member of Sales&lt;/td&gt;
 &lt;td&gt;ask for an absence; edit or delete it while it is requested&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Marta&lt;/td&gt;
 &lt;td&gt;member of Sales, &lt;code&gt;manager&lt;/code&gt; there&lt;/td&gt;
 &lt;td&gt;see and decide Sales&amp;rsquo; absences&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Dario&lt;/td&gt;
 &lt;td&gt;member of Support, &lt;code&gt;delegate&lt;/code&gt; in Sales for two weeks&lt;/td&gt;
 &lt;td&gt;see and decide Sales&amp;rsquo; absences in that period&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Hugo&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;hr&lt;/code&gt; in the company&lt;/td&gt;
 &lt;td&gt;see every absence of the company&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="the-realm"&gt;The realm&lt;a class="hd-anchor" href="#the-realm" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p class="hd-example-path"&gt;&lt;code&gt;examples/config/hr/realm.yaml&lt;/code&gt;&lt;/p&gt;</description></item></channel></rss>