Skip to content

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)

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:

{
  "api": {
    "enabled": true,
    "host": "0.0.0.0",
    "port": 8799,
    "token": "your-secure-api-token"
  }
}
{
  "api": {
    "jwt_secret": "your-jwt-secret-key"
  }
}

3. Restart Sygen Core

systemctl restart sygen
# or: sygen restart

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

  1. User opens the Admin Panel → redirected to /login
  2. User enters username + password
  3. Credentials are sent to POST /api/auth/login on Sygen Core
  4. If 2FA is enabled, Core returns requires_2fa: true + temp_token → user enters TOTP code → POST /api/auth/2fa/login
  5. Core returns access_token (short-lived), refresh_token (long-lived), and user info
  6. All subsequent API calls include Authorization: Bearer <access_token>
  7. On 401 responses, the client automatically refreshes via POST /api/auth/refresh
  8. Refresh re-checks user status from users.json (role, active, allowed_agents)
  9. 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 (mode 0600)

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-2fa to skip the prompt)
  • bumps token_version so every existing JWT for that user is immediately invalidated
  • removes .initial_admin_password when resetting admin

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 agent field; create/update/delete/run require the job's agent to be allowed
  • Webhooks — filtered by the webhook's agent field; create/update/delete/verify require the webhook's agent to be allowed
  • Tasks — filtered by parent_agent / agent field; get/cancel/create validate agent access
  • Sessions — filtered by session's agent field; delete validates agent access
  • Chat sessions — filtered by agent field; 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

  1. Navigate to Settings → Servers
  2. Click Add Server
  3. 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
  4. 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 success with empty body ("", done, ok, success, completed) → not persisted (logged only).
  • cron success with a real body → silent.
  • cron failedcritical.
  • task success with empty body → not persisted.
  • task success with a real body → silent.
  • task failedcritical.
  • webhook successinfo.
  • webhook errorwarning.

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.