# 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 ``` ```json { "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: ```json { "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 ``` ```json { "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: ```json { "data": [...], "total": 128, "page": 1, "per_page": 50 } ``` Audit entry shape: ```json { "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: ```json { "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](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](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](notifications.md#admin-broadcast-v0286) for full details.