Registration and Onboarding Defaults
Table of Contents
- 1. Summary
- 2. The three questions a registration answers
- 3. The policy the door reads
- 4. The default tenant
- 5. The default party
- 6. The default role
- 7. The waiting state
- 8. The two flags, and the write path they lack
- 9. Where the defaults come from
- 10. The registration flow end to end
- 11. Refusal codes
- 12. Who may set what
- 13. Rejected alternatives
- 14. What this changes
- 15. Out of scope
- 16. See also
1. Summary
Self-registration creates an account; it does not, by itself, give a person a place to work. This page settles the whole answer: the tenant a registration lands in, the party a new account joins and the role it receives, the state an account holds while an administrator has not finished setting it up, and who is allowed to change each of those. The rule the rest of the page follows is that a default lives as a flag on the row it points at, because row-level security decides which actor may write that row, and a default nobody can set is not a default. The interactions that carry it out are the entry journeys, and the survey this page answers is analyse the sign-up process.
2. The three questions a registration answers
A registration is only useful once three questions have an answer. The journey Sign up states the problem exactly: registration creates an account, sign-in refuses an account with no party, and the honest end of the journey is therefore a confirmation and not a workspace. The three questions are:
- Which tenant does the account belong to? An account row carries a
tenant_idand cannot be created without one. - Which party does the account work in? Sign-in refuses an account with no association, so this is what makes the difference between an account that works and an account that waits.
- Which role does the account hold? The baseline access the account starts from, granted at creation rather than by a later administrator.
Each answer is a policy, and each policy has a different owner. That ownership is the reason the storage shape below is what it is.
3. The policy the door reads
The door asks one question before it offers a form, and that question has one
answer. A new unauthenticated read — iam.v1.ops.registration_policy — returns
the whole policy for the tenant at the address:
| Field | Meaning | Source |
|---|---|---|
signups_enabled |
Whether the deployment accepts registrations at all | The setting system.user_signups, read at system scope |
authorization_required |
Whether a registration also waits on an approval step | The setting system.signup_requires_authorization |
tenant |
The tenant a registration lands in, when one is known | The address, else the tenant flagged is_registration_default |
party |
The party a new account joins, when the tenant nominates one | The party flagged is_registration_default |
role |
The role a new account receives | The role flagged is_registration_default |
usable_now |
Whether a registration can sign in without an administrator | Derived: tenant, party and role are all present |
The password rules stay where they are: ores.iam.login_messages already
answers iam.v1.ops.get_password_policy, and the sign-up screen reads it the same
way the sign-in screen does.
3.1. The scope of the switch
system.user_signups is the deployment's answer, not a tenant's. It is seeded
once on the system tenant, and its registered default is false, so a
deployment nobody has asked refuses registration. The settings are scoped, so a
tenant override beside it is possible, but nothing sets one today and the door
must not confuse the two. What the door reports is the deployment's switch; a
tenant that wants its own answer is a later change with its own rule for which
of the two wins.
The read is separate from iam.v1.ops.bootstrap_status rather than folded into it.
The bootstrap read answers whether the deployment has been set up; this one
answers what the deployment offers a person who is not in it yet. They change
for different reasons, and a client that wants one rarely wants the other.
3.2. Resolving the tenant
The tenant is resolved from the address before anything else happens, which is the rule Multi-Tenancy Architecture already states for a pre-authentication request: parse the hostname, look the tenant up by it, and use the handler's own tenant only when there is none. Registration adds one step after that:
- The address names a tenant — register there. This is the multi-tenant case and needs no configuration at all.
- The address names none — use the tenant flagged
is_registration_default. - Neither — registration is closed, and the policy read says so with a reason. It is never placed in the system tenant by omission, which is what happens today and what the survey found.
Rule 3 is what keeps the single-tenant case honest: a deployment serving one tenant resolves it from the address, and a deployment serving many at one address needs the flag.
3.3. The person types a username, not a tenant
The tenant is not a field on either Entry screen. The BFF forwards the hostname
the request arrived at, and the service resolves the tenant from it. The login
path takes the same treatment: the sign-in journey already says the tenant is
not typed, because the address states it, so both journeys resolve it the same
way and the ordinary door stops requiring user@hostname.
4. The default tenant
| Where | A flag, is_registration_default, on ores.iam.tenant |
| Who sets it | A super administrator |
| How many | At most one, enforced by a partial unique index over the live rows |
| When it is used | Only when the address names no tenant |
The flag sits on the tenant row because the tenant row is the referent: the
flag cannot point at a tenant that does not exist, on a table whose write policy
admits exactly one actor. The write policy on ores_iam_tenants_tbl is
tenant_id = current_tenant_id, and every tenant row carries the system tenant,
so only a system-tenant session may write one. That is the super administrator,
and it is why this flag is theirs and the other two are not.
A deployment that nominates no default and serves one tenant still works, because the address resolves it. A deployment that nominates none and serves many at one address refuses registration with a stated reason rather than guessing.
5. The default party
| Where | A flag, is_registration_default, on ores.refdata.party |
| Who sets it | A tenant administrator |
| How many | At most one per tenant, enforced by a partial unique index over the live rows |
| When it is used | At registration, to give the new account its first association |
The flag sits on the party row because that is the row the tenant administrator
may write. ores_refdata_parties_tbl is tenant-isolated, so a tenant
administrator writes their own parties. The alternative — a default_party_id
column on the tenant row — was rejected on exactly this ground: the tenant row's
write policy admits only the system tenant, so the tenant administrator could not
set their own default. See Rejected alternatives.
When the flag is set, registration creates the association and the account is usable at once. When it is not, the account waits in the state below.
6. The default role
| Where | A flag, is_registration_default, on ores.iam.role |
| Who sets it | A tenant administrator |
| How many | At most one per tenant, enforced by a partial unique index over the live rows |
| When it is used | At registration, to grant the account's baseline access |
The role is per tenant and the tenant administrator owns the tenant's roles, so
the flag sits on the role row for the same reason the party flag sits on the
party row. The flag replaces the hard-coded name the registration path uses
today: account_setup_service assigns domain::roles::viewer by name and throws
when it is missing. A flag on the row removes both the hard-coding and the
failure mode, because a flag cannot misspell a role that does not exist.
A tenant that nominates no role is told so by the policy read, and registration is closed with that reason rather than creating an account that holds nothing.
A default role is a floor and not a ceiling. A person who needs more than it grants asks for it, and that request is its own journey rather than part of the door: it happens after registration, it may be refused, and somebody else answers it. That journey belongs to the Access group and is documented there, not here.
7. The waiting state
An account that exists but has not been given a place to work is pending. This
is a second value beside active on a new status lookup,
ores_iam_account_statuses_tbl, following ores.iam.account's existing
account_type pattern: a code column validated against a lookup, with the two
values active and pending seeded.
| Event | Status |
|---|---|
| Registration in a tenant that nominates a default party and role | active, with the association and the role written |
| Registration in a tenant that nominates neither or only some | pending, with the role written when one is nominated and no association |
| An administrator gives a pending account its first party | active |
One column, one transition, and the transition belongs to the administrator's act rather than to a timer. The rule the roster and the sign-in path both read is therefore: pending means an administrator has not finished setting this account up.
7.1. What sign-in does with it
iam.v1.ops.login refuses a pending account before it refuses a missing party,
and with its own code and its own words: Your account is waiting for an
administrator to finish setting it up. The person learns what they are waiting
for instead of being told to contact an administrator about a party they have
never heard of. The refusal is a code, not a matched sentence — see the refusal
codes below.
7.2. What the roster does with it
The account roster reads the status and shows a pending account as such, with the action that releases it. That is the administrator's side of the journey and it belongs to Directory journeys, which this page states as a requirement rather than building.
8. The two flags, and the write path they lack
The two booleans stay where they are. system.user_signups and
system.signup_requires_authorization are values with a scope and no referent,
which is what ores.variability.system_setting is for, and they are
already seeded and already read by the typed settings service.
What is missing is not storage but a way to write one. ores.variability.operations
carries two subjects, clear_bootstrap_mode and complete_party_onboarding, and
neither reads nor writes a named setting. So the super administrator's kill
switch needs a settings read/write pair, and that pair is the first general
write path the settings have ever had:
- A read that returns the settings in scope, so a screen can show the current state rather than assume it.
- A write that sets one setting by name, permission-gated, with the writer and the change reason recorded as every other write records them.
Until that pair exists, the switch can only be set by seeding it: there is no
screen and no subject that writes a named setting. What the door does with the
value is settled — the live path reads it through
iam.v1.ops.registration_policy and refuses the registration it should never
have accepted — so the missing write is a convenience for an operator rather
than the difference between a closed door and an open one.
8.1. The authorisation flag
system.signup_requires_authorization must not claim an approval nothing
performs. The live path reads it now, and the behaviour is the honest one: a
deployment that sets it closes registration, the policy read reports
authorization_required, and the door states why rather than accepting a form it
would refuse. The approval workflow itself is out of scope here and belongs to
ores.workflow.
9. Where the defaults come from
A fresh installation has none of these values, and the door cannot invent them. First run is where they are set, because it is the only journey that runs on an empty installation and it already creates the things a default points at.
- The default tenant is the first tenant. At the end of its provisioning the system administrator confirms it as the tenant registrations land in. The flag is the super administrator's to set, and the first tenant is the only sensible answer while the installation holds one.
- The switch is answered in the same pass. The system administrator states whether the installation accepts self-registration, and the door reports that answer from then on. A deployment that refuses needs nothing else. One that accepts says so deliberately rather than by omission, which is the difference between a door that is closed and a door nobody has looked at.
- The default party and the default role are the tenant administrator's, and the journey's first sign-in is where they name them, beside the party they choose to work as. An administrator who never names them still leaves a deployment that accepts registrations; the accounts it creates wait in the pending state rather than failing at sign-in.
New tenant and New party ask the same questions when they create a tenant or a party later, because an installation that grows past its first tenant still has to say which tenant is the default and where a new account joins.
This is why the switch and the defaults belong to the setup journeys rather than to the sign-up screen: the person who opens the door is not the person who decides what it opens onto.
10. The registration flow end to end
- The door asks
iam.v1.ops.registration_policy, unauthenticated, with the address it was served at. - The screen renders the form when the policy allows registration and the destination is known, and the closed notice with its reason when it does not.
- The form takes a username, an email address, a password and its confirmation,
and states the password rules from
iam.v1.ops.get_password_policy. - The BFF posts to its registration route, under the same limiter family the sign-in route uses, and forwards the hostname.
- The service resolves the tenant, reads both flags, validates the username, the email and the password, creates the account with the status the defaults allow, writes its login record, and grants the nominated role.
- The response carries the account, its status, the role and party it received, and an error code when the registration was refused.
- The confirmation states what the account received and what it still waits for, then returns the person to the door. It does not sign them in: a pending account cannot open a session, and an active one should arrive at the door deliberately.
11. Refusal codes
Both Entry subjects drop a fact today: a refusal carries a sentence and no code,
so a client distinguishes a locked account from a wrong password by matching
English prose. Both responses gain an error_code, drawn from the existing
ores::utility::serialization::error_code vocabulary where it already fits:
| Subject | Codes |
|---|---|
signup_response |
signup_disabled, signup_requires_authorization, no_registration_destination, no_default_role, username_taken, email_taken, weak_password, invalid_request |
login_response |
invalid_credentials, account_locked, account_pending, no_party_assignment, tenant_inactive, bootstrap_mode |
The message stays for a human and may change; the code is what a screen branches on, which is what makes each branch a screen state rather than a guess.
12. Who may set what
| Actor | May set | Because |
|---|---|---|
| Super administrator | The default tenant flag; system.user_signups through the new settings write |
The tenant table and the system-scope settings are theirs to write |
| Tenant administrator | The default party flag; the default role flag | The party and role tables are tenant-isolated |
No actor sets another tenant's defaults. A tenant administrator cannot nominate the deployment's default tenant, which is why that flag is not theirs; a super administrator can, which does not make the other two theirs.
13. Rejected alternatives
- Every default in the variability settings. A setting holds a value, and a
default party or role is a reference. Stored as a string it can name a role
that was retired or misspell one, and the registration path finds out by
failing — which is the failure mode the current hard-coded
Vieweralready has. Booleans stay settings because they have no referent; references become flags on the row they point at. default_party_idanddefault_role_idon the tenant row. Decisive against it: the write policy onores_iam_tenants_tblistenant_id = current_tenant_id, and tenant rows carry the system tenant, so only a super administrator may write a tenant row. The tenant administrator's own choice would be unreachable to them.- A flag with no uniqueness rule. Two parties flagged as the default is a
question with no answer. Each flag carries a partial unique index over the live
rows, scoped by tenant where the table is, in the shape
ores.refdata.partyalready uses forroot_party_uniqandsystem_party_uniq. - Confirmation only, with no waiting state. The confirmation could simply tell the person to ask an administrator. It was rejected because it leaves the state implicit: the roster cannot find the people who are waiting, and the sign-in refusal cannot say what is actually wrong.
14. What this changes
| Layer | Change |
|---|---|
| Models | ores.iam.tenant, ores.refdata.party and ores.iam.role gain is_registration_default; a new ores.iam.account_status lookup; ores.iam.account gains account_status; ores.iam.signup_messages gains a hostname, a status, the granted party and role, and error_code; ores.iam.login_messages gains error_code |
| SQL | The columns, the lookup and its seeds, the partial unique indexes, and the history and validation the entity pattern requires |
| Services | Registration reads the flags and the defaults and writes the status; login sets a code on every refusal and refuses a pending account before a party-less one |
| Variability | A settings read and a settings write, permission-gated |
| BFF | A registration-policy route, a registration route under the limiter, and the hostname forwarded |
| Wire protocol | The browser-safe exports and the operation entries the two subjects need |
| Clients | The sign-up screen and the sign-in screen changes, on the same subjects from the shell. Every dialog carries the ORE Studio banner, the sign-in screen included |
| Journeys | First run gains the registration step and nominates the tenant administrator's defaults; New tenant and New party ask the same questions when they create a tenant or a party |
15. Out of scope
- The approval workflow behind
system.signup_requires_authorization, which is a workflow inores.workflow. - Email verification, invitation delivery and the notification that tells an administrator somebody is waiting.
- Self-service password recovery from the door, which needs a delivery mechanism outside IAM.
- The Directory journeys that release a pending account and show it on the roster. This page states the requirement; Directory journeys owns the screen.
16. See also
- Analyse the sign-up process end to end — the survey this page answers, and the evidence for the defects it corrects.
- Sign up and Sign in — the journeys that read this policy.
- The entry journeys — the same policy carried out: the layers, the order a sign-in is refused in, the refusal codes, and a sequence diagram per interaction.
- Entry journeys — the group the door belongs to.
- Multi-Tenancy Architecture — how the address resolves to a tenant.
- Multi-Party Login Flow — why an account with no party is refused.
- Role-Based Access Control — what the default role grants.
- Identity and Access — the cluster this page belongs to.