View Markdown ↗

Authentication, users, and access#

The application separates three concerns:

  1. Platform identity: who may open remote.futrx.
  2. Agent credentials: whether Claude, Codex, or Kimi can run.
  3. Project membership: which registered users may access a project.

Application gate#

stateDiagram-v2 [*] --> CheckServerClaim CheckServerClaim --> ClaimAdmin: server is unclaimed CheckServerClaim --> SignIn: server is claimed ClaimAdmin --> CheckLocalAdmin: admin creates email and password SignIn --> CheckLocalAdmin: valid local or Google session CheckLocalAdmin --> WaitForAdmin: legacy admin password setup is incomplete CheckLocalAdmin --> CheckProvider: local admin is configured CheckProvider --> ConnectProvider: no agent provider is authenticated and caller is admin CheckProvider --> WaitForProvider: no provider is authenticated and caller is member ConnectProvider --> Workspace: any provider becomes authenticated WaitForProvider --> Workspace: admin connects a provider CheckProvider --> Workspace: provider already authenticated

The backend middleware applies the same order to /api/* and /ws*: valid registered session, completed local-admin setup, then at least one authenticated agent provider. Provider-login endpoints are exempt from the final check so onboarding can finish.

Identities and roles#

IdentitySign-inScope
Local administratorEmail and a password of 12–1024 charactersCannot be removed or demoted; manages host-wide setup
Invited administratorGoogle OAuthAdmin routes and all projects
Invited memberGoogle OAuthOnly projects where their email is a member, plus loose chats

Sessions are signed in the remote_session secure, HTTP-only cookie and last 30 days. Passwords use a salted, one-way hash. Google OAuth is optional until the admin wants to invite users.

First administrator flow#

sequenceDiagram actor Admin participant UI participant Auth as Auth service participant Store as File auth store participant Users as User directory Admin->>UI: Submit email and password UI->>Auth: POST /auth/local/claim Auth->>Store: Confirm server is unclaimed Auth->>Store: Save password hash Auth->>Users: Add bootstrap admin Auth->>Store: Sign session cookie Store-->>UI: Authenticated status

The first claim is public only while no admin exists. A legacy installation that already has an administrator identity but no password requires authorization by that existing admin.

Invited user flow#

flowchart LR OAuth["Admin saves Google client ID and secret"] --> Invite["Admin registers user email and role"] Invite --> Login["User chooses Google sign-in"] Login --> Callback["Google callback returns verified identity"] Callback --> Registered{"Email is registered?"} Registered -->|"No"| Deny["Deny access"] Registered -->|"Yes"| Session["Issue platform session"] Session --> Projects["Show permitted projects and chats"]

User guardrails:

  • Google sign-in must be configured before users can be added.
  • Only admins can add users, remove users, or change roles.
  • The last administrator cannot be removed or demoted.
  • The local administrator cannot be removed or demoted.

Agent-provider authentication#

Provider credentials are host-wide and admin-managed.

flowchart TD Start["Admin opens Agent settings"] --> Provider{"Provider"} Provider -->|"Claude"| ClaudeStart["Start CLI authorization-code flow"] ClaudeStart --> ClaudeURL["Open authorization URL"] ClaudeURL --> ClaudeCode["Paste returned code"] ClaudeCode --> Saved["Credentials detected on host"] Provider -->|"Codex"| CodexDevice["Start device-code login"] Provider -->|"Kimi"| KimiDevice["Start device-code login"] CodexDevice --> Verify["Open verification URL and enter code"] KimiDevice --> Verify Verify --> Saved Saved --> Broadcast["Status WebSocket updates onboarding UI"]

Claude uses an interactive authorization URL plus a pasted code. Codex and Kimi use device-code flows. Credential files are later synchronized into project containers before agent execution.

Project access rules#

flowchart TD Request["Authenticated request for a project resource"] --> Admin{"Caller is admin?"} Admin -->|"Yes"| Allow["Allow"] Admin -->|"No"| Member{"Email is in project access list?"} Member -->|"Yes"| Allow Member -->|"No"| Deny["403 Forbidden"]
OperationAdminProject member
See project and its chatsYesYes
Create chat in projectYesYes
Start, stop, restart, inspect, repairYesYes
Read or change secretsYesYes
Edit project membershipYesYes, but cannot remove the final member
Set resource limitsYesNo
Delete projectYesNo
Manage global users or Google OAuthYesNo
Connect agent providersYesNo

Project access is enforced independently on project/chat HTTP resources, chat sockets, terminal sockets, uploads, workspace snapshots, project skills, and preview requests.

Preview and IDE authentication#

Caddy calls /auth/verify before forwarding IDE or preview traffic. For a preview host, the backend extracts the project slug and checks membership. IDE hosts currently receive the registered-user check but not a per-project membership check. After verification, Caddy strips platform session cookies before the request enters project-controlled code.

sequenceDiagram actor User participant Caddy participant Auth as /auth/verify participant Project as Project app or IDE User->>Caddy: Request project subdomain Caddy->>Auth: Forward-auth with session and original host Auth->>Auth: Validate session, and preview project membership Auth-->>Caddy: 200, redirect, or deny Caddy->>Project: Proxy without platform auth cookies Project-->>User: App or IDE response

Code map#

remote.futrx documentation