This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/docs/ICD/admin.md
2026-03-15 01:33:38 +00:00

6.5 KiB

Platform Administration

Cross-cutting admin operations that don't belong to a specific domain.

User Management

GET    /admin/users                  → { "users": [...], "total": N }
POST   /admin/users                  ← { "username", "password", "email", "role" }
PUT    /admin/users/:id/role         ← { "role": "admin|user" }
PUT    /admin/users/:id/active       ← { "is_active": false }
DELETE /admin/users/:id
POST   /admin/users/:id/reset-password  ← { "password": "..." }
POST   /admin/users/:id/vault/reset     → destroys user's UEK (BYOK keys lost)

PUT /admin/users/:id/role refuses to demote the last remaining admin (returns 409). DELETE /admin/users/:id also refuses if the target is the last admin, and destroys the user's vault before deleting the row.

Global Settings & Policies

Settings are key-value pairs in global_settings. Policies are boolean flags that gate features.

GET /admin/settings                  → all settings
GET /admin/settings/:key             → single setting
PUT /admin/settings/:key             ← { "value": ... }

The handler auto-detects: if the value is a string and the key is a known policy name, it writes to the policies table. Otherwise it writes to global_config as JSON.

Public settings (non-admin, for FE bootstrapping):

GET /settings/public
{
  "banner": { "enabled": true, "text": "DEVELOPMENT", "bg": "#007a33", "fg": "#ffffff", "position": "both" },
  "branding": { "instance_name": "Switchboard", "logo_url": "...", "tagline": "..." },
  "has_admin_prompt": true,
  "storage_configured": true,
  "paste_to_file_chars": 2000,
  "policies": {
    "allow_registration": "true",
    "allow_user_byok": "true",
    "allow_user_personas": "true",
    "channel_retention_mode": "flexible"
  }
}

Note: policy values are strings ("true" / "false"), not booleans.

Banner Configuration

The environment banner is stored as a single global_config entry under the key "banner". It's a simple object:

{
  "enabled": true,
  "text": "DEVELOPMENT",
  "position": "both|top|bottom",
  "bg": "#007a33",
  "fg": "#ffffff"
}

Set via PUT /admin/settings/banner with { "value": { ... } }. The admin UI provides a color picker, position selector, and preset dropdown. The Go template base layout reads the banner from PageData and renders top/bottom strips with CSS custom properties.

Platform Stats

GET /admin/stats
{
  "users": 42,
  "channels": 156,
  "messages": 12847
}

Storage & Vault

Storage status:

GET /admin/storage/status            → { "backend", "healthy", "file_count", "total_bytes", ... }
GET /admin/storage/orphans           → { "count": 3 }
POST /admin/storage/cleanup          → removes orphaned blobs
GET /admin/storage/extraction        → extraction queue status

Vault:

GET /admin/vault/status              → { "encryption_key_set", "user_vaults_count", ... }

Audit Log

GET /admin/audit?page=1&per_page=50&action=user.create&actor_id=uuid&resource_type=...
GET /admin/audit/actions             → { "actions": ["user.create", "policy.update", ...] }

The list endpoint returns a paginated envelope:

{
  "data": [...],
  "total": 128,
  "page": 1,
  "per_page": 50
}

Audit entry shape:

{
  "id": "uuid",
  "actor_id": "uuid",
  "actor_name": "admin",
  "action": "user.create",
  "resource_type": "user",
  "resource_id": "uuid",
  "metadata": "{}",
  "ip_address": "10.0.0.1",
  "created_at": "..."
}

Usage & Pricing

GET /admin/usage                     → { "totals", "results" }
GET /admin/usage/teams/:id           → { "results": [...] }
GET /admin/usage/users/:id           → { "results": [...] }
GET /admin/pricing                   → [ ... ]
PUT /admin/pricing                   ← { "provider_config_id", "model_id", "input_per_m", "output_per_m" }
DELETE /admin/pricing/:provider_config_id/:model_id

GET /admin/pricing returns a bare array (no envelope).

PUT /admin/pricing accepts the full PricingEntry shape:

{
  "provider_config_id": "uuid",
  "model_id": "gpt-4o",
  "input_per_m": 2.50,
  "output_per_m": 10.00,
  "cache_create_per_m": 1.25,
  "cache_read_per_m": 0.50,
  "currency": "USD"
}

Pricing cannot be set for personal BYOK providers (403).

Personal usage (non-admin):

GET /usage                           → { "totals", "results" }

Scoped to the user's BYOK provider usage only.

Roles (Platform)

GET    /admin/roles                  → { "roles": [...] }
GET    /admin/roles/:role
PUT    /admin/roles/:role            ← { "permissions": {...} }
POST   /admin/roles/:role/test       ← test role permissions

Email Test

POST /admin/notifications/test-email

No request body required. Sends a test email to the authenticated admin's own email address using the configured SMTP settings.


Archived Channels

GET    /admin/channels/archived      → { "channels": [...], "total", "page", "per_page", "retention_mode" }
DELETE /admin/channels/:id/purge     → permanent delete (ignores retention)

Both endpoints require platform admin auth. GET accepts ?page=1&per_page=50 query parameters (max 100 per page). DELETE verifies the channel is archived before purging and cleans up associated file storage blobs.

Surfaces Admin

See surfaces.md for the full surface management API.

GET    /admin/surfaces               → all surfaces (including disabled)
GET    /admin/surfaces/:id           → surface with manifest
POST   /admin/surfaces/install       ← multipart .surface archive
PUT    /admin/surfaces/:id/enable
PUT    /admin/surfaces/:id/disable
DELETE /admin/surfaces/:id

Tasks Admin

See tasks.md for the full task management API.

GET    /admin/tasks                  → all tasks across all users/teams
POST   /admin/tasks/:id/run          → force run
POST   /admin/tasks/:id/kill         → cancel active run
DELETE /admin/tasks/:id              → delete task

Notifications Admin (v0.28.6)

POST   /admin/notifications/broadcast   ← { "title", "message", "level?" }
                                         → { "message": "broadcast sent", "count": N }

Sends a system.announcement notification to all active users. Also emits a system.broadcast WebSocket event for real-time delivery. level is optional (info | warning | critical, default info). See notifications.md for full details.