Skip to main content
By the end of this page your team has named users with the right workspace roles, someone other than you can administer the server, and — if you run an identity provider — sign-in to the served web app goes through it. The first account comes from the claim, not from this page: see First run.

Two ways to administer

From the admin console, while the server runs. Sign in at the server’s URL and open Settings → Backends → Open admin console. Users, passwords, workspace grants, the server-admin role, paired devices and audit reports are all there, and nothing has to be restarted. This is the normal path — adding a colleague costs no downtime. An invite is one act: a name, an email, a workspace with a role, and — on a server whose login is the password — an initial password. Then tell the person three things: the server address, the email, the password. No invite mail is sent; there is no email plane. The email is required for a user because it is what every sign-in joins on — a user admitted without one cannot sign in by any route until Set email on their row repairs it. Service accounts never have one. That is the whole invite. The person signs in from their own client — the extension or the desktop app’s Sign in to a server…, or oh login — in the browser, on the server’s consent page, with those credentials or with the identity provider, and their device is bound to them; nobody mints a code or a token for them. Each device they sign in is listed under Paired devices as a session of theirs. See how a person signs in from a client. From the CLI, with the daemon stopped. storage.json is single-writer, so the offline verbs refuse while the daemon runs. They exist for scripting and for recovery — including the one case the console cannot cover, a server with no administrator left.
Deactivation is permanent for that record — reactivation is not supported; add the user anew.

Two kinds of role

They are deliberately independent. Administering the server does not imply seeing every workspace. A server admin with no grants can add users and mint tokens and still read nothing — which is the posture an audit reviewer expects, and it keeps workspace grants the single answer to “who may read this”.
The server refuses to remove the last server admin:
Deactivating that user is not blocked, though — if a server ends up with nobody who can administer it, ohd user set-admin <id-or-email> with the daemon stopped is the way back in.

Local passwords

Set from the console, or offline:
For scripts, supply the password via OH_DAEMON_USER_PASSWORD or OH_DAEMON_USER_PASSWORD_FILE — never a flag. A password is what lets a person sign in through a browser. Clearing the password of the only holder on a server with no identity provider leaves nothing a browser can sign in with, and the served web app says exactly that rather than pretending to be a door.

Seats & licensing

The free tier includes 6 seats — active users in the directory. Paid plans only add seats above that; the software is otherwise identical. At the seat limit, adding a user refuses; an individual-seat key matching the user’s email admits past it:
Team licenses install as a file:
Removing a license never touches existing users or data — past-grace expiry only stops NEW growth beyond the free tier.

SSO login (OIDC)

Team deployments can let users sign in through an OpenID Connect provider — in the served web app and on the page every native client sends a person to. Configure the provider in daemon.json:
Register <redirectOrigin>/auth/oidc/callback as the client’s redirect URI with the provider. For confidential clients, put the client secret in daemon.json as oidc.clientSecret or (better) in the service environment as OH_DAEMON_OIDC_CLIENT_SECRET; public clients need no secret — the flow always runs PKCE. redirectOrigin may be omitted for single-hostname deployments; the daemon then derives it from the request. A successful login maps the provider’s verified email onto a daemon user and mints a session token bound to that user, expiring after the server-wide sessionTtlDays (a top-level daemon.json key, default 30 — the one policy every session on the server follows, whether it began in a browser, at the claim, or on a device). Unknown emails are refused unless autoProvision is true, which creates the user with zero workspace grants — grant access from the console or with ohd user grant.
A server with an identity provider is never “unclaimed” and never shows the setup card: configuring an IdP is itself an act of administration performed on the box, so there is no second ceremony. That also means no login mints the first administrator automatically. Grant it once, offline, to a user who already exists in the directory — one ohd user add put there, or one a first SSO login auto-provisioned:
Every administrator after that is a toggle in the console.
A sign-in from a native client rides the same provider: the consent page offers the provider’s button, the round-trip comes back to the consent card, and the approval lands where the client waits — its registered return address on the code grant, the device’s poll on the device grant — bound to the user the provider resolves. Pairing and operator-minted tokens keep working unchanged; SSO is additive.