Files
nexus/README.md
T

396 lines
17 KiB
Markdown

# 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.
> 📋 **Architektur-Review** (2026-06-22): Board-first Orchestrierung, sichere
> Backend-Brücke und Gateway-Integration geprüft. Siehe
> [`docs/architecture-board-first-orchestration.md`](docs/architecture-board-first-orchestration.md)
> CI runs automatically on every push. CD can run **automatically after successful CI**
> on main or can be triggered **manually** (workflow_dispatch). Deploy reads
> `VERSION` but does not mutate Git or create tags. Rollback and database backup
> are separate manual workflows.
> See [phases/deployment.md](phases/deployment.md) for full CD documentation.
## 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 (Ollama and NVIDIA removed — OpenClaw-only)
- Responsive dark-mode operations dashboard
- Traefik reverse-proxy with Let's Encrypt TLS on `nexus.noveria.net`
## Local/container start
```bash
cp .env.template .env
# Replace every placeholder, especially POSTGRES_PASSWORD, JWT_KEY and BOOTSTRAP_OWNER_EMAIL.
docker compose up --build -d
curl http://127.0.0.1:18880/health
```
On an empty database the API creates exactly one owner from `BOOTSTRAP_OWNER_EMAIL`,
derives the initial display name from that email, and logs a generated temporary password once.
After first seed the password lives only in PostgreSQL. Existing databases are
never overwritten by the bootstrap process.
The API is exposed via Traefik reverse-proxy with automatic Let's Encrypt TLS.
Health checks, rate limiting, and security headers are active.
## 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 |
|---|---|
| `/home/projekte_bao/openclaw/data/openclaw/workspace-iris` | `/mnt/workspace-iris` |
| `/home/projekte_bao/openclaw/data/openclaw/workspace-programmer` | `/mnt/workspace-programmer` |
| `/home/projekte_bao/openclaw/data/openclaw/workspace-reviewer` | `/mnt/workspace-reviewer` |
| `/home/projekte_bao/openclaw/data/openclaw/workspace-architekt` | `/mnt/workspace-architekt` |
| `/home/projekte_bao/openclaw/data/openclaw/workspace-researcher` | `/mnt/workspace-researcher` |
| `/home/projekte_bao/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 with visible parent/child agent flow |
| `/models` | Models | Provider routing status |
| `/activity` | Activity | Audit timeline |
| `/chat` | Mobile Chat | Owner-chat preview |
## API endpoints
### Backend Bridge (Agent-zu-Backend, NICHT Frontend)
Der `/api/bridge/` Pfad ist ein strukturierter MCP-artiger Kommando-Adapter für die
Agent-zu-Backend-Kommunikation. Kein Frontend-Code ruft diese Endpunkte auf.
Auth: `X-Agent-Id` Header, `X-Nexus-Api-Key`, oder JWT. Rate-Limited (30/min).
| Methode | Pfad | Kommando | Beschreibung |
|---|---|---|---|
| `GET` | `/api/bridge/health` | — | Bridge-Health-Check |
| `POST` | `/api/bridge/tasks` | `create_task` | Neue Top-Level-Task erstellen |
| `POST` | `/api/bridge/tasks/{id}/children` | `create_child_task` | Child-Task unter Parent erstellen |
| `PATCH` | `/api/bridge/tasks/{id}/status` | `update_status` | Task-Status ändern |
| `POST` | `/api/bridge/tasks/{id}/activity` | `append_activity` | Aktivitätseintrag anhängen |
| `POST` | `/api/bridge/tasks/{id}/handoff` | `handoff` | Task an anderen Agent übergeben |
| `GET` | `/api/bridge/board` | `get_board` | Vollständiges Task-Board |
| `GET` | `/api/bridge/tasks/{id}` | `get_task` | Einzelne Task abrufen |
| `GET` | `/api/bridge/tasks/{id}/children` | `get_children` | Child-Tasks abrufen |
| `GET` | `/api/bridge/tasks/{id}/activity` | `get_activity` | Task-Aktivität abrufen |
| `GET` | `/api/bridge/agent-overview` | `get_agent_overview` | Agent-Workflow-Übersicht |
Response-Format (TaskBridgeCommandResponse<T>):
```json
{
"ok": true,
"command": "create_task",
"data": { ... },
"error": null,
"timestamp": "2026-06-22T15:30:00.000Z"
}
```
### 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) |
### Parent/Child task flow
The Task Board now models OpenClaw delegation as a visible parent/child flow:
- Iris keeps the parent task `In progress` while delegated work is running.
- Delegated agent work is represented as visible child tasks linked via `parentTaskId`.
- Child tasks use the normal visible states (`Backlog`, `In progress`, `Review`, `Blocked`, `Done`) instead of a separate hidden delegation lane.
- Agent progress hints on parent tasks derive from recent activity and child-task status summaries.
- Full workflow documentation: [`docs/openclaw-task-board-flow.md`](docs/openclaw-task-board-flow.md)
### 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 routes through the OpenClaw Gateway only.
Ollama and NVIDIA providers have been removed. Currently active models:
| Agent | Model |
|-------|-------|
| Iris | `openai/gpt-5.4` |
| Programmer, Executor | `deepseek/deepseek-v4-flash` |
| Reviewer, Architekt, Researcher | `deepseek/deepseek-v4-pro` |
Claude models (Sonnet 4.6, Opus 4.6/4.7/4.8) are available via `claude-cli` backend.
The Settings module reports runtime and provider state without exposing
credentials.
## CI/CD
### CI — Automatic
Every push to `main` triggers `.gitea/workflows/ci.yaml`:
- **Backend**: .NET restore → build → test
- **Frontend**: pnpm install → type-check → test → build
- **Security**: Scan for hardcoded secrets in source code
CI must never break. If it does, Reviewer fixes.
### CD — Auto + Manual (CD v3)
Deployment can happen automatically or manually:
#### Auto-Deploy (after successful CI on main)
- Triggered by `workflow_run` after `CI - Build & Test` succeeds on `main`
- Deploys the current `main` version after CI succeeds.
- Skips automatically if the triggering commit contains `[skip ci]`
- The deploy workflow reads `VERSION`; it does not mutate Git, bump versions, or create tags
#### Manual Deploy (`workflow_dispatch`)
1. DevOps triggers `Deploy Nexus v2` in Gitea Actions
2. Workflow validates `VERSION`, builds and deploys `main`
3. Health check + smoke test verify the deployment
#### Rollback (`workflow_dispatch`)
1. DevOps triggers `Rollback to Previous Version` in Gitea Actions
2. Enters target git tag (e.g. `v0.2.49`) + confirmation `ROLLBACK`
3. Workflow checks out the tag, rebuilds with `--no-cache`, redeploys
4. Health check + smoke test verify the rollback
#### Database Backup (`workflow_dispatch`)
1. DevOps triggers `Database Backup` in Gitea Actions
2. Optionally also copies backup to a host path (`/home/projekte_bao/backups`)
3. Workflow dumps PostgreSQL via `pg_dumpall`, gzips, and uploads as a Gitea artifact
4. Artifacts are retained for 90 days (configurable)
5. Optional nightly schedule (uncomment the cron trigger in `backup.yaml`)
#### Failure Handling
When deploy or rollback fails:
- **DevOps (Architekt)** analyses the error
- **Reviewer (Code-Fixer)** fixes the problem
- **DevOps** re-deploys to verify the fix
The workflow outputs a formatted handoff message with the job URL.
Full CD documentation: [phases/deployment.md](phases/deployment.md)