Users
Human identities within the SOAT instance, authenticated via username and password.
Overview
Users are global, not project-scoped. The first is created via bootstrap; afterwards only admins may create users. Example: Permissions in Practice — Step 2 (Create regular users).
Attached Policies control access; see IAM.
See the Permissions Reference for the IAM action strings for this module.
Related Tutorials
- Permissions in Practice - Step 2 (Create regular users)
- Chat with an LLM - Step 1 (Log in as admin)
- Agent with Persistent Memory - Step 1 (Log in as admin)
Data Model
| Field | Type | Description |
|---|---|---|
id | string | Public identifier prefixed with user_ |
username | string | Unique login name |
role | string | "admin" or "user" — see Roles |
created_at | string | ISO 8601 creation timestamp |
updated_at | string | ISO 8601 last-updated timestamp |
Sensitive fields (passwordHash, internal numeric ID) are never exposed in responses.
Key Concepts
Roles
| Role | Description |
|---|---|
admin | Full access to all resources and operations. Bypasses policy evaluation. |
user | Access determined by the policies attached to the account. |
Bootstrap
The POST /api/v1/users/bootstrap creates the first admin user, unauthenticated, only while the user table is empty (409 Conflict otherwise). Or bootstrap on startup with:
SOAT_ADMIN_USERNAME=admin
SOAT_ADMIN_PASSWORD=supersecret
With both set and no users, the server creates the admin before accepting requests; otherwise they are ignored.
Authentication
POST /api/v1/users/login with username and password returns a signed JWT (public ID and role), passed as Authorization: Bearer <token>. See IAM — Authentication and Chat with an LLM — Step 1 (Log in as admin).
Policy Attachment
The user-policies endpoint replaces the user's full policy list. User create/delete require the admin role and bypass the policy engine.
Examples
Bootstrap first user
- CLI
- SDK
- curl
soat bootstrap-user --username admin --password supersecret
import { SoatClient } from '@soat/sdk';
const soat = new SoatClient({ baseUrl: 'https://api.example.com' });
const { data, error } = await soat.users.bootstrapUser({
body: { username: 'admin', password: 'supersecret' },
});
if (error) throw new Error(JSON.stringify(error));
curl -X POST https://api.example.com/api/v1/users/bootstrap \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "supersecret"}'
Login
- CLI
- SDK
- curl
soat login-user --username admin --password supersecret
const { data, error } = await soat.users.loginUser({
body: { username: 'admin', password: 'supersecret' },
});
if (error) throw new Error(JSON.stringify(error));
// data.token is the JWT to use in subsequent requests
curl -X POST https://api.example.com/api/v1/users/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "supersecret"}'
Create an additional user
- CLI
- SDK
- curl
soat create-user --username alice --password alicepass
const authedSoat = new SoatClient({ baseUrl: 'https://api.example.com', token: 'sk_...' });
const { data, error } = await authedSoat.users.createUser({
body: { username: 'alice', password: 'alicepass' },
});
if (error) throw new Error(JSON.stringify(error));
curl -X POST https://api.example.com/api/v1/users \
-H "Authorization: Bearer <admin-token>" \
-H "Content-Type: application/json" \
-d '{"username": "alice", "password": "alicepass"}'