# Nexus Nexus is the operations platform for the Noveria ecosystem. OpenClaw is an adapter-backed agent runtime, not a dependency of the frontend or domain model. ## Current foundation - Vue 3, TypeScript, Pinia, Vue Router and Tailwind CSS - ASP.NET Core 10 REST API (Minimal API pattern) - Entity Framework Core and PostgreSQL - JWT owner authentication with rotating refresh sessions - `IAgentRuntime` abstraction with an OpenClaw adapter - `IModelProvider` abstractions for Ollama and NVIDIA - Responsive dark-mode operations dashboard - Container-only entry point on `127.0.0.1:18880` ## Local/container start ```bash cp .env.example .env # Replace every placeholder, especially POSTGRES_PASSWORD, JWT_KEY, # OWNER_EMAIL and OWNER_PASSWORD. docker compose up --build -d curl http://127.0.0.1:18880/health ``` On an empty database the API creates exactly one owner from `OWNER_EMAIL`, `OWNER_PASSWORD` and `OWNER_DISPLAY_NAME`. The password must contain at least 14 characters. Existing databases are never overwritten by the bootstrap process. The web service is loopback-only. Public reverse-proxy activation for `nexus.noveria.net` remains a separate infrastructure change and must terminate TLS before forwarding to port `18880`. ## Workspace mounts The API container mounts agent workspaces from the host for file browsing and the config editor. These are mounted under `/mnt/workspace-{agentId}`: | Host path | Container mount | |---|---| | `/opt/openclaw/data/openclaw/workspace-iris` | `/mnt/workspace-iris` | | `/opt/openclaw/data/openclaw/workspace-programmer` | `/mnt/workspace-programmer` | | `/opt/openclaw/data/openclaw/workspace-reviewer` | `/mnt/workspace-reviewer` | | `/opt/openclaw/data/openclaw/workspace-architekt` | `/mnt/workspace-architekt` | | `/opt/openclaw/data/openclaw/workspace-researcher` | `/mnt/workspace-researcher` | | `/opt/openclaw/data/openclaw/workspace-executor` | `/mnt/workspace-executor` | ## Frontend architecture ### Source layout ``` frontend/src/ ├── App.vue # Root shell with sidebar + standalone views ├── main.ts # App bootstrap ├── router.ts # Vue Router config ├── types/ │ ├── index.ts # Re-exports │ ├── agent.ts # AgentInfo, AgentDetail, TeamMember │ ├── config.ts # ConfigFileInfo, SecurityStatus │ ├── dashboard.ts # OperationsSnapshot, RuntimeStatus, etc. │ └── project.ts # MemoryFile, DocFile types ├── stores/ │ ├── auth.ts # Auth store (JWT, login/refresh/logout) │ └── operations.ts # Operations store (snapshot, CRUD, approve/reject) ├── services/ │ └── api.ts # Authenticated fetch wrapper (auto-refresh) ├── composables/ │ └── useTime.ts # Greeting composable (Morgen/Tag/Abend) ├── views/ │ ├── LoginView.vue │ ├── DashboardView.vue # New dashboard (Phase 2) │ ├── ProjectDetailView.vue │ ├── SettingsView.vue │ ├── MemoryView.vue │ ├── DocsView.vue │ ├── TeamView.vue │ ├── SecurityView.vue │ ├── IncidentsView.vue │ ├── CalendarView.vue │ ├── AgentDetailView.vue │ └── AgentsIndexView.vue ├── components/ │ ├── layout/ │ │ ├── AppSidebar.vue │ │ └── AppHeader.vue │ └── dashboard/ # New dashboard components (Phase 2) │ ├── IrisPanel.vue # Agent overview + metrics + chat │ ├── OperationsFeed.vue # Live activity feed with filters │ ├── AgendaPanel.vue # Daily agenda with checkboxes + localStorage │ ├── ActiveInitiatives.vue # Project cards with progress │ └── RecentlyFinished.vue # Quick status chips └── ModuleView.vue ``` ### App.vue `standaloneViews` whitelist New views must be registered in the `standaloneViews` computed property in `App.vue` (line ~34). Without this entry, `RouterView` will not render the component — the route is valid but the template stays empty. ### New dashboard (Phase 2) The dashboard was redesigned with a three-column layout: - **IrisPanel** — Agent avatar/greeting, metrics counters (open tasks, blocked, overdue, today), AI suggestions, quick action buttons, and an inline chat box. - **OperationsFeed** — Searchable/filterable activity feed with colour-coded status dots and yesterday/today/week grouping. - **AgendaPanel** — Daily agenda with checkable items persisted in `localStorage` under key `nexus-agenda-done`. Items are sectioned into "Heute", "Morgen", and "Überfällig". - **ActiveInitiatives** — Project initiative cards with progress bars, status badges (healthy/attention/blocked/paused/completed), and last activity timestamps. - **RecentlyFinished** — Horizontally scrollable chip list of recently completed items. ### Authentication - Passwords use versioned PBKDF2-SHA256 hashes with random salts and 210,000 iterations. - Access tokens expire after 15 minutes and are held only in browser memory. - Refresh tokens are random, stored only as SHA-256 hashes in PostgreSQL, rotated on use and checked for reuse. - The browser receives the refresh token only as a `HttpOnly`, `Secure`, `SameSite=Strict` cookie. - Login and refresh endpoints are rate-limited per forwarded client IP (5 attempts/minute). - All `/api/v1` operations routes require a valid access token; `/health` remains public. - Swagger is enabled only in the Development environment. - CSRF protection via `X-CSRF-TOKEN` header and `nexus-csrf` cookie (not HttpOnly). ### Security - Never commit `.env`. - Generate `JWT_KEY` from at least 32 random bytes. - Rotate any credential that has appeared in chat before using it. - Do not expose PostgreSQL or the API container directly. - Keep OpenClaw behind the `IAgentRuntime` contract. - Keep the API reachable only through the bundled web proxy or another trusted reverse proxy. ## Frontend routes (SPA) The SPA uses history-mode routes. Standalone views (whitelisted in App.vue): | Route | View | Description | |---|---|---| | `/login` | LoginView | Owner login | | `/dashboard` | DashboardView | Operations snapshot with IrisPanel, Feed, Agenda | | `/memory` | MemoryView | Memory file browser with search | | `/docs` | DocsView | Documentation file browser | | `/team` | TeamView | Agent team org map | | `/security` | SecurityView | Security status center | | `/projects/:id` | ProjectDetailView | Project detail | | `/incidents` | IncidentsView | Incident diary | | `/calendar` | CalendarView | Cron/scheduler overview | | `/agents` | AgentsIndexView | Agent inventory | | `/agents/:id` | AgentDetailView | Agent detail + config editor | | `/settings` | SettingsView | Profile + password management | Legacy ModuleView routes (not standalone, rendered through `ModuleView.vue`): | Route | Name | Description | |---|---|---| | `/projects` | Projects | Project portfolio | | `/tasks` | Task Board | Task board | | `/models` | Models | Provider routing status | | `/activity` | Activity | Audit timeline | | `/chat` | Mobile Chat | Owner-chat preview | ## API endpoints ### Health & Auth (public or rate-limited) | Method | Path | Auth | Description | |---|---|---|---| | `GET` | `/health` | No | Health check with runtime + PostgreSQL | | `GET` | `/api/v1/auth/csrf` | No | Get CSRF token | | `POST` | `/api/v1/auth/login` | No (rate-limited) | Login with email/password | | `POST` | `/api/v1/auth/refresh` | No (rate-limited) | Refresh access token | | `POST` | `/api/v1/auth/logout` | No | Clear refresh token | | `GET` | `/api/v1/auth/me` | Yes | Current user info | | `PATCH` | `/api/v1/auth/profile` | Yes | Update display name | | `POST` | `/api/v1/auth/change-password` | Yes | Change password (min 10 chars) | ### Operations | Method | Path | Description | |---|---|---| | `GET` | `/api/v1/operations/snapshot` | Full operations snapshot (runtime, agents, projects, tasks, activity, metrics) | ### Projects | Method | Path | Description | |---|---|---| | `GET` | `/api/v1/projects` | List all projects | | `POST` | `/api/v1/projects` | Create project | | `GET` | `/api/v1/projects/{id}` | Get project detail | | `PATCH` | `/api/v1/projects/{id}` | Update project (name, description, status) | | `DELETE` | `/api/v1/projects/{id}` | Delete or archive project (archives if has tasks) | ### Tasks | Method | Path | Description | |---|---|---| | `GET` | `/api/v1/tasks` | List all tasks | | `POST` | `/api/v1/tasks` | Create task | | `GET` | `/api/v1/tasks/pending-approval` | Tasks in progress older than 1 hour | | `PATCH` | `/api/v1/tasks/{id}` | Update task (title, priority, projectId) | | `PATCH` | `/api/v1/tasks/{id}/state` | Update task state | | `POST` | `/api/v1/tasks/{id}/approve` | Approve task (in-progress → done) | | `POST` | `/api/v1/tasks/{id}/reject` | Reject task (in-progress → backlog) | | `DELETE` | `/api/v1/tasks/{id}` | Delete task (only done/backlog states) | ### Agents | Method | Path | Description | |---|---|---| | `GET` | `/api/v1/agents` | List all agents | | `GET` | `/api/v1/agents/{id}` | Agent detail (with sub-agents, identity) | | `GET` | `/api/v1/agents/{id}/activity` | Agent-specific activity (last 50) | | `POST` | `/api/v1/agents/{id}/command` | Send command to agent | | `GET` | `/api/v1/agents/{id}/config` | List agent config files (IDENTITY.md, SOUL.md, etc.) | | `GET` | `/api/v1/agents/{id}/config/{fileName}` | Read config file content | | `PUT` | `/api/v1/agents/{id}/config/{fileName}` | Save config file (atomic write) | ### Memory & Docs | Method | Path | Description | |---|---|---| | `GET` | `/api/v1/memory` | List memory files (daily + MEMORY.md) | | `GET` | `/api/v1/memory/search?q=` | Full-text search in memory files | | `GET` | `/api/v1/memory/{name}` | Get memory file content | | `GET` | `/api/v1/docs` | List documentation files by category | | `GET` | `/api/v1/docs/{**path}` | Get doc file content (catch-all) | ### Activity & Team | Method | Path | Description | |---|---|---| | `GET` | `/api/v1/activity` | Paginated activity feed (supports `type`, `sort`, `page`, `pageSize`) | | `GET` | `/api/v1/routing` | Model routing status | | `GET` | `/api/v1/team` | Team org map with identity excerpts | | `GET` | `/api/v1/incidents` | List incident diary entries | | `GET` | `/api/v1/incidents/{name}` | Get incident detail | | `GET` | `/api/v1/security/status` | Security configuration status | ### Calendar (Scheduler) | Method | Path | Description | |---|---|---| | `GET` | `/api/v1/calendar` | Cron job overview (gateway or fallback) | | `GET` | `/api/v1/calendar/upcoming` | Upcoming cron jobs | ### Chat | Method | Path | Auth | Description | |---|---|---|---| | `POST` | `/api/v1/chat` | Yes (rate-limited) | Route message through IAgentRuntime | Project and task mutations create activity records. The API applies committed EF Core migrations after PostgreSQL becomes healthy. No destructive endpoints are implemented on the data layer. ## State machine (tasks) Tasks follow a simple state machine: ``` Backlog → In progress → Done Backlog → Blocked → In progress / Done ``` - Only `In progress` or `Blocked` tasks can be approved (→ Done) or rejected (→ Backlog). - Only `Done` or `Backlog` tasks can be deleted. ## Runtime chat and model routing `POST /api/v1/chat` routes authenticated owner messages through the `IAgentRuntime` contract. The browser never receives a Gateway password or model provider key. Conversation IDs are stable per browser and Iris is the default agent target. The configured model-routing policy is: 1. `qwen3:4b` through Ollama for routine and monitoring work 2. `moonshotai/kimi-k2.6` through NVIDIA for primary work 3. `gpt-5.5` through OpenClaw for strategic and critical review The Settings module reports runtime and provider state without exposing credentials. # Trigger CI