From eeb6174de0b806b04ffc0dec5353d2ee67f8e36f Mon Sep 17 00:00:00 2001 From: Bao Date: Tue, 9 Jun 2026 16:31:42 +0200 Subject: [PATCH] Initial commit: Nexus Mission Control Platform - ASP.NET Core 10 Backend (JWT Auth, Agent config API) - Vue 3 Frontend (Dashboard, Team, Agents, Config Editor) - PostgreSQL Database - Docker Compose setup - Mission Control Dashboard redesign --- .env.example | 12 + .env.template | 27 + .gitignore | 31 + DEPLOYMENT_PLAN.md | 155 + README.md | 101 + backend-tests/AgentServiceTests.cs | 78 + backend-tests/Nexus.Api.Tests.csproj | 24 + ...CoverageSourceRootsMapping_Nexus.Api.Tests | Bin 0 -> 490 bytes .../bin/Debug/net10.0/HealthChecks.NpgSql.dll | Bin 0 -> 21504 bytes ...ft.AspNetCore.Authentication.JwtBearer.dll | Bin 0 -> 52008 bytes ...osoft.EntityFrameworkCore.Abstractions.dll | Bin 0 -> 34848 bytes ...crosoft.EntityFrameworkCore.Relational.dll | Bin 0 -> 2193952 bytes .../net10.0/Microsoft.EntityFrameworkCore.dll | Bin 0 -> 2825760 bytes .../Microsoft.IdentityModel.Abstractions.dll | Bin 0 -> 19488 bytes .../Microsoft.IdentityModel.JsonWebTokens.dll | Bin 0 -> 143392 bytes .../Microsoft.IdentityModel.Logging.dll | Bin 0 -> 36784 bytes ....IdentityModel.Protocols.OpenIdConnect.dll | Bin 0 -> 137144 bytes .../Microsoft.IdentityModel.Protocols.dll | Bin 0 -> 40880 bytes .../Microsoft.IdentityModel.Tokens.dll | Bin 0 -> 329248 bytes .../bin/Debug/net10.0/Microsoft.OpenApi.dll | Bin 0 -> 486752 bytes ...ft.TestPlatform.CommunicationUtilities.dll | Bin 0 -> 137240 bytes .../Microsoft.TestPlatform.CoreUtilities.dll | Bin 0 -> 97824 bytes ...Microsoft.TestPlatform.CrossPlatEngine.dll | Bin 0 -> 315424 bytes ...soft.TestPlatform.PlatformAbstractions.dll | Bin 0 -> 53280 bytes .../Microsoft.TestPlatform.Utilities.dll | Bin 0 -> 67104 bytes ...crosoft.VisualStudio.CodeCoverage.Shim.dll | Bin 0 -> 17464 bytes ...osoft.VisualStudio.TestPlatform.Common.dll | Bin 0 -> 247840 bytes ....VisualStudio.TestPlatform.ObjectModel.dll | Bin 0 -> 334880 bytes .../bin/Debug/net10.0/Newtonsoft.Json.dll | Bin 0 -> 695336 bytes backend-tests/bin/Debug/net10.0/Nexus.Api | Bin 0 -> 78256 bytes .../Debug/net10.0/Nexus.Api.Tests.deps.json | 766 +++++ .../bin/Debug/net10.0/Nexus.Api.Tests.dll | Bin 0 -> 10752 bytes .../bin/Debug/net10.0/Nexus.Api.Tests.pdb | Bin 0 -> 24456 bytes .../Nexus.Api.Tests.runtimeconfig.json | 19 + .../bin/Debug/net10.0/Nexus.Api.deps.json | 900 +++++ backend-tests/bin/Debug/net10.0/Nexus.Api.dll | Bin 0 -> 214016 bytes backend-tests/bin/Debug/net10.0/Nexus.Api.pdb | Bin 0 -> 56392 bytes .../net10.0/Nexus.Api.runtimeconfig.json | 20 + .../Nexus.Api.staticwebassets.endpoints.json | 1 + .../Npgsql.EntityFrameworkCore.PostgreSQL.dll | Bin 0 -> 677376 bytes backend-tests/bin/Debug/net10.0/Npgsql.dll | Bin 0 -> 1478656 bytes .../Swashbuckle.AspNetCore.Swagger.dll | Bin 0 -> 22016 bytes .../Swashbuckle.AspNetCore.SwaggerGen.dll | Bin 0 -> 157696 bytes .../Swashbuckle.AspNetCore.SwaggerUI.dll | Bin 0 -> 722944 bytes .../System.IdentityModel.Tokens.Jwt.dll | Bin 0 -> 90032 bytes .../bin/Debug/net10.0/appsettings.json | 25 + ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16416 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 16928 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 21040 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 20520 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 23584 bytes ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16928 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 16928 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 21016 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 21016 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 24088 bytes .../Debug/net10.0/dist/Nexus.Api.deps.json | 337 ++ .../net10.0/dist/Nexus.Api.runtimeconfig.json | 21 + .../Nexus.Api.staticwebassets.endpoints.json | 1 + .../bin/Debug/net10.0/dist/appsettings.json | 25 + .../bin/Debug/net10.0/dist/web.config | 11 + ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16928 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 16928 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 21024 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 21024 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 24096 bytes ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16928 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 16928 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 21024 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 21024 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 24096 bytes ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16928 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 16936 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 21024 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 21032 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 24104 bytes ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16928 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 17440 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 22048 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 21544 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 24608 bytes ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16928 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 16936 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 21536 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 21016 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 24096 bytes ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16936 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 16944 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 21544 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 21016 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 24104 bytes ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16416 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 16928 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 21024 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 21024 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 24104 bytes ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 17440 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 17440 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 23072 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 23072 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 26152 bytes backend-tests/bin/Debug/net10.0/testhost.dll | Bin 0 -> 48672 bytes ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16944 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 16936 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 21040 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 20512 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 23584 bytes .../bin/Debug/net10.0/xunit.abstractions.dll | Bin 0 -> 21928 bytes .../bin/Debug/net10.0/xunit.assert.dll | Bin 0 -> 187088 bytes .../bin/Debug/net10.0/xunit.core.dll | Bin 0 -> 70352 bytes .../Debug/net10.0/xunit.execution.dotnet.dll | Bin 0 -> 279760 bytes .../xunit.runner.visualstudio.testadapter.dll | Bin 0 -> 1213136 bytes ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16416 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 16928 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 20000 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 20000 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 23080 bytes ...tform.CommunicationUtilities.resources.dll | Bin 0 -> 16416 bytes ...t.TestPlatform.CoreUtilities.resources.dll | Bin 0 -> 16928 bytes ...TestPlatform.CrossPlatEngine.resources.dll | Bin 0 -> 20512 bytes ...alStudio.TestPlatform.Common.resources.dll | Bin 0 -> 20000 bytes ...dio.TestPlatform.ObjectModel.resources.dll | Bin 0 -> 23080 bytes ...oreApp,Version=v10.0.AssemblyAttributes.cs | 4 + .../Debug/net10.0/Nexus.Ap.676E4176.Up2Date | 0 .../net10.0/Nexus.Api.Tests.AssemblyInfo.cs | 22 + .../Nexus.Api.Tests.AssemblyInfoInputs.cache | 1 + ....GeneratedMSBuildEditorConfig.editorconfig | 18 + .../net10.0/Nexus.Api.Tests.GlobalUsings.g.cs | 8 + .../net10.0/Nexus.Api.Tests.assets.cache | Bin 0 -> 53788 bytes ...s.Api.Tests.csproj.AssemblyReference.cache | Bin 0 -> 22184 bytes ...s.Api.Tests.csproj.CoreCompileInputs.cache | 1 + ...exus.Api.Tests.csproj.FileListAbsolute.txt | 126 + .../obj/Debug/net10.0/Nexus.Api.Tests.dll | Bin 0 -> 10752 bytes .../Nexus.Api.Tests.genruntimeconfig.cache | 1 + .../obj/Debug/net10.0/Nexus.Api.Tests.pdb | Bin 0 -> 24456 bytes .../obj/Debug/net10.0/ref/Nexus.Api.Tests.dll | Bin 0 -> 7680 bytes .../Debug/net10.0/refint/Nexus.Api.Tests.dll | Bin 0 -> 7680 bytes .../Nexus.Api.Tests.csproj.nuget.dgspec.json | 865 +++++ .../obj/Nexus.Api.Tests.csproj.nuget.g.props | 27 + .../Nexus.Api.Tests.csproj.nuget.g.targets | 10 + backend-tests/obj/project.assets.json | 2899 +++++++++++++++++ backend-tests/obj/project.nuget.cache | 57 + backend/Contracts/AuthRequests.cs | 42 + backend/Contracts/Requests.cs | 9 + backend/Contracts/Responses.cs | 50 + backend/Dockerfile | 13 + backend/Domain/Entities.cs | 93 + backend/Domain/Identity.cs | 45 + .../20260609064750_InitialCreate.Designer.cs | 220 ++ .../20260609064750_InitialCreate.cs | 143 + .../Migrations/NexusDbContextModelSnapshot.cs | 217 ++ backend/Infrastructure/NexusDbContext.cs | 31 + backend/Integrations/Contracts.cs | 40 + backend/Integrations/NvidiaProvider.cs | 25 + backend/Integrations/OllamaProvider.cs | 37 + backend/Integrations/OpenClawRuntime.cs | 75 + backend/Nexus.Api.csproj | 18 + backend/Program.cs | 1229 +++++++ backend/Routing/ModelRoutingService.cs | 35 + backend/Services/AgentService.cs | 222 ++ backend/Services/AuthService.cs | 248 ++ backend/Services/PasswordSecurity.cs | 67 + backend/appsettings.json | 25 + backend/dist/HealthChecks.NpgSql.dll | Bin 0 -> 21504 bytes ...ft.AspNetCore.Authentication.JwtBearer.dll | Bin 0 -> 52008 bytes ...osoft.EntityFrameworkCore.Abstractions.dll | Bin 0 -> 34616 bytes ...crosoft.EntityFrameworkCore.Relational.dll | Bin 0 -> 2194744 bytes .../dist/Microsoft.EntityFrameworkCore.dll | Bin 0 -> 2828600 bytes .../Microsoft.IdentityModel.Abstractions.dll | Bin 0 -> 19488 bytes .../Microsoft.IdentityModel.JsonWebTokens.dll | Bin 0 -> 143392 bytes .../dist/Microsoft.IdentityModel.Logging.dll | Bin 0 -> 36784 bytes ....IdentityModel.Protocols.OpenIdConnect.dll | Bin 0 -> 137144 bytes .../Microsoft.IdentityModel.Protocols.dll | Bin 0 -> 40880 bytes .../dist/Microsoft.IdentityModel.Tokens.dll | Bin 0 -> 329248 bytes backend/dist/Microsoft.OpenApi.dll | Bin 0 -> 486752 bytes backend/dist/Nexus.Api | Bin 0 -> 78256 bytes backend/dist/Nexus.Api.deps.json | 356 ++ backend/dist/Nexus.Api.dll | Bin 0 -> 223232 bytes backend/dist/Nexus.Api.pdb | Bin 0 -> 54772 bytes backend/dist/Nexus.Api.runtimeconfig.json | 21 + .../Nexus.Api.staticwebassets.endpoints.json | 1 + .../Npgsql.EntityFrameworkCore.PostgreSQL.dll | Bin 0 -> 677376 bytes backend/dist/Npgsql.dll | Bin 0 -> 1478656 bytes .../dist/Swashbuckle.AspNetCore.Swagger.dll | Bin 0 -> 22016 bytes .../Swashbuckle.AspNetCore.SwaggerGen.dll | Bin 0 -> 157696 bytes .../dist/Swashbuckle.AspNetCore.SwaggerUI.dll | Bin 0 -> 722944 bytes .../dist/System.IdentityModel.Tokens.Jwt.dll | Bin 0 -> 90032 bytes backend/dist/appsettings.json | 25 + backend/dist/web.config | 11 + compose.yaml | 64 + frontend/Dockerfile | 13 + frontend/index.html | 14 + frontend/nginx.conf | 29 + frontend/package.json | 30 + frontend/pnpm-lock.yaml | 1442 ++++++++ frontend/src/App.vue | 168 + frontend/src/components/ModuleView.vue | 630 ++++ .../src/components/config/ConfigEditor.vue | 206 ++ frontend/src/components/config/ConfigTabs.vue | 73 + .../dashboard/ActiveInitiatives.vue | 187 ++ .../src/components/dashboard/AgendaPanel.vue | 188 ++ .../src/components/dashboard/IrisPanel.vue | 315 ++ .../components/dashboard/OperationsFeed.vue | 210 ++ .../components/dashboard/RecentlyFinished.vue | 76 + frontend/src/components/layout/AppHeader.vue | 94 + frontend/src/components/layout/AppSidebar.vue | 203 ++ frontend/src/components/team/AgentCard.vue | 162 + .../src/components/team/AgentStatusBadge.vue | 40 + frontend/src/composables/useTime.ts | 12 + frontend/src/main.ts | 28 + frontend/src/router.ts | 40 + frontend/src/services/api.ts | 24 + frontend/src/stores/auth.ts | 103 + frontend/src/stores/operations.ts | 198 ++ frontend/src/style.css | 219 ++ frontend/src/types/agent.ts | 29 + frontend/src/types/config.ts | 19 + frontend/src/types/dashboard.ts | 67 + frontend/src/types/index.ts | 4 + frontend/src/types/project.ts | 30 + frontend/src/utils/markdown.ts | 108 + frontend/src/views/AgentDetailView.vue | 424 +++ frontend/src/views/AgentsIndexView.vue | 316 ++ frontend/src/views/CalendarView.vue | 397 +++ frontend/src/views/DashboardView.vue | 87 + frontend/src/views/DocsView.vue | 485 +++ frontend/src/views/IncidentsView.vue | 456 +++ frontend/src/views/LoginView.vue | 61 + frontend/src/views/MemoryView.vue | 463 +++ frontend/src/views/ProjectDetailView.vue | 481 +++ frontend/src/views/SecurityView.vue | 332 ++ frontend/src/views/SettingsView.vue | 276 ++ frontend/src/views/TeamView.vue | 299 ++ frontend/tests/operations.test.ts | 18 + frontend/tsconfig.app.json | 18 + frontend/tsconfig.json | 8 + frontend/tsconfig.node.json | 10 + frontend/vite.config.ts | 18 + ops/deploy.sh | 36 + ops/install-ollama-host.sh | 52 + ops/nginx-nexus.conf | 49 + ops/setup-https.sh | 107 + phases/changelog.md | 31 + phases/deployment.md | 71 + phases/phase-1-mvp.md | 128 + phases/phase-2.md | 132 + phases/phase-3.md | 12 + phases/runtime-routing.md | 23 + 248 files changed, 19706 insertions(+) create mode 100644 .env.example create mode 100644 .env.template create mode 100644 .gitignore create mode 100644 DEPLOYMENT_PLAN.md create mode 100644 README.md create mode 100644 backend-tests/AgentServiceTests.cs create mode 100644 backend-tests/Nexus.Api.Tests.csproj create mode 100644 backend-tests/bin/Debug/net10.0/.msCoverageSourceRootsMapping_Nexus.Api.Tests create mode 100755 backend-tests/bin/Debug/net10.0/HealthChecks.NpgSql.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.EntityFrameworkCore.Abstractions.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.EntityFrameworkCore.Relational.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.EntityFrameworkCore.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.IdentityModel.Abstractions.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.IdentityModel.Logging.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.IdentityModel.Protocols.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.IdentityModel.Tokens.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.OpenApi.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.TestPlatform.CommunicationUtilities.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.TestPlatform.CoreUtilities.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.TestPlatform.CrossPlatEngine.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.TestPlatform.PlatformAbstractions.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.TestPlatform.Utilities.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.VisualStudio.CodeCoverage.Shim.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.VisualStudio.TestPlatform.Common.dll create mode 100755 backend-tests/bin/Debug/net10.0/Microsoft.VisualStudio.TestPlatform.ObjectModel.dll create mode 100755 backend-tests/bin/Debug/net10.0/Newtonsoft.Json.dll create mode 100755 backend-tests/bin/Debug/net10.0/Nexus.Api create mode 100644 backend-tests/bin/Debug/net10.0/Nexus.Api.Tests.deps.json create mode 100644 backend-tests/bin/Debug/net10.0/Nexus.Api.Tests.dll create mode 100644 backend-tests/bin/Debug/net10.0/Nexus.Api.Tests.pdb create mode 100644 backend-tests/bin/Debug/net10.0/Nexus.Api.Tests.runtimeconfig.json create mode 100644 backend-tests/bin/Debug/net10.0/Nexus.Api.deps.json create mode 100644 backend-tests/bin/Debug/net10.0/Nexus.Api.dll create mode 100644 backend-tests/bin/Debug/net10.0/Nexus.Api.pdb create mode 100644 backend-tests/bin/Debug/net10.0/Nexus.Api.runtimeconfig.json create mode 100644 backend-tests/bin/Debug/net10.0/Nexus.Api.staticwebassets.endpoints.json create mode 100755 backend-tests/bin/Debug/net10.0/Npgsql.EntityFrameworkCore.PostgreSQL.dll create mode 100755 backend-tests/bin/Debug/net10.0/Npgsql.dll create mode 100755 backend-tests/bin/Debug/net10.0/Swashbuckle.AspNetCore.Swagger.dll create mode 100755 backend-tests/bin/Debug/net10.0/Swashbuckle.AspNetCore.SwaggerGen.dll create mode 100755 backend-tests/bin/Debug/net10.0/Swashbuckle.AspNetCore.SwaggerUI.dll create mode 100755 backend-tests/bin/Debug/net10.0/System.IdentityModel.Tokens.Jwt.dll create mode 100644 backend-tests/bin/Debug/net10.0/appsettings.json create mode 100755 backend-tests/bin/Debug/net10.0/cs/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/cs/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/cs/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/cs/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/cs/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/de/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/de/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/de/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/de/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/de/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100644 backend-tests/bin/Debug/net10.0/dist/Nexus.Api.deps.json create mode 100644 backend-tests/bin/Debug/net10.0/dist/Nexus.Api.runtimeconfig.json create mode 100644 backend-tests/bin/Debug/net10.0/dist/Nexus.Api.staticwebassets.endpoints.json create mode 100644 backend-tests/bin/Debug/net10.0/dist/appsettings.json create mode 100644 backend-tests/bin/Debug/net10.0/dist/web.config create mode 100755 backend-tests/bin/Debug/net10.0/es/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/es/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/es/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/es/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/es/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/fr/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/fr/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/fr/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/fr/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/fr/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/it/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/it/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/it/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/it/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/it/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ja/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ja/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ja/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ja/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ja/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ko/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ko/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ko/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ko/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ko/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/pl/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/pl/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/pl/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/pl/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/pl/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/pt-BR/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/pt-BR/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/pt-BR/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/pt-BR/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/pt-BR/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ru/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ru/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ru/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ru/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/ru/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/testhost.dll create mode 100755 backend-tests/bin/Debug/net10.0/tr/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/tr/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/tr/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/tr/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/tr/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/xunit.abstractions.dll create mode 100755 backend-tests/bin/Debug/net10.0/xunit.assert.dll create mode 100755 backend-tests/bin/Debug/net10.0/xunit.core.dll create mode 100755 backend-tests/bin/Debug/net10.0/xunit.execution.dotnet.dll create mode 100755 backend-tests/bin/Debug/net10.0/xunit.runner.visualstudio.testadapter.dll create mode 100755 backend-tests/bin/Debug/net10.0/zh-Hans/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/zh-Hans/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/zh-Hans/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/zh-Hans/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/zh-Hans/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/zh-Hant/Microsoft.TestPlatform.CommunicationUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/zh-Hant/Microsoft.TestPlatform.CoreUtilities.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/zh-Hant/Microsoft.TestPlatform.CrossPlatEngine.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/zh-Hant/Microsoft.VisualStudio.TestPlatform.Common.resources.dll create mode 100755 backend-tests/bin/Debug/net10.0/zh-Hant/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll create mode 100644 backend-tests/obj/Debug/net10.0/.NETCoreApp,Version=v10.0.AssemblyAttributes.cs create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Ap.676E4176.Up2Date create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Api.Tests.AssemblyInfo.cs create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Api.Tests.AssemblyInfoInputs.cache create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Api.Tests.GeneratedMSBuildEditorConfig.editorconfig create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Api.Tests.GlobalUsings.g.cs create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Api.Tests.assets.cache create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Api.Tests.csproj.AssemblyReference.cache create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Api.Tests.csproj.CoreCompileInputs.cache create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Api.Tests.csproj.FileListAbsolute.txt create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Api.Tests.dll create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Api.Tests.genruntimeconfig.cache create mode 100644 backend-tests/obj/Debug/net10.0/Nexus.Api.Tests.pdb create mode 100644 backend-tests/obj/Debug/net10.0/ref/Nexus.Api.Tests.dll create mode 100644 backend-tests/obj/Debug/net10.0/refint/Nexus.Api.Tests.dll create mode 100644 backend-tests/obj/Nexus.Api.Tests.csproj.nuget.dgspec.json create mode 100644 backend-tests/obj/Nexus.Api.Tests.csproj.nuget.g.props create mode 100644 backend-tests/obj/Nexus.Api.Tests.csproj.nuget.g.targets create mode 100644 backend-tests/obj/project.assets.json create mode 100644 backend-tests/obj/project.nuget.cache create mode 100644 backend/Contracts/AuthRequests.cs create mode 100644 backend/Contracts/Requests.cs create mode 100644 backend/Contracts/Responses.cs create mode 100644 backend/Dockerfile create mode 100644 backend/Domain/Entities.cs create mode 100644 backend/Domain/Identity.cs create mode 100644 backend/Infrastructure/Migrations/20260609064750_InitialCreate.Designer.cs create mode 100644 backend/Infrastructure/Migrations/20260609064750_InitialCreate.cs create mode 100644 backend/Infrastructure/Migrations/NexusDbContextModelSnapshot.cs create mode 100644 backend/Infrastructure/NexusDbContext.cs create mode 100644 backend/Integrations/Contracts.cs create mode 100644 backend/Integrations/NvidiaProvider.cs create mode 100644 backend/Integrations/OllamaProvider.cs create mode 100644 backend/Integrations/OpenClawRuntime.cs create mode 100644 backend/Nexus.Api.csproj create mode 100644 backend/Program.cs create mode 100644 backend/Routing/ModelRoutingService.cs create mode 100644 backend/Services/AgentService.cs create mode 100644 backend/Services/AuthService.cs create mode 100644 backend/Services/PasswordSecurity.cs create mode 100644 backend/appsettings.json create mode 100755 backend/dist/HealthChecks.NpgSql.dll create mode 100755 backend/dist/Microsoft.AspNetCore.Authentication.JwtBearer.dll create mode 100755 backend/dist/Microsoft.EntityFrameworkCore.Abstractions.dll create mode 100755 backend/dist/Microsoft.EntityFrameworkCore.Relational.dll create mode 100755 backend/dist/Microsoft.EntityFrameworkCore.dll create mode 100755 backend/dist/Microsoft.IdentityModel.Abstractions.dll create mode 100755 backend/dist/Microsoft.IdentityModel.JsonWebTokens.dll create mode 100755 backend/dist/Microsoft.IdentityModel.Logging.dll create mode 100755 backend/dist/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll create mode 100755 backend/dist/Microsoft.IdentityModel.Protocols.dll create mode 100755 backend/dist/Microsoft.IdentityModel.Tokens.dll create mode 100755 backend/dist/Microsoft.OpenApi.dll create mode 100755 backend/dist/Nexus.Api create mode 100644 backend/dist/Nexus.Api.deps.json create mode 100644 backend/dist/Nexus.Api.dll create mode 100644 backend/dist/Nexus.Api.pdb create mode 100644 backend/dist/Nexus.Api.runtimeconfig.json create mode 100644 backend/dist/Nexus.Api.staticwebassets.endpoints.json create mode 100755 backend/dist/Npgsql.EntityFrameworkCore.PostgreSQL.dll create mode 100755 backend/dist/Npgsql.dll create mode 100755 backend/dist/Swashbuckle.AspNetCore.Swagger.dll create mode 100755 backend/dist/Swashbuckle.AspNetCore.SwaggerGen.dll create mode 100755 backend/dist/Swashbuckle.AspNetCore.SwaggerUI.dll create mode 100755 backend/dist/System.IdentityModel.Tokens.Jwt.dll create mode 100644 backend/dist/appsettings.json create mode 100644 backend/dist/web.config create mode 100644 compose.yaml create mode 100644 frontend/Dockerfile create mode 100644 frontend/index.html create mode 100644 frontend/nginx.conf create mode 100644 frontend/package.json create mode 100644 frontend/pnpm-lock.yaml create mode 100644 frontend/src/App.vue create mode 100644 frontend/src/components/ModuleView.vue create mode 100644 frontend/src/components/config/ConfigEditor.vue create mode 100644 frontend/src/components/config/ConfigTabs.vue create mode 100644 frontend/src/components/dashboard/ActiveInitiatives.vue create mode 100644 frontend/src/components/dashboard/AgendaPanel.vue create mode 100644 frontend/src/components/dashboard/IrisPanel.vue create mode 100644 frontend/src/components/dashboard/OperationsFeed.vue create mode 100644 frontend/src/components/dashboard/RecentlyFinished.vue create mode 100644 frontend/src/components/layout/AppHeader.vue create mode 100644 frontend/src/components/layout/AppSidebar.vue create mode 100644 frontend/src/components/team/AgentCard.vue create mode 100644 frontend/src/components/team/AgentStatusBadge.vue create mode 100644 frontend/src/composables/useTime.ts create mode 100644 frontend/src/main.ts create mode 100644 frontend/src/router.ts create mode 100644 frontend/src/services/api.ts create mode 100644 frontend/src/stores/auth.ts create mode 100644 frontend/src/stores/operations.ts create mode 100644 frontend/src/style.css create mode 100644 frontend/src/types/agent.ts create mode 100644 frontend/src/types/config.ts create mode 100644 frontend/src/types/dashboard.ts create mode 100644 frontend/src/types/index.ts create mode 100644 frontend/src/types/project.ts create mode 100644 frontend/src/utils/markdown.ts create mode 100644 frontend/src/views/AgentDetailView.vue create mode 100644 frontend/src/views/AgentsIndexView.vue create mode 100644 frontend/src/views/CalendarView.vue create mode 100644 frontend/src/views/DashboardView.vue create mode 100644 frontend/src/views/DocsView.vue create mode 100644 frontend/src/views/IncidentsView.vue create mode 100644 frontend/src/views/LoginView.vue create mode 100644 frontend/src/views/MemoryView.vue create mode 100644 frontend/src/views/ProjectDetailView.vue create mode 100644 frontend/src/views/SecurityView.vue create mode 100644 frontend/src/views/SettingsView.vue create mode 100644 frontend/src/views/TeamView.vue create mode 100644 frontend/tests/operations.test.ts create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts create mode 100755 ops/deploy.sh create mode 100755 ops/install-ollama-host.sh create mode 100644 ops/nginx-nexus.conf create mode 100755 ops/setup-https.sh create mode 100644 phases/changelog.md create mode 100644 phases/deployment.md create mode 100644 phases/phase-1-mvp.md create mode 100644 phases/phase-2.md create mode 100644 phases/phase-3.md create mode 100644 phases/runtime-routing.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1dcc95a --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +POSTGRES_DB=nexus +POSTGRES_USER=nexus +POSTGRES_PASSWORD=replace-with-a-strong-database-password +JWT_KEY=replace-with-at-least-32-random-bytes +OWNER_EMAIL=owner@example.com +OWNER_PASSWORD=replace-with-at-least-14-characters +OWNER_DISPLAY_NAME=Owner +OPENCLAW_BASE_URL=http://host.docker.internal:18789 +OPENCLAW_GATEWAY_TOKEN= +OPENCLAW_GATEWAY_PASSWORD= +OLLAMA_BASE_URL=http://host.docker.internal:11434 +NVIDIA_API_KEY= diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..cf01fd4 --- /dev/null +++ b/.env.template @@ -0,0 +1,27 @@ +# Nexus Production Environment Configuration +# Copy this file to .env and fill in all values before deploying. + +# ── PostgreSQL ────────────────────────────────────────── +POSTGRES_DB=nexus +POSTGRES_USER=nexus +POSTGRES_PASSWORD=*** + +# ── Connection String (inferred from above when using compose) ── +# ConnectionStrings__Nexus=Host=localhost;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD} + +# ── JWT ───────────────────────────────────────────────── +# Generate with: openssl rand -base64 48 +JWT_KEY=*** # at least 32 bytes (base64-encoded) +JWT_ISSUER=nexus +JWT_AUDIENCE=nexus-web + +# ── Owner Account ─────────────────────────────────────── +OWNER_EMAIL=*** +OWNER_PASSWORD=*** # at least 14 characters; leave empty for auto-generated +OWNER_DISPLAY_NAME=*** # leave empty for auto-generated from email + +# ── OpenClaw Integration ──────────────────────────────── +# Base URL of the OpenClaw gateway (host.docker.internal from inside container) +OPENCLAW_BASE_URL=http://host.docker.internal:18789 +OPENCLAW_GATEWAY_TOKEN=*** +OPENCLAW_GATEWAY_PASSWORD=*** diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..475f9b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Nexus .gitignore + +# Build outputs +backend/bin/ +backend/obj/ +frontend/dist/ +frontend/node_modules/ + +# Environment +.env +!.env.example + +# IDE +.idea/ +.vs/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Docker +docker-compose.override.yml + +# Logs +*.log + +# OS temp +*.tmp diff --git a/DEPLOYMENT_PLAN.md b/DEPLOYMENT_PLAN.md new file mode 100644 index 0000000..e3fc2ff --- /dev/null +++ b/DEPLOYMENT_PLAN.md @@ -0,0 +1,155 @@ +# Nexus Phase 1 Deployment Plan + +> Generiert: 2026-06-09T02:49 CEST | Agent: architekt + +## Status: ✅ Bereit zum Deployment + +### Build-Artefakte + +| Komponente | Build-Methode | Status | +|---|---|---| +| Backend | Docker Multi-Stage (`dotnet publish` in Container) | ✅ Dockerfile bereit | +| Frontend | Docker Multi-Stage (`pnpm build` in Container) | ✅ Dockerfile bereit | +| Compose | `compose.yaml` | ✅ 3 Services definiert | +| Env | `.env` | ✅ Alle Secrets gesetzt | + +### Architektur + +``` +┌──────────────────────────────────────────────────────┐ +│ VPS Host (Debian 12) │ +│ ┌───────────────────────────────────────────────┐ │ +│ │ Docker Network: nexus │ │ +│ │ ┌─────────┐ ┌──────────┐ ┌────────────┐ │ │ +│ │ │postgres │ │ api │ │ web │ │ │ +│ │ │ :5432 │ │ :8080 │ │ :80 │ │ │ +│ │ │ 17-alp. │ │ .NET 10 │ │ nginx 1.27 │ │ │ +│ │ └─────────┘ └──────────┘ └─────┬──────┘ │ │ +│ │ │ │ │ +│ └───────────────────────────────────┼───────────┘ │ +│ │ │ +│ 127.0.0.1:18880 │ +│ │ │ +│ ┌───────────────────────────┼───────────────────┐ │ +│ │ Host nginx reverse proxy │ │ │ +│ │ nexus.noveria.net :443 ───┘ │ │ +│ └───────────────────────────────────────────────┘ │ +│ │ +│ ┌───────────────────────────────────────────────┐ │ +│ │ OpenClaw Gateway (Container) │ │ +│ │ Port 18789 ← host.docker.internal │ │ +│ └───────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────┘ +``` + +--- + +## 1. Deployment (vom VPS-Host ausführen) + +```bash +# Ins Nexus-Verzeichnis wechseln +cd /opt/openclaw/data/openclaw/workspace/nexus + +# Prüfen ob compose.yaml und .env da sind +ls -la compose.yaml .env + +# Deployment starten +docker compose up -d --build + +# Logs verfolgen +docker compose logs -f +``` + +**Erwartet:** +- 3 Container starten: `postgres`, `api`, `web` +- PostgreSQL wird healthy +- API migriert Datenbank und seedet Owner-Account +- Web (nginx) lauscht auf `127.0.0.1:18880` + +--- + +## 2. Post-Deployment-Verifikation + +### 2.1 Health Check +```bash +curl -s https://nexus.noveria.net/health | jq +``` +Erwartet: JSON mit `status: "Healthy"`, DB-Check `healthy`, OpenClaw-Check Status. + +### 2.2 Agent-Inventar +```bash +curl -s https://nexus.noveria.net/api/v1/agents | jq +``` +Erwartet: Array von Agenten oder leere Liste (404/401 möglich vor Login). + +### 2.3 SPA-Routing +```bash +curl -sI https://nexus.noveria.net/dashboard | head -20 +``` +Erwartet: HTTP 200, `content-type: text/html` (nginx liefert `index.html` für alle Routes). + +### 2.4 API-Dokumentation +```bash +curl -sI https://nexus.noveria.net/swagger/index.html +``` + +### 2.5 Container-Status +```bash +docker compose ps +``` +Erwartet: Alle 3 Services `Up` (healthy). + +--- + +## 3. Gateway-Neustart + +Nach erfolgreichem Deployment muss der OpenClaw-Gateway neu gestartet werden, damit der Researcher-Agent aktiv wird. + +⚠️ **Kein `docker restart` möglich** — kein Docker-Socket im Gateway-Container. + +**Im Gateway-Container (über OpenClaw-Session):** +```bash +PID=$(pgrep -f "node dist/index.js gateway" | head -1) +kill -HUP $PID +``` + +> Der Gateway-Container hat `restart: unless-stopped` in seiner eigenen Compose-Konfiguration. Ein `kill` (SIGTERM) auf den Node-Prozess führt automatisch zum Container-Neustart via Docker. Für graceful reload ist `kill -HUP` bevorzugt. + +--- + +## 4. Troubleshooting + +### API startet nicht +```bash +docker compose logs api +``` +Häufige Ursachen: +- PostgreSQL nicht healthy → `docker compose logs postgres` +- JWT_KEY / DB-Passwort falsch + +### Web nicht erreichbar +```bash +# Prüfen ob nginx im Container läuft +docker compose exec web nginx -t +# Prüfen ob Port gebunden ist +ss -tlnp | grep 18880 +``` + +### Host nginx Reverse Proxy +Falls `nexus.noveria.net` nicht erreichbar: +- Host nginx Config prüfen: Proxy-Pass auf `http://127.0.0.1:18880` +- TLS-Zertifikat gültig? + +--- + +## Abhängigkeiten + +| Was | Status | +|---|---| +| compose.yaml | ✅ Vorhanden | +| .env mit Secrets | ✅ Vorhanden | +| backend/Dockerfile | ✅ Multi-Stage .NET 10 | +| frontend/Dockerfile | ✅ Multi-Stage Node 24 + nginx | +| frontend/nginx.conf | ✅ CSP, Proxy, SPA-Routing | +| Host nginx Reverse Proxy | ⚠️ Muss auf Port 18880 zeigen | +| Docker installiert auf VPS | ⚠️ Vorausgesetzt | diff --git a/README.md b/README.md new file mode 100644 index 0000000..31a162a --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +# 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 +- 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`. + +## 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. +- All `/api/v1` operations routes require a valid access token; `/health` remains public. +- Swagger is enabled only in the Development environment. + +## 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. + +## Implemented Phase 1 modules + +The SPA uses history-mode routes: + +- `/login` owner login +- `/dashboard` operations snapshot +- `/projects` project portfolio +- `/tasks` task board +- `/agents` runtime and agent inventory +- `/models` provider routing status +- `/activity` audit timeline +- `/chat` mobile owner-chat preview +- `/settings` runtime and provider overview + +The API currently exposes: + +- `POST /api/v1/auth/login` +- `POST /api/v1/auth/refresh` +- `POST /api/v1/auth/logout` +- `GET /api/v1/auth/me` +- `GET /api/v1/operations/snapshot` +- `GET|POST /api/v1/projects` +- `GET|POST /api/v1/tasks` +- `PATCH /api/v1/tasks/{id}/state` +- `GET /api/v1/activity` +- `GET /api/v1/agents` +- `GET /api/v1/models` +- `GET /health` + +Project and task mutations create activity records. The API applies committed EF +Core migrations after PostgreSQL becomes healthy. No destructive endpoints are +implemented. + +## 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. diff --git a/backend-tests/AgentServiceTests.cs b/backend-tests/AgentServiceTests.cs new file mode 100644 index 0000000..ae1f572 --- /dev/null +++ b/backend-tests/AgentServiceTests.cs @@ -0,0 +1,78 @@ +using Nexus.Api.Services; +using Nexus.Api.Integrations; +using Nexus.Api.Domain; +using Microsoft.Extensions.Configuration; +using Xunit; + +namespace Nexus.Api.Tests; + +public class AgentServiceTests +{ + [Fact] + public async Task GetAgentsAsync_ReturnsCorrectCount() + { + var config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["AgentConfigPath"] = "/home/node/.openclaw/openclaw.json" + }) + .Build(); + var runtime = new FakeRuntime(); + var service = new AgentService(config, runtime); + + var agents = await service.GetAgentsAsync(CancellationToken.None); + Assert.True(agents.Count >= 4, $"Expected at least 4 agents, got {agents.Count}"); + } + + [Fact] + public async Task GetAgentAsync_Iris_ReturnsOrchestrator() + { + var config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["AgentConfigPath"] = "/home/node/.openclaw/openclaw.json" + }) + .Build(); + var runtime = new FakeRuntime(); + var service = new AgentService(config, runtime); + + var agent = await service.GetAgentAsync("iris", CancellationToken.None); + Assert.NotNull(agent); + Assert.Equal("Orchestrator", agent.Role); + } + + [Fact] + public async Task GetAgentAsync_Unknown_ReturnsNull() + { + var config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["AgentConfigPath"] = "/home/node/.openclaw/openclaw.json" + }) + .Build(); + var runtime = new FakeRuntime(); + var service = new AgentService(config, runtime); + + var agent = await service.GetAgentAsync("nonexistent", CancellationToken.None); + Assert.Null(agent); + } +} + +public sealed class FakeRuntime : IAgentRuntime +{ + public string Name => "FakeRuntime"; + + public Task GetStatusAsync(CancellationToken cancellationToken = default) + => Task.FromResult(new AgentRuntimeStatus( + Runtime: "OpenClaw", + Status: OperationalStatus.Online, + Latency: TimeSpan.FromMilliseconds(10), + Detail: "Fake runtime for testing")); + + public Task ChatAsync(string message, string conversationId, string agentId, CancellationToken cancellationToken = default) + => Task.FromResult(new AgentChatResult( + Runtime: "OpenClaw", + AgentId: agentId, + ConversationId: conversationId, + Content: "Echo: " + message)); +} diff --git a/backend-tests/Nexus.Api.Tests.csproj b/backend-tests/Nexus.Api.Tests.csproj new file mode 100644 index 0000000..202ccb2 --- /dev/null +++ b/backend-tests/Nexus.Api.Tests.csproj @@ -0,0 +1,24 @@ + + + + net10.0 + enable + enable + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/backend-tests/bin/Debug/net10.0/.msCoverageSourceRootsMapping_Nexus.Api.Tests b/backend-tests/bin/Debug/net10.0/.msCoverageSourceRootsMapping_Nexus.Api.Tests new file mode 100644 index 0000000000000000000000000000000000000000..19c4b730368d0493354b2c24cf41dc3374bc3975 GIT binary patch literal 490 zcmc(aF%E+;5Co^@G4=~m@B#in0s`X1iAWF>ydAQ(TaXY2`2J&*dbS?Q;M8^;q}*&&T+TA77kYJ^%m! literal 0 HcmV?d00001 diff --git a/backend-tests/bin/Debug/net10.0/HealthChecks.NpgSql.dll b/backend-tests/bin/Debug/net10.0/HealthChecks.NpgSql.dll new file mode 100755 index 0000000000000000000000000000000000000000..3c061a930b22503350ea95cccf375b6802f6954e GIT binary patch literal 21504 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 literal 0 HcmV?d00001 diff --git a/backend-tests/bin/Debug/net10.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll b/backend-tests/bin/Debug/net10.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll new file mode 100755 index 0000000000000000000000000000000000000000..d1474c007e6367dcca8cfc95515cc6a31216b6eb GIT binary patch literal 52008 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 literal 0 HcmV?d00001 diff --git a/backend-tests/bin/Debug/net10.0/Microsoft.EntityFrameworkCore.Abstractions.dll b/backend-tests/bin/Debug/net10.0/Microsoft.EntityFrameworkCore.Abstractions.dll new file mode 100755 index 0000000000000000000000000000000000000000..18fb191d198d6ad7366949119d01f78ba39b270d GIT binary patch literal 34848 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