Admin Panel¶
Web-based administration interface for monitoring and managing your Sygen infrastructure. Built with Next.js, it connects to the Sygen Core API server to provide real-time dashboards, agent chat, task management, and multi-server control.
Separate repository
The Admin Panel lives in its own repository: sygen-admin. This page covers setup and integration with Sygen Core.
Architecture¶
┌─────────────────────────┐
│ Browser Client │
└────────┬────────┬────────┘
│ │
HTTP │ │ WebSocket
│ │
┌────────▼────────▼────────┐
│ Sygen Admin (Next.js) │
│ :3000 │
└────────┬────────┬────────┘
│ │
REST API │ │ WS
│ │
┌────────▼────────▼────────┐
│ Sygen Core API Server │
│ :8799 │
│ ┌──────────────────┐ │
│ │ Agents / Cron / │ │
│ │ Memory / Tasks │ │
│ └──────────────────┘ │
└──────────────────────────┘
- HTTP REST — all CRUD operations, authentication, system status
- WebSocket
/ws/admin— real-time chat streaming, tool activity events, system status updates
Quick Start¶
Prerequisites¶
- Sygen Core with the API server enabled (see Core API Setup below)
- Node.js 20+ (only for manual builds)
Option 1: Sygen CLI (Recommended)¶
The easiest way — the sygen admin command handles cloning, building, and running automatically:
sygen admin setup # Clone repo, install deps, build
sygen admin start # Start the admin panel (auto-detects API URL and token)
The CLI reads your config.json and passes the API URL and token to the admin panel automatically. No manual .env configuration needed.
All available subcommands:
| Command | Description |
|---|---|
sygen admin setup | Clone repository, install dependencies, build |
sygen admin start | Start the server (default port 3000) |
sygen admin start --port 3001 | Start on a custom port |
sygen admin stop | Stop the server |
sygen admin status | Show installation and running status |
sygen admin update | Pull latest changes, rebuild, restart |
sygen admin open | Open in default browser |
Option 2: Docker¶
docker run -d \
--name sygen-admin \
-p 3000:3000 \
-e NEXT_PUBLIC_SYGEN_API_URL=http://your-sygen-server:8799 \
-e SYGEN_API_URL=http://your-sygen-server:8799 \
-e SYGEN_API_TOKEN=your-api-token \
-e NEXT_PUBLIC_USE_MOCK=false \
ghcr.io/alexeymorozua/sygen-admin:latest
Open http://localhost:3000 in your browser.
Option 3: Docker Compose¶
git clone https://github.com/alexeymorozua/sygen-admin.git
cd sygen-admin
cp .env.example .env
# Edit .env with your Sygen Core URL and API token
docker compose up -d
Option 4: Manual Build¶
git clone https://github.com/alexeymorozua/sygen-admin.git
cd sygen-admin
cp .env.example .env
# Edit .env with your settings
npm install
npm run build
npm start
Environment Variables¶
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_SYGEN_API_URL | Yes | http://localhost:8080 | Base URL of your Sygen Core API server |
NEXT_PUBLIC_SYGEN_API_TOKEN | Yes | — | API bearer token for authenticating with Sygen Core |
NEXT_PUBLIC_USE_MOCK | No | false | Set to true for development/demo. When true, the UI uses built-in mock data |
Warning
All variables prefixed with NEXT_PUBLIC_ are embedded at build time in Docker. If you change them, rebuild the image.
Core API Setup¶
Enable the API server in Sygen Core so the Admin Panel can connect.
1. Enable the API¶
Edit your config/config.json:
2. Set a JWT secret (optional but recommended)¶
3. Restart Sygen Core¶
REST Endpoints¶
Authentication¶
| Method | Endpoint | Description |
|---|---|---|
POST | /api/auth/login | Authenticate with username+password or API token |
POST | /api/auth/refresh | Refresh access token (re-checks user status) |
POST | /api/auth/logout | Logout (revokes refresh token) |
GET | /api/auth/me | Get current user info (live from users.json) |
PUT | /api/auth/profile | Update own display name, password, or avatar |
POST | /api/auth/2fa/login | Complete 2FA login with temp token + TOTP code |
POST | /api/auth/2fa/setup | Generate TOTP secret for the current user |
POST | /api/auth/2fa/verify | Verify TOTP code and enable 2FA |
POST | /api/auth/2fa/disable | Disable 2FA (requires current TOTP code) |
System¶
| Method | Endpoint | Description |
|---|---|---|
GET | /health | Basic health check (200 OK) |
GET | /api/system/status | CPU, RAM, disk, uptime_seconds, uptime_human. Breaking (v1.3.29): agents, sessions, cron_jobs, tasks_total, tasks_active are removed — use /api/dashboard/summary.counters instead. See migration-1.3.29.md for the full migration checklist. |
GET | /api/dashboard/summary | One-shot dashboard payload: system.* (metrics + uptime_human), counters (agents_total/agents_online/active_tasks/running_crons/failed_last_24h), and the 10 latest recent_activity events with localized title/subtitle/severity. Honors Accept-Language: ru|en (default ru). 5s server-side cache per language+ACL bucket. |
GET | /api/activity | Legacy activity feed (raw type / message). Kept for backward compatibility; new clients should use /api/activity/recent. |
GET | /api/activity/recent?limit=20&severity=error | Canonical activity stream. Each event has id, type (machine, for filtering), title + subtitle (localized), agent_name, timestamp (ISO-8601 UTC), severity (info/success/warning/error). Types: task_completed, task_failed, agent_started, agent_stopped, cron_fired, cron_failed, webhook_called, auth_login. Unknown types receive a localized fallback — raw machine strings never leak into title. Optional severity query filters server-side; when severity=error, honors the caller's errors_ack_at from users.json (events older than the ack are hidden). |
GET | /api/activity/{id}?verbose=1 | Lookup a single event by id. verbose=1 adds the raw source record + origin (source) for debug. |
POST | /api/dashboard/errors/ack | Mark the Errors card as seen for the current user. Writes errors_ack_at = time.time() to the user's row in users.json, invalidates the summary cache, and returns {ok: true, ack_at: <ts>}. Per-user: acking on one browser is reflected on all devices. counters.failed_last_24h cutoff becomes max(now - 86400, errors_ack_at) for that user. Requires viewer role. Static-token legacy admin (no users.json row) is rejected with 400. |
GET | /api/config | System configuration (secrets masked as ***) |
GET | /api/logs?lines=100&agent=main | Fetch logs (optional filters) |
GET | /api/logs/poll?agent=main&after=0 | Poll for new log lines (live tail) |
Agents¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/agents | List all agents (includes has_avatar; provider is derived from model via ModelRegistry.provider_for() when not set in agents.json) |
GET | /api/agents/{name} | Get agent details (same provider-derivation fallback) |
GET | /api/agents/{name}/avatar | Get agent avatar image (public, no auth) |
POST | /api/agents/{name}/avatar | Upload agent avatar (multipart, admin only) |
DELETE | /api/agents/{name}/avatar | Delete agent avatar (admin only) |
GET | /api/agents/{name}/metrics?period=24h | Aggregated metrics for an agent (24h or 7d) |
GET | /api/agents/{name}/metrics/history?period=24h | Time-bucketed metrics history for charts |
Chat¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/chat?agent={id} | Get chat history |
POST | /api/chat/{agentId} | Send message to agent |
WS | /ws/admin | Real-time streaming |
Chat Sessions¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/chat/sessions?agent={id} | List chat sessions for an agent |
POST | /api/chat/sessions | Create a new chat session |
PUT | /api/chat/sessions/{id} | Rename a chat session ({title}) |
DELETE | /api/chat/sessions/{id} | Delete a chat session and its history |
POST | /api/chat/sessions/{id}/provider | Set or clear a per-session provider/model override |
GET | /api/chat/sessions/{id}/messages | Get message history for a session (supports pagination) |
PUT | /api/chat/sessions/{id}/messages | Save messages to a session (supports merge mode) |
POST | /api/chat/sessions/{id}/read | Advance the per-user read marker for a session |
GET /api/chat/sessions/{id}/messages returns the full history as a plain array by default. When the client passes ?limit=N (max 500), the response switches to a paginated shape {messages, has_more, total} containing the newest N messages. To fetch the previous page, add ?before=<id> — the server returns the limit messages immediately older than that id. The admin UI uses this to load a 50-message window and lazily pull older pages as the user scrolls up. The response envelope also carries a top-level last_read_message_id (string or null) — the caller's own read marker for that session, so the client can paint unread state on initial load without a second round trip.
PUT /api/chat/sessions/{id}/messages overwrites the stored history with the request body by default. When the client passes ?merge=true, the server upserts messages by id against the existing file (preserving order), so a paginated client that only holds a slice in memory cannot wipe messages it never loaded.
POST /api/chat/sessions/{id}/provider accepts either {"provider": "claude", "model": "opus"} to set an override, or {"provider": null, "model": null} to reset to the agent default. The override is per-chat-session only — it is not written back to the agent's config.json. When a message is sent on a session with an active override, the WebSocket dispatcher prepends an @<model> directive so the orchestrator's directive parser routes the message to the selected provider.
POST /api/chat/sessions/{id}/read takes {"last_read_message_id": "<msg-id>"} and advances the caller's read marker to that message. The marker is monotonic, per-user, per-session: the server compares the requested id's index against the stored marker's index in the session's history and only advances it to the right. Requests with an older or equal id are idempotent — no write happens, the current effective marker is returned. Unknown message ids (not present in the session's history) respond 400. The response shape is {session_id, last_read_message_id, unread_count}. GET /api/chat/sessions additionally returns last_read_message_id and unread_count per session in the list so clients can render badges without extra requests. State lives in ~/.sygen/chat_reads.json ({user_id: {session_id: {last_read_message_id, updated_at}}}). A successful advance broadcasts a chat.read WebSocket event to every socket held by the same user (cross-device sync).
Providers¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/providers/available | List providers (Claude/Gemini/Codex), their models, and the main agent default |
Returns {providers: [{name, authenticated, models, default_model}], agent_default_model, agent_default_provider} — unauthenticated providers are still listed (with authenticated: false) so the admin UI can render them as disabled. Authenticated state is derived from the main agent's live ProviderManager, so it reflects the actual auth result of each CLI (Claude Code, Gemini CLI, Codex).
Cron Jobs¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/cron | List all cron jobs |
POST | /api/cron | Create cron job |
PUT | /api/cron/{id} | Update cron job |
DELETE | /api/cron/{id} | Delete cron job |
POST | /api/cron/{id}/run | Trigger manual run |
Webhooks¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/webhooks | List all webhooks |
POST | /api/webhooks | Create webhook |
PUT | /api/webhooks/{id} | Update webhook |
DELETE | /api/webhooks/{id} | Delete webhook |
POST | /api/webhooks/{id}/verify | Verify HMAC-SHA256 signature for a webhook payload |
Tasks¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/tasks?status=running&limit=50 | List tasks (optional filters) |
GET | /api/tasks/{id} | Get task details |
POST | /api/tasks | Create a new task |
POST | /api/tasks/{id}/cancel | Cancel running task |
Sessions¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/sessions | List active sessions |
DELETE | /api/sessions/{id} | Terminate session |
Memory¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/memory | Get main memory content |
PUT | /api/memory | Update memory content |
GET | /api/memory/modules | List memory modules. Each module includes lines (number) and size (human-readable), so clients can render progress against the cron cleanup soft limit (~80 lines per module) |
GET | /api/memory/modules/{filename} | Read a specific memory module |
PUT | /api/memory/modules/{filename} | Update a specific memory module |
RAG¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/rag/status | Read current RAG state: enabled, embedding_model, reranker_enabled, reranker_model, index_workspace, index_memory, top_k_retrieval, top_k_final, plus vector DB path/size/existence, indexed chunk_count (counted directly from chroma.sqlite3) and memory_fact_count (non-empty, non-comment lines across memory_system/*.md — used by the admin UI to drive the 200/500 recommendation thresholds) |
PUT | /api/rag/config | Update RAG config (admin only). Accepts any subset of enabled, reranker_enabled, index_workspace, index_memory, top_k_retrieval, top_k_final. Unknown fields are rejected with 400. Response includes restart_required: true — bot must be restarted for changes to take effect |
Skills¶
Skills live in two scopes:
- Global (shared across all agents):
~/.sygen/skills/ - Per-agent (private to one agent):
~/.sygen/workspace/skills/for the main agent, or~/.sygen/agents/{name}/workspace/skills/for sub-agents
A per-agent skill with the same name as a global one overrides it for that agent.
| Method | Endpoint | Description |
|---|---|---|
GET | /api/agents/{agent}/skills?scope=merged | Default: merged view of global + per-agent skills. Per-agent entries that shadow a global one carry overrides: true |
GET | /api/agents/{agent}/skills?scope=own | Per-agent skills only |
GET | /api/agents/{agent}/skills?scope=global | Global skills only |
POST | /api/agents/{agent}/skills | Create a new per-agent skill (body: {name, content}) |
GET | /api/agents/{agent}/skills/{skill} | Read a skill's main document (falls back to the global copy if not present per-agent) |
PUT | /api/agents/{agent}/skills/{skill} | Update a per-agent skill's main document |
DELETE | /api/agents/{agent}/skills/{skill} | Delete a per-agent skill directory |
Write operations always target the per-agent directory — globals are read-only through the per-agent API. Skill names must match [a-zA-Z0-9_-]+ (same charset as agent names). Path traversal protection is enforced.
Files¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/files/list?path=… | List files and directories inside the user-files area |
POST | /api/files/mkdir | Create a new directory |
DELETE | /api/files | Delete a file or directory |
Uploads and downloads use the existing /upload and static-serving endpoints.
Users (Admin only)¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/users | List all users (passwords excluded) |
POST | /api/users | Create user |
PUT | /api/users/{username} | Update user (role, agents, password, active) |
DELETE | /api/users/{username} | Delete user |
Audit Log (Admin only)¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/audit?limit=200 | Get recent audit log entries |
Export / Import (Admin only)¶
| Method | Endpoint | Description |
|---|---|---|
GET | /api/export | Export cron jobs, webhooks, and users as JSON |
POST | /api/import | Import configuration (merge mode: add new, skip existing) |
WebSocket Protocol (/ws/admin)¶
Client → Server:
{ "type": "auth", "token": "your-jwt-token" }
{ "type": "message", "agent": "main", "text": "Hello" }
{ "type": "abort", "agent": "main" }
Server → Client:
{ "type": "auth_ok", "agents": ["main", "assistant"], "role": "admin" }
{ "type": "text_delta", "text": "partial response...", "session_id": "sess-...", "agent": "main" }
{ "type": "tool_activity", "tool": "Bash", "session_id": "sess-...", "agent": "main" }
{ "type": "system_status", "data": "Thinking...", "session_id": "sess-...", "agent": "main" }
{ "type": "result", "text": "final answer", "files": [], "session_id": "sess-...", "agent": "main" }
{ "type": "error", "message": "description", "session_id": "sess-...", "agent": "main" }
{ "type": "chat_message", "kind": "task_result", "role": "agent", "agent": "main",
"session_id": "sess-...", "content": "...",
"meta": { "task_id": "t-1", "task_name": "Flight search" }, "timestamp": 1700000000 }
{ "type": "chat.read", "session_id": "sess-...",
"last_read_message_id": "msg-...", "unread_count": 0 }
Streaming frames (text_delta, tool_activity, system_status, result, error) carry session_id + agent so the client can route the event to the correct chat when multiple sessions are open. The server also mirrors every streaming frame to the same user's other live WebSockets (e.g. a second browser or mobile PWA) so chat sessions stay in sync across devices.
chat_message envelopes are Telegram-transport deliveries mirrored into the admin chat. kind is one of task_result, task_question, interagent, or text (background). They are persisted into the agent's most recently updated REST chat session so they survive a client reload.
chat.read is broadcast whenever any of the user's clients advances a session read marker (via POST /api/chat/sessions/{id}/read). Every live WebSocket that belongs to the same user receives the same payload, so a sidebar badge cleared on the desktop immediately disappears on mobile without a refetch. The event is scoped to the marker-owning user — other users sharing the same session (if permissions ever allow it) do not see it. last_read_message_id may be null when no marker has been stored yet (e.g. when validation rejected an unknown message id).
Authentication¶
JWT Flow¶
- User opens the Admin Panel → redirected to
/login - User enters username + password
- Credentials are sent to
POST /api/auth/loginon Sygen Core - If 2FA is enabled, Core returns
requires_2fa: true+temp_token→ user enters TOTP code →POST /api/auth/2fa/login - Core returns
access_token(short-lived),refresh_token(long-lived), anduserinfo - All subsequent API calls include
Authorization: Bearer <access_token> - On 401 responses, the client automatically refreshes via
POST /api/auth/refresh - Refresh re-checks user status from
users.json(role, active, allowed_agents) - If refresh fails or user is disabled, the user is redirected back to login
Default User¶
On first startup, a default admin user is created automatically with a random 16-character password:
- Username:
admin - Password: written to
~/.sygen/_secrets/.initial_admin_password(mode0600)
Warning
Delete .initial_admin_password after first login, or reset it to something you control with sygen users set-password (see below).
Resetting a Forgotten Password¶
If you've lost the admin password (or the bootstrap file was removed without being read), run on the host:
sygen users set-password # defaults to username "admin"
sygen users set-password alice # reset any user
sygen users set-password admin --disable-2fa # also clear TOTP
The command:
- prompts for the new password twice (no echo)
- rejects anything shorter than 8 characters
- if TOTP is enabled for the user, asks whether to keep or disable it (use
--disable-2fato skip the prompt) - bumps
token_versionso every existing JWT for that user is immediately invalidated - removes
.initial_admin_passwordwhen resettingadmin
The command writes to _secrets/users.json atomically and never prints the password. It requires shell access to the host — it is not exposed in the admin panel.
Roles & Permissions¶
| Role | Permissions |
|---|---|
| admin | Full access — CRUD on all resources (users, cron jobs, webhooks, memory, tasks, sessions, config), export/import, audit log |
| operator | Read all resources + run cron jobs, cancel tasks, create/manage chat sessions, view logs |
| viewer | Read-only access to dashboards, agents, tasks, cron jobs, webhooks, sessions, activity |
Create/Update/Delete operations on cron jobs, webhooks, memory, users, and config require the admin role.
Agent-Scoped Access (allowed_agents)¶
Admins can restrict users to specific agents via the allowed_agents field. When set, the restriction applies across all agent-scoped endpoints:
- Agents — only listed agents are visible
- Cron jobs — filtered by the job's
agentfield; create/update/delete/run require the job's agent to be allowed - Webhooks — filtered by the webhook's
agentfield; create/update/delete/verify require the webhook's agent to be allowed - Tasks — filtered by
parent_agent/agentfield; get/cancel/create validate agent access - Sessions — filtered by session's
agentfield; delete validates agent access - Chat sessions — filtered by
agentfield; create/delete/read/write messages validate agent access - Logs / Log polling — agent param is validated against allowed list
- Activity feed — events are filtered to only show allowed agents
- Agent metrics — agent name is validated against allowed list
Users with an empty allowed_agents list have access to all agents (no restriction).
Rate Limiting¶
The Core API enforces rate limits on login attempts (5 per minute per IP). Exceeding the limit returns 429 Too Many Requests. The Admin Panel handles this gracefully with retry logic.
Multi-Server¶
The Admin Panel can manage multiple Sygen Core instances from a single interface.
Adding Servers¶
- Navigate to Settings → Servers
- Click Add Server
- Fill in:
- Name — display label (e.g., "Production", "Staging")
- URL — Sygen Core API URL (e.g.,
https://prod.example.com:8799) - Token — API token for that server
- Color — visual identifier in the UI
- Click Test Connection to verify, then Save
Switching Servers¶
Use the server selector in the dashboard header to switch between configured servers. Each server maintains its own connection state and health status. The active server indicator shows which instance you're currently managing.
Default Server¶
The server configured via environment variables (NEXT_PUBLIC_SYGEN_API_URL and NEXT_PUBLIC_SYGEN_API_TOKEN) is automatically added as the default. Additional servers are stored in the browser's local storage.
Pages¶
URL-based detail selection
Notifications, memory modules, cron jobs, webhooks, tasks, agents, files, and skills all use query params (?id=…, ?module=…, ?skill=…, ?path=…) to track the selected item. Selections survive page reloads, are linkable, and can be shared with other admins.
Dashboard¶
Real-time system health overview with CPU, RAM, and disk metrics read from Linux /proc (not hardcoded). Each metric includes a sparkline history chart showing the last 30 readings. The dashboard auto-refreshes every 10 seconds and includes a manual refresh button. Also displays agent count, active tasks, and recent activity.
Backed by a single GET /api/dashboard/summary call (replaced the previous 3-4 parallel fetches to /agents, /system/status, /activity, etc.). The recent-activity widget renders the backend-localized title / subtitle only — raw event type strings are never shown.
Agents¶
List of all registered agents with their status (online/offline), provider, and session info. The header shows online/total agent count. Click an agent card to open a detail panel showing model, provider, sessions, and allowed users. Each agent can have a custom avatar — click the avatar area in the detail panel to upload an image, or use the upload/delete controls in the Info tab. Agent avatars are served publicly without authentication and displayed in agent cards, chat messages, and the detail panel. Includes a logs viewer tab with live tailing (real-time polling with pause/resume), a metrics tab with sparkline charts (executions, duration, success rate, errors for 24h/7d periods), and an "Open Chat" quick action.
Chat¶
Real-time WebSocket chat with any agent. Supports multi-session conversations — create, switch, and delete chat sessions per agent with server-side message persistence. Features streaming responses, tool activity indicators, and file attachments. Select the target agent from the sidebar and manage session history in the sessions panel.
Cron Jobs¶
Full CRUD for scheduled tasks via modal forms. Create new jobs with name, cron expression, agent assignment, and description. The editor includes client-side cron expression validation with real-time hints, human-readable schedule descriptions (e.g., "Daily at 03:00"), a preset schedule picker (every minute, hourly, daily, weekly, monthly), and an enabled/disabled toggle. Edit existing jobs, delete with confirmation dialog, and trigger manual runs. Shows last run time and next scheduled execution. All operations provide toast notification feedback.
Webhooks¶
Full CRUD for webhook endpoints via modal forms. Create new webhooks with name, URL, HTTP method, and agent assignment. Edit existing webhooks and delete with confirmation dialog. Each webhook displays its URL, linked agent, and trigger count. All operations provide toast notification feedback.
Tasks¶
Monitor background tasks. View status (running, completed, failed, cancelled), output, and duration. Cancel running tasks directly from the UI. The task list auto-refreshes every 5 seconds when tasks are running. The detail panel shows the task prompt/description and an expandable result/output view. A running task count indicator with pulse animation is displayed in the header, along with a manual refresh button.
Memory¶
View and edit all agent memory modules. Content loads dynamically on module selection via dedicated per-module endpoints (selection is URL-driven via ?module=… so it survives reloads and is linkable). Root files (MAINMEMORY.md, SHAREDMEMORY.md) show a plain line count; nested modules under modules/ show a colored N / 80 pill (green → yellow → orange → red) against the cron cleanup soft limit — so it's obvious at a glance which modules the cleanup cron is about to trim. All modules are editable (not just the main memory file). The editor supports the memory markdown format used by Sygen's memory system. Save operations provide toast notification feedback. The backend enforces path traversal protection on filenames.
Skills¶
Skill management with global and per-agent scopes. Pick an agent from the selector at the top, browse its skills in the left list (or the overlay on mobile), and edit the primary document (SKILL.md / README.md) in the editor.
A scope filter at the top of the list (Все / Общие / Только агента) toggles between the merged view, global skills only, and per-agent skills only. Each entry carries a badge: 🌐 Общий, 👤 Агента, or 🌐 ← 👤 Перекрывает общий when a per-agent skill shadows a global one.
The "New skill" button opens a modal for name + initial content and always creates in the per-agent dir; deletion is confirmed inline. Skill names are validated against [a-zA-Z0-9_-]+. All operations are backed by /api/agents/{agent}/skills (with ?scope= query param) and provide toast feedback.
Files¶
File browser and uploader. Lists items inside the user-files area with breadcrumb navigation (URL-driven via ?path=…). Supports creating directories, uploading files (drag-and-drop or picker), downloading, and deleting. Preview pane shows metadata for the selected entry.
Settings¶
System configuration viewer and server management. The configuration is fetched from the GET /api/config endpoint with sensitive values (API keys, tokens) masked as ***. Configure connected servers and view current runtime settings. Also hosts the RAG Management block: toggle retrieval on/off, see indexed chunk count and vector DB size, inspect the embedding model and top-K values, and flip sub-toggles for index_memory, index_workspace, and the reranker. All RAG changes are persisted via PUT /api/rag/config and require a bot restart (the UI surfaces a warning).
Toast Notifications¶
A global toast notification system is integrated across all CRUD operations (cron jobs, webhooks, memory). Toasts support four types: success, error, warning, and info. They auto-dismiss with configurable duration, use a slide-in animation, and stack up to 5 notifications at a time.
Users (Admin only)¶
User management with role-based access control. Create, edit, and delete users with role assignment (admin/operator/viewer). Toggle active status, set per-agent access restrictions via allowed_agents, and view the audit log tab with action history (login, user CRUD events).
Notifications¶
Dedicated page for viewing all system notifications (cron results, system events, task completions). The left panel shows a filterable list by type (All / Cron / System / Task / Webhook) with unread indicators (bold title + blue dot). Click a notification to see its full content rendered as Markdown in the detail panel. Actions include "Mark as read", "Reply" (opens chat with the agent that produced the notification), and "Mark all read". The sidebar shows a notification icon with an unread count badge, and the popup bell shows the 5 most recent notifications with a "Show all" link to the full page. Mobile-responsive with fullscreen overlay for the detail panel.
Severity model¶
Every notification carries a severity field with one of four values:
| Severity | Meaning | Counts as unread? | Default visible? |
|---|---|---|---|
critical | Failures (cron failed, task failed) — bold red | yes | yes |
warning | Webhook errors — yellow | yes | yes |
info | Webhook success and most events — neutral | yes | yes |
silent | Successful cron/task with a non-empty body — visible but downplayed | no | no |
Server-side mapping (sygen_bot/api/notifications.py::classify_event):
cron successwith empty body ("",done,ok,success,completed) → not persisted (logged only).cron successwith a real body →silent.cron failed→critical.task successwith empty body → not persisted.task successwith a real body →silent.task failed→critical.webhook success→info.webhook error→warning.
The admin UI exposes a severity-filter row of toggles next to the type filter; the choice is stored in localStorage (key sygen.notif.severities.v1). The unread badge in the sidebar excludes silent records — they are always visible in the list when their toggle is on, but they never push the badge.
The REST endpoint GET /api/notifications accepts ?severity=critical,warning,info to constrain results. GET /api/notifications/unread-count always excludes silent.
A throttling helper lives in sygen_bot/api/notification_throttle.py (should_throttle(source_id, severity)). It is a foundation for upcoming push notifications: critical is never throttled, warning/info are throttled if the same source_id was seen in the last 60 seconds, silent is always throttled. Currently the function is invoked from observers only for logging — no real push delivery is suppressed yet.
Profile¶
User profile page with display name editing, password change, and avatar upload. The user avatar is displayed in chat messages and the profile page. Avatar files are uploaded via /upload and stored as a path reference in the user profile.
Servers¶
Add, edit, and remove Sygen Core server connections. Test connectivity and switch between instances.
Deployment¶
Nginx Reverse Proxy¶
server {
listen 443 ssl;
server_name admin.example.com;
ssl_certificate /etc/ssl/certs/admin.example.com.pem;
ssl_certificate_key /etc/ssl/private/admin.example.com.key;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket support
location /ws/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
}
SSL/TLS¶
For production deployments, always terminate TLS at the reverse proxy. Use certificates from Let's Encrypt or your organization's CA. The Admin Panel itself does not handle TLS — it expects the proxy to provide it.
Resource Requirements¶
| Component | CPU | RAM | Disk |
|---|---|---|---|
| Admin Panel (Next.js) | 0.5 vCPU | 256 MB | 200 MB |
| Sygen Core API | shared with Sygen | shared with Sygen | — |
The Admin Panel is lightweight. The main resource consumer is the Sygen Core instance itself.