Identity & SSO
Who can sign in, what each role may do, how people are invited, and how an organisation brings its own identity provider.
Opening a workspace is ownership and nothing else: administering the organisation is not access to its people's work. Owners and admins may start, stop, create and remove workspaces, but not open a colleague's shell. Reassigning a workspace is the recorded way to hand it over.
| Action | operator | owner | admin | member | auditor | Shared password |
|---|---|---|---|---|---|---|
| Open your own workspace | ||||||
| Open a colleague's workspace | ||||||
| Read the organisation's pages | ||||||
| Open the Audit and AI usage tabs | ||||||
| Start, stop, create or remove its workspaces | ||||||
| Change organisation settings, SSO included | ||||||
| Invite as member, admin or auditor | ||||||
| Make someone an owner | ||||||
| Remove or demote the last owner | ||||||
| Platform settings, mail, new organisations |
Press a cell for the rule behind it. Refused cells show the reason code the console gives, where it has one. “Their agent” is an agent session started by that person; “shared password” is a session opened with the front-door password.
Two kinds of account
Operators run the host. They sign in at /admin/ with a password, create organisations, grant owners, and hold the platform settings: outbound mail, the AI agents, egress policy, the cluster. The first one is created when you install.
Everyone else belongs to organisations and signs in at /portal/. Inside an organisation each person holds one of four roles. There is no self-service signup: accounts arrive by invitation.
It is not a directory. There is no SAML, no SCIM provisioning, no native MFA and no passkeys.
The four organisation roles
| Role | May | May not |
|---|---|---|
| owner | everything an admin may; is the organisation's accountable holder | create another owner; leave the organisation without an owner |
| admin | invite and re-role members, admins and auditors; change settings and SSO; start, stop, create and remove workspaces; read the Audit and AI usage tabs | open a colleague's workspace; grant owner |
| member | use the workspaces they own; read the organisation's pages; ask for an egress host | administer anything |
| auditor | read the organisation's pages, its Audit tab (a dashboard) and its AI usage tab | change anything, or open a terminal, IDE or preview, even in a workspace they own |
An organisation cannot be deleted yet. Members can be disabled, offboarded and removed; the organisation stays. A plain member is not offered the Audit tab (since orxy V1.7.2.2).
Defaults
| Setting | Scope | Default | Allowed values |
|---|---|---|---|
| Require per-developer identity | platform | off, until an operator turns it on | on, off |
| Invitation lifetime | platform | 7 days | fixed |
| Session lifetime | platform | 24 hours | fixed; revocable on the server |
| Outbound mail driver | platform | none: refuses every send | none, smtp, console (development only) |
| SMTP port and TLS | platform | 587, auto | auto, starttls, implicit, none |
| SSO provider | organisation | none configured | Microsoft Entra ID, Google Workspace, generic OIDC |
| Require SSO | organisation | off | on only with a complete provider and one break-glass member |
| Password sign-in alongside SSO | organisation | on | on, off |
| Require MFA | organisation | off | on: the identity provider must assert it |
Invitations
Open the organisation's members. OrganisationsacmeMembersInvite
Enter an email and pick a role. Admins can invite members, admins and auditors. Only an operator can invite an owner: that is how a new organisation gets its first one.
Deliver the link. With mail configured, it is emailed. Without it, the console shows the accept link once, for you to send by another channel. It lasts 7 days.
The person accepts and sets a password at
/portal/, or signs in with the organisation's SSO. The membership becomes active with the role you picked.
An invitation is itself the approval, so there is no second step. It cannot quietly lower someone who already holds a higher role, and it does not undo a suspension unless an admin re-invites that member on purpose.
Outbound mail
Mail is off until an operator configures it: the default driver refuses every send and says so, rather than dropping messages. Until then there are no emailed invitations, no email verification and no password reset. Configure SMTP in the console, where the password is write-only, or through the serving tier's environment, which overrides it.
ConsoleSettingsOutbound mail
AGENT_STACK_MAIL_DRIVER=smtp
AGENT_STACK_SMTP_HOST=smtp.example.com
AGENT_STACK_SMTP_PORT=587
AGENT_STACK_SMTP_USER=kodela@example.com
AGENT_STACK_SMTP_FROM=kodela@example.com
AGENT_STACK_SMTP_TLS=auto
AGENT_STACK_BASE_URL=https://kodela.example.comKeep AGENT_STACK_SMTP_PASSWORD in the host's secret store, not in a file you commit. AGENT_STACK_BASE_URL must be the exact origin people open: links are never built from the request's Host header, so without it every identity email refuses with no-base-url. Then use Send a test message on the same page; it tells missing settings, connection failure, authentication failure and relay rejection apart. From the host, python3 bin/_mail.py status gives the same check without sending.
No delivery is on record yet. The SMTP driver exists and is tested; a real relay has not yet delivered an invitation on record.
Single sign-on per organisation
Each organisation can bring its own identity provider over OpenID Connect: authorization code with PKCE, a nonce bound to the request, and issuer, audience and age checks on the ID token. It is configured by the organisation's owner or admin, not by the operator.
| Provider | You enter | Checked |
|---|---|---|
| Microsoft Entra ID | directory (tenant) ID, application (client) ID, client secret | the directory is pinned: not common, and the token's tenant must match |
| Google Workspace | client ID, client secret, allowed domains (required) | the hosted-domain claim against the organisation's verified domains |
| Generic OIDC | client ID, client secret, the endpoints by hand | discovery is not fetched; Okta and others work through this path, untested |
Register an application with your provider (Entra admin centre, App registrations; or Google Cloud, Credentials, OAuth client ID) with this redirect URI:
https://kodela.example.com/portal/sso/callbackThe base URL must be your https origin first; providers will not register a plain-http redirect.
Enter it in the organisation's settings. OrganisationsacmeSettingsSingle sign-on The secret is write-only; the page shows whether one is installed, never its value. A provider that is on but has no secret is marked incomplete, and no sign-in button appears.
Verify your email domains. An SSO identity links to an existing account only when the provider marks the email verified and the domain is verified for this organisation. It is never linked by a plain email match, and never to an operator. Otherwise a new identity is created for this organisation, with no role until an owner or admin assigns one.
Test with one person, then decide on Require SSO. Require SSO binds members, not addresses, and needs at least one break-glass member who can still sign in with a password, so a lapsed provider cannot lock the owner out. Require MFA refuses sign-ins whose token does not assert MFA.
Tested only against a stub identity provider. No sign-in against a real Entra ID, Google or Okta tenant is on record. There is no SAML and no SCIM.
Sign-in and sessions
Passwords are stored with PBKDF2-SHA256 at 200,000 iterations, and an unknown account costs the same work as a known one. Repeated failures lock out the source; that lockout is held in memory and clears on restart. A session is an HMAC-signed, HttpOnly, SameSite=Lax cookie that lasts 24 hours, marked Secure on an https origin, and revocable on the server.
Disabling an account does not end its live sessions. Offboarding does: Membersdev@example.testOffboard disables the account, removes its ownership and signs it out.
Require per-developer identity
Out of the box, the shared front-door password is still accepted at the portal. A session opened with it is authenticated but names nobody, and it can reach any workspace. Close it once every developer has their own account, or they will be locked out.
ConsoleGovernanceControlsRequire per-developer identity
After this, the shared password is refused at the portal sign-in. Keep it in door.env: it is still used between the console process and the door.
Verify it works
Sign in as a member and open a colleague's workspace URL. Good:
{"error": "not your workspace"}with a 403.Sign in as an auditor and open their own workspace. Good: refused with
org-read-only.Check mail.
python3 bin/_mail.py status, then Send a test message.Read the posture. The console's Governance page reports whether identity is required, and the compliance report marks identity partial until it is.
$ bin/compliance-report
Troubleshooting
409 an organisation must keep one owner
You are removing or demoting the only owner. Make someone else owner first; that needs an operator.
409 last-break-glass
The organisation requires SSO and this is the last member who can sign in with a password. Exempt someone else first, or turn off Require SSO.
403 organisation admins only
A member tried an admin action. Ask an owner or admin, or have your role changed.
403 an identified session is required
You are signed in with the shared front-door password. Sign in with your own account.
403 not your workspace
Workspaces open only for their owner. An admin can reassign it to you.
Invitation email never arrives
Mail is off by default (not-configured), or the base URL is unset (no-base-url). Copy the accept link from the console, then configure mail.
Test message fails with auth-failed or connect-failed
Wrong relay credentials, or the relay host and port are unreachable from the host. Check the TLS mode against the port.
No SSO button on the sign-in page
The provider is off, or on without a client secret (shown as incomplete).
Sign-in fails over plain http
On an https origin the session cookie is Secure, so a browser will not send it over http. Use the https address.
What it does not do yet
- No SAML, no SCIM. OIDC only, and provisioning is by invitation.
- SSO is unproven against a real provider. Tested against a stub identity provider only.
- No native MFA or passkeys. MFA only as a requirement the identity provider asserts.
- Mail refuses until configured. No delivery through a real relay is on record.
- Shared password on by default. Unattributable until “require identity” is on.
- No organisation deletion. Organisations cannot be removed yet.
- No rate limit on invitations. Nor on workspace creation.
Related
Verified against the main branch, 4 Oct 2026.