Skip to content

Registration Modes ​

Surge supports three registration modes that control how new user accounts can be created.

Overview ​

Surge supports three registration modes that control how new identities are created. The mode is set via the SURGE_REGISTRATION environment variable and applies globally to all browser and service endpoints.

bash
export SURGE_REGISTRATION=open    # default
export SURGE_REGISTRATION=invite
export SURGE_REGISTRATION=closed

The mode parsed at startup:

rust
match value.as_str() {
    "invite" => RegistrationMode::Invite,
    "closed" => RegistrationMode::Closed,
    _        => RegistrationMode::Open,
}

Any value other than "invite" or "closed" — including unset, typos, and empty strings — defaults to Open.

Open ​

Anyone can register through the login flow without any prior authorization.

When to use: public-facing applications, consumer SaaS, or any scenario where self-service sign-up is desired.

Browser flow ​

The GET /v1/login response (both redirect and inline modes) includes "registration_mode": "open". The frontend should render a registration option alongside the login form.

json
{
  "flow_id": "aeg_f_3k7m9p2q5r8t1v4w6x",
  "csrf_token": "aeg_csrf_...",
  "registration_mode": "open"
}

Registration within a flow:

bash
curl -X POST http://localhost:3000/v1/flows/aeg_f_.../register \
  -H "Content-Type: application/json" \
  -d '{"username": "bob", "password": "correct-horse-battery-staple", "display_name": "Bob", "csrf_token": "aeg_csrf_..."}'

On success, an identity is created and a session is issued immediately.

Service API ​

Services with direct_auth grant can create identities directly via the service API regardless of registration mode, but in open mode the browser flow also works.

Invite ​

Not yet implemented. Setting SURGE_REGISTRATION=invite is accepted at startup, but the browser registration endpoint currently rejects it: attempting to register returns an error stating invite-based registration is not yet implemented. There is no invite-code creation surface (CLI or API) yet.

If you need controlled sign-up today, use Closed mode and provision identities through the service API.

Closed ​

Registration is disabled entirely through the browser login flow. Identities are created only via the service API (direct_auth or identity_write grants).

When to use: enterprise deployments where users are provisioned by an external system (HRIS, IdP, admin tooling), or when all user creation is done out-of-band.

Browser flow ​

When SURGE_REGISTRATION=closed, the GET /v1/login response returns "registration_mode": "closed". The frontend should render only a login form — no registration option, no invite-code prompt.

json
{
  "flow_id": "aeg_f_...",
  "csrf_token": "aeg_csrf_...",
  "registration_mode": "closed"
}

Attempting to call POST /v1/flows/{id}/register when registration is closed returns:

HTTP/1.1 403 Forbidden

{
  "error": "registration_disabled",
  "message": "Registration is currently disabled."
}

Service API ​

The service API remains the only path for identity creation:

bash
curl -X POST http://localhost:3000/v1/register \
  -H "Authorization: Bearer aeg_svc_..." \
  -H "Content-Type: application/json" \
  -d '{"username": "bob", "password": "correct-horse-battery-staple", "display_name": "Bob"}'

Migration between modes ​

Switching registration modes is safe and immediate — it doesn't affect existing identities or sessions. The change only impacts how new identities are created:

bash
# Switch from open to invite — existing users are unaffected
export SURGE_REGISTRATION=invite

# Switch from closed to open — new registrations are immediately available
export SURGE_REGISTRATION=open

After restarting (or reloading config), the new mode takes effect. No database migration is needed.

Note: invite codes aren't creatable yet (see Invite), so there's nothing to migrate around for invite mode today.

How modes affect the login flow UI ​

The flow-init response always includes registration_mode, which tells the frontend what to display:

ModeFlow init valueFrontend shows
open"registration_mode": "open"Login form + "Sign up" link
invite"registration_mode": "invite"Login form + gated "Sign up (invite required)" — mode is accepted, but registration isn't yet functional (see Invite)
closed"registration_mode": "closed"Login form only

The frontend is responsible for reading this value and rendering the appropriate UI. Surge doesn't serve UI — it only exposes the data that tells the UI what to render.