Capabilities
Authentication
auth@1capability contract
On this page
Introduction
auth@1 answers one question for your applet: who is this, and what am I allowed to know about them?
Profile attributes are Rack-owned and applet-requested. They are never applet-written, and there is no Rack.auth.set().
There is nothing to call at runtime. The released profile arrives in your applet's launch payload.
Declaring Attributes
To get started, declare the capability and list the profile attributes you would like to receive in the manifest's requires block:
"requires": {
"capabilities": ["auth@1"],
"profile": ["display_name", "business_unit", "locale"]
}Attribute keys must match ^[a-z][a-z0-9_]*$.
Requesting an attribute is not obtaining it. Each Rack decides which attributes it is willing to release to applets, and anything outside that set is withheld silently. A withheld attribute is not an error: your applet simply never receives it, and should be written to cope without it.
display_name is always released. Attributes that don't exist on the Rack's own user accounts, such as a cohort, a business unit or a region, may still reach your applet when the Rack has been given values for them.
Reading the Participant
Once your applet has launched, you may read the participant and their released profile:
const launch = await Rack.init({ slug: 'my-applet' });
launch.actor; // { id, name } — always present, no capability needed
Rack.auth.user(); // { id, display_name, …released attributes } | null
Rack.auth.attribute('business_unit'); // unknown → null
Rack.auth.attribute('locale', 'en'); // unknown → 'en'
Rack.auth.attribute<string[]>('regions', []); // typedBoth are synchronous reads of launch data, so they are free to call in a render path.
actor is the participant the Rack resolved: a signed-in user, or a cookie-keyed guest when the deployment enables guests. Rack.auth.user() is the participant's profile, and it exists only when your manifest declares auth@1.
Degrading Gracefully
An attribute you requested may be absent because the Rack does not release it, because nobody has supplied a value for it, or because this participant is a guest. All three look the same to your applet, and all three must work.
For example, you may phrase a sentence so it reads well with or without the attribute:
const unit = Rack.auth.attribute<string>('business_unit');
const intro = unit
? `Here is what the ${unit} team said last quarter.`
: `Here is what your colleagues said last quarter.`;Design the withheld path first and treat personalisation as an enhancement. An applet that renders Hello, undefined when an attribute is missing has failed a kind of validation the Rack cannot perform for you.
launch.auth.withheld lists the requested keys this Rack never releases. It is useful during development to tell "not released here" apart from "not set for this person", but it says nothing about any one participant.
Roles
Roles answer a different question: what part does this person play in this applet? They need no capability. Every launch carries the participant's roles as a list of names, resolved by the Rack, and you may read them like so:
Rack.auth.roles(); // ['member', 'applet-author', 'judge']
Rack.auth.hasRole('judge'); // true
Rack.auth.isGuest(); // falseRoles are resolved at launch, so a change to someone's roles applies from their next launch.
Roles inform your code; capabilities don't check them. A db@1 collection's permissions and a broadcast's facilitator_events know nothing about roles. Use a role to decide what to render and which actions to offer, and rely on your manifest's scopes and permissions to protect data.
Declared roles are also not a room's facilitator and participant. Those belong to broadcast@1, change during a session, and are enforced by the Rack.
Built-In Roles
Every Rack resolves four built-in roles for every applet:
| Role | Held by |
|---|---|
member |
A signed-in user of the Rack. |
guest |
A participant without an account, where the deployment allows guests. Exactly one of member and guest is present. |
applet-author |
The applet's owner. |
rack-administrator |
An administrator of the Rack. |
Declared Roles
Your applet may add its own roles in the manifest's roles block. Each declared role has a grant, which says who holds it before the Rack assigns anyone:
grant |
Held by |
|---|---|
everyone |
Every participant. |
members |
Every signed-in participant. |
guests |
Every guest. |
assigned |
Nobody, until the Rack assigns it. The default. |
The Rack may then assign declared roles to signed-in participants, per applet: to an individual user, to everyone holding one of the Rack's own roles, to an email address, or to every address at a domain. An assignment adds to the grant. It never takes the role away from someone the grant already gives it to. Guests receive grants only, never assignments.
An assignment to a role the current version no longer declares is kept, but ignored until a version declares that role again.
Trying Roles Locally
In mock mode, the participant is a member holding the manifest's everyone and members grants. You may launch as a guest by adding ?ziprack-guest to your dev server's URL, or hold roles as if the Rack had assigned them with ?ziprack-roles=judge,applet-author:
http://localhost:5173/?ziprack-guest
http://localhost:5173/?ziprack-roles=judge,applet-author