Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5.7 KiB
Authentication
GearBox uses a public-read, authenticated-write model. All GET endpoints are publicly accessible with no credentials required. Any request that modifies data (POST, PUT, PATCH, DELETE) requires authentication.
This is a single-user app. There is exactly one admin account.
Table of Contents
- First-Time Setup
- Web UI Authentication
- API Keys
- Auth Middleware Behavior
- Auth API Reference
- Frontend Behavior
First-Time Setup
When no users exist, all write endpoints return 403 with { "error": "setup_required" }. To create the admin account, visit /login in the browser and complete the setup form, or call the setup endpoint directly:
POST /api/auth/setup
Content-Type: application/json
{
"username": "admin",
"password": "yourpassword"
}
Requirements:
username: any non-empty stringpassword: minimum 6 characters
This endpoint only works when no users exist. Subsequent calls return 403 { "error": "Setup already completed" }.
On success, a session cookie is set and 201 is returned:
{ "username": "admin" }
Web UI Authentication
Sessions use an httpOnly cookie named gearbox_session.
| Property | Value |
|---|---|
| Cookie name | gearbox_session |
| httpOnly | true |
| sameSite | Lax |
| path | / |
| Max age | 30 days |
The session expiry is automatically refreshed on each authenticated request. As long as the app is used at least once every 30 days, the session stays active.
Passwords are hashed with argon2 via Bun.password.
Changing Your Password
Requires an active session cookie.
PUT /api/auth/password
Content-Type: application/json
{
"currentPassword": "oldpassword",
"newPassword": "newpassword"
}
API Keys
API keys are intended for programmatic access (scripts, MCP clients, integrations). They are managed under Settings > API Keys in the web UI, or via the API endpoints listed below.
Key behavior
- Keys are shown once at creation time. Store them securely.
- Keys are stored as an argon2 hash. Only the 8-character prefix is stored in plaintext for display and lookup purposes.
- Pass the key via the
X-API-Keyrequest header on any write request.
POST /api/items
X-API-Key: gbk_a1b2c3d4...
Content-Type: application/json
{ "name": "Revelate Tangle", "categoryId": 2 }
If both a session cookie and an X-API-Key header are present, the API key is checked first.
Auth Middleware Behavior
The middleware applied to /api/* (excluding /api/auth/*) follows these rules:
GETrequests — always allowed, no auth check.- No users exist — returns
403 { "error": "setup_required" }. X-API-Keyheader present — verified against stored hashes;401on failure.gearbox_sessioncookie present — verified against sessions table; refreshed on success;401on failure.- Neither credential present — returns
401 { "error": "Authentication required" }.
The /api/auth/* routes handle their own auth logic and are excluded from the global middleware.
Auth API Reference
GET /api/auth/me
Returns the current session state. Always public.
Response when logged in:
{
"user": { "id": 1 },
"setupRequired": false
}
Response when logged out, setup complete:
{
"user": null,
"setupRequired": false
}
Response when no users exist:
{
"user": null,
"setupRequired": true
}
POST /api/auth/setup
Create the first admin account. Only works when no users exist.
Request:
{
"username": "admin",
"password": "yourpassword"
}
Response: 201
{ "username": "admin" }
Sets gearbox_session cookie.
POST /api/auth/login
Log in with username and password.
Request:
{
"username": "admin",
"password": "yourpassword"
}
Response: 200
{ "username": "admin" }
Sets gearbox_session cookie. Returns 401 on invalid credentials.
POST /api/auth/logout
Clear the current session. No request body needed.
Response:
{ "ok": true }
Clears the gearbox_session cookie and deletes the session from the database.
PUT /api/auth/password
Change the admin password. Requires an active session cookie (not API key).
Request:
{
"currentPassword": "oldpassword",
"newPassword": "newpassword"
}
Response:
{ "ok": true }
Returns 401 if currentPassword is incorrect.
GET /api/auth/keys
List all API keys. Returns name, prefix, and creation timestamp — never the full key.
Requires auth.
Response:
[
{
"id": 1,
"name": "Claude Code",
"prefix": "gbk_a1b2",
"createdAt": "2025-03-01T10:00:00.000Z"
}
]
POST /api/auth/keys
Create a new API key. The full key is returned once and cannot be retrieved again.
Requires auth.
Request:
{ "name": "Claude Code" }
Response: 201
{
"id": 1,
"name": "Claude Code",
"key": "gbk_a1b2c3d4e5f6g7h8i9j0...",
"prefix": "gbk_a1b2"
}
DELETE /api/auth/keys/:id
Revoke an API key by ID. Requires auth.
Response:
{ "ok": true }
Frontend Behavior
- A login button is shown in the top-right corner of the UI (Gitea-style).
- The floating action button (FAB) for adding items is hidden when not logged in.
- Edit and delete actions on items, threads, and setups require auth. Unauthenticated users see read-only views.
- When
setupRequiredis true, the UI redirects to the setup flow.