A self-hostable, security-first platform for building, orchestrating and governing autonomous AI agents - powered entirely by local LLMs.
Sentium is a distributed multi-agent system built on .NET 10 Aspire and a React front-end. It lets you create AI agents, compose them into multi-agent workflows, give them tools (code execution, knowledge retrieval, scheduling, memory), and run the whole thing on your own hardware against local models served by Ollama - no data ever leaves the machine.
What sets Sentium apart is that every agent action is mediated by a security control plane: a dedicated Policy Decision Point (Sentinel) authorizes each tool call, a hardened Docker sandbox isolates all code execution, and a continuous self-improvement loop lets agents learn from past runs.
- Key features
- Architecture
- Technology stack
- Getting started
- Running the tests
- Security model
- Repository layout
| Capability | Description |
|---|---|
| Conversational assistant | A streaming (SSE) chat assistant with tool use, live "thinking" traces, and human-in-the-loop approval for high-risk tool calls. |
| Custom agents | Define your own agents (name, persona/instructions, model) and manage them through the portal. |
| Multi-agent orchestration | An Orchestrator agent decomposes a goal, assembles a squad of specialist agents, runs them sequentially, then a Validator agent reviews and triggers self-correcting re-runs. |
| Visual workflow builder | Compose reusable, ordered multi-agent workflows; run them on demand and replay every run from a persisted log. |
| Retrieval-Augmented Generation (RAG) | Document ingestion (chunk → embed → store in Qdrant) across three vector collections: knowledge base, agent learnings, and user memories. |
| Self-improvement loop | Agents capture reusable learnings that are automatically, semantically recalled and injected into future runs. |
| Isolated code execution | Agents can run Python / Node.js in a heavily hardened, network-disabled Docker sandbox; output artifacts are harvested to blob storage. |
| Skills | Built-in skills plus user-defined and uploaded-Markdown skills, unlocked on demand via a load_skill mechanism. |
| Autonomous scheduling | Agents (or users) can schedule recurring background jobs (Quartz cron) that execute code through the sandbox. |
| Workspaces & files | Upload files into workspaces; they are asynchronously vectorized and made available to agents as context. |
| Security control plane (Sentinel) | Every tool call passes a Policy Decision Point that performs rate-limiting and an LLM-based semantic-intent / prompt-injection check, with a fail-closed posture and full forensic audit. |
| Health monitoring (Watchdog) | Continuous probing of all services and infrastructure, incident tracking, and a live status stream. |
| Knowledge map | An interactive, animated visualization of the vector store and semantic search traversal. |
| Centralized configuration (Registry) | Per-user and global runtime settings with a two-tier cache and cluster-wide invalidation. |
| Identity & access | Full OpenID Connect provider, cookie-based BFF authentication, a two-tier role model (Member / Sovereign), and per-user data isolation. |
Sentium is composed of seven ASP.NET Core micro-services, two React front-ends, and a set of backing infrastructure components - all orchestrated for local development by .NET Aspire.
flowchart TB
subgraph Clients
Portal["sentium-portal<br/>(React + Vite)"]
IdUI["sentium-identity-ui<br/>(login / consent)"]
end
Portal -->|httpOnly cookie| Gateway
IdUI --> Identity
subgraph Edge
Gateway["Gateway (YARP BFF)<br/>OIDC client · token refresh"]
end
Gateway -->|JWT bearer| AgentRuntime
Gateway --> Sentinel
Gateway --> Sandbox
Gateway --> Registry
Gateway --> Watchdog
Gateway --> Identity
subgraph Services
Identity["Identity<br/>(OpenIddict OIDC)"]
AgentRuntime["AgentRuntime<br/>(agents · RAG · orchestration · scheduling)"]
Sentinel["Sentinel<br/>(Policy Decision Point)"]
Sandbox["Sandbox<br/>(hardened Docker exec)"]
Registry["Registry<br/>(config)"]
Watchdog["Watchdog<br/>(health)"]
end
AgentRuntime -->|X-Internal-Token| Sentinel
AgentRuntime -->|X-Internal-Token| Sandbox
Sandbox -->|X-Internal-Token| Sentinel
subgraph Infrastructure
SQL[("SQL Server<br/>5 databases")]
Redis[("Redis<br/>HybridCache L2")]
NATS[("NATS JetStream<br/>messaging + invalidation")]
Qdrant[("Qdrant<br/>vector store")]
Ollama[("Ollama<br/>local LLMs")]
Blobs[("Azurite<br/>blob storage")]
Seq[("Seq<br/>logs")]
end
AgentRuntime --> SQL & Redis & NATS & Qdrant & Ollama & Blobs
Sentinel --> SQL & Ollama
Sandbox --> SQL & Blobs
Registry --> SQL & Redis & NATS
Watchdog --> NATS
Identity --> SQL
Detailed diagrams. This is the high-level view. Per-feature sequence diagrams (authentication, agents, workflows, RAG, scheduling, Sentinel, Sandbox, Registry, Watchdog) plus component and domain class diagrams live in docs/diagrams/.
| Service | Responsibility |
|---|---|
| Gateway | YARP-based Backend-for-Frontend. The single OIDC client; performs login/logout, transparent access-token refresh, and forwards requests to services as JWT-bearer calls. The browser only ever holds an httpOnly cookie. |
| Identity | OpenIddict OIDC/OAuth2 authorization server. Authorization-code + PKCE, refresh tokens, client credentials. Owns users, roles, profile and registration. |
| AgentRuntime | The core engine: agents, the streaming assistant, conversations, multi-agent orchestration, RAG/ingestion, learnings, skills, tools, workspaces, the knowledge map, and the Quartz scheduler. |
| Sentinel | The Policy Decision Point (PDP). Every agent action is authorized via POST /policy/evaluate through a defence-in-depth policy stack, with forensic auditing and a fail-closed default. |
| Sandbox | Executes agent-submitted code in ephemeral, security-hardened Docker containers and harvests output artifacts to blob storage. |
| Registry | Centralized, key-based runtime configuration with per-user and global scopes, EF Core JSON columns, two-tier caching and NATS-broadcast invalidation. |
| Watchdog | Periodically probes every service and infrastructure dependency, raises/resolves incidents, and streams live health over SSE. |
Every service follows the same clean-architecture layering:
{Service}.Api → HTTP controllers, validation, middleware
{Service}.Application → use cases, orchestration, background workers
{Service}.Core → domain entities, interfaces, DTOs
{Service}.Infrastructure → EF Core, external clients, persistence
- Authentication - the Gateway BFF is the only OIDC client; the front-end is fully cookie-based and never touches tokens.
- Internal service-to-service calls are authenticated with a pre-shared
X-Internal-Tokenheader (theSystemCallerpolicy), bypassing the user-facing PDP for trusted background work. - Per-user data isolation - user-scoped tables carry a
UserIdand EF Core global query filters scope every query automatically; theSovereignrole and background tasks bypass the filter through an explicit accessor. - Async messaging over NATS JetStream - workflow execution (
workflow.*), real-time streaming (stream.*), and config invalidation (registry.settings.invalidated). - Caching via HybridCache (L1 in-process + L2 Redis), invalidated cluster-wide over NATS.
- Resilience - a global Polly pipeline (retry + timeout + circuit-breaker) is applied to all HttpClients.
Backend
- .NET 10 / C# · ASP.NET Core
- .NET Aspire (orchestration & service discovery)
Microsoft.Extensions.AI+Microsoft.Agents.AI(agent framework) over Ollama- Entity Framework Core (SQL Server)
- YARP (reverse proxy / BFF) · OpenIddict (OIDC server)
- NATS JetStream · Redis (HybridCache) · Quartz.NET (scheduling)
- Qdrant (vector store) · Docker.DotNet (sandbox)
- FluentValidation · Polly · Serilog → Seq
Models (default, all local via Ollama)
- Chat / reasoning: Gemma (
gemma4:e4bby default; Qwen3 and others selectable) - Embeddings: nomic-embed-text (768-dimensional)
Frontend
- React 19 + TypeScript + Vite (
sentium-portal,sentium-identity-ui) - TanStack Query v5 (server state) · Zustand v5 (client/streaming state)
- SCSS modules · pnpm
Testing
- xUnit v3 · Testcontainers ·
Aspire.Hosting.Testing(backend) - Vitest (frontend) · Playwright (E2E)
Full installation guide: docs/installation.md covers all three ways to run Sentium - .NET Aspire (recommended for development), Docker Compose (self-hosted deployment), and front-end only - with prerequisites, configuration reference, GPU acceleration, platform notes, and troubleshooting. The quick paths below get you started.
- .NET 10 SDK
- pnpm (11.x)
- Docker - required for the code-execution sandbox, Testcontainers, and the Aspire-managed infrastructure containers
- An NVIDIA GPU is recommended for usable local-LLM latency (the AppHost requests GPU support for Ollama), but not strictly required
The Aspire AppHost is the single entry point. It starts all services, both front-ends, and every infrastructure dependency (SQL Server, Redis, NATS, Qdrant, Ollama, Seq, Azurite), and automatically pulls the default Ollama models on first run.
dotnet run --project src/aspire/Sentium.AppHost/Sentium.AppHost.csprojThen open the Aspire dashboard (the URL is printed on startup) to see every resource, its logs, and its endpoints. From there:
- Portal -
http://localhost:5173 - Identity UI -
http://localhost:5174
First start takes a while: Docker images and the Ollama models are downloaded. Subsequent starts reuse the persisted data volumes.
If you'd rather self-host the whole platform with plain Docker (no .NET SDK on the host), a complete docker-compose.yml builds every service image and starts all infrastructure. It wires the services together with the same connection strings and service-discovery keys that Aspire injects.
Prerequisites: Docker (with the Compose plugin) and the .NET SDK only for the one-time dev-certificate step below.
# 1. Configure secrets and ports
cp .env.example .env # then edit the secrets in .env
# 2. Generate the Gateway's TLS dev certificate (needed for the secure BFF auth cookie),
# using the same password you set for CERT_PASSWORD in .env, and trust it locally:
mkdir certs # dotnet dev-certs does not create the target directory
dotnet dev-certs https -ep certs/sentium.pfx -p <CERT_PASSWORD>
dotnet dev-certs https --trust
# 3. Build and start everything
docker compose up --buildOnce the stack is healthy:
- Portal -
http://localhost:5173 - Gateway (API/BFF) -
https://localhost:8443 - Identity (OIDC + login UI) -
http://localhost:8081 - Seq (logs) -
http://localhost:8090, Qdrant -http://localhost:6333/dashboard
Services apply their EF Core migrations automatically on first start, and a one-shot ollama-init container pulls the chat + embedding models into a persistent volume.
Notes & caveats
- Trust model / OIDC. The Identity server is the canonical OIDC issuer and must be reachable under the same URL from both your browser and the back-end containers. Compose uses
host.docker.internalfor this, which works out of the box on Docker Desktop. On native Linux, thehost.docker.internal:host-gatewaymapping is included, but you must also let your own browser resolve it (e.g. add127.0.0.1 host.docker.internalto/etc/hosts). - HTTPS for the auth cookie. The Gateway terminates TLS (step 2) so the BFF cookie can be issued as
Secure/SameSite=Noneacross the portal origin. This mirrors how the Aspire setup runs over HTTPS. - Code sandbox. The Sandbox mounts the host Docker socket and a job directory that must share an identical path on host and container (
SANDBOX_JOBS_DIR). This works on Linux/macOS and the WSL2 backend; Windows users should point it at a path inside WSL2. - GPU. Local inference is slow on CPU. Uncomment the NVIDIA
deployblock on theollamaservice (requires the NVIDIA Container Toolkit) for GPU acceleration.
The Aspire AppHost remains the recommended path for day-to-day development (richer dashboard, hot reload, no cert/issuer setup). Docker Compose targets self-hosted deployment and wider adoption.
cd src/clients/sentium-portal
pnpm install
pnpm dev # Vite dev server on :5173
pnpm lint
pnpm buildEF Core migrations are per-service. To add one:
dotnet ef migrations add <Name> \
--project src/services/<Service>/<Service>.Infrastructure \
--startup-project src/services/<Service>/<Service>.ApiBackend (xUnit v3)
dotnet test # everything
dotnet test tests/Sentium.Tests.Unit/Sentium.Tests.Unit.csproj # unit
dotnet test tests/Sentium.Tests.Integration/... # integration (needs Docker)Frontend (Vitest)
cd src/clients/sentium-portal
pnpm test:run
pnpm test:coverageEnd-to-end (Playwright) - boots the full Aspire stack in a dedicated Testing mode with isolated databases and seeded baseline data:
cd e2e
pnpm test # headless
pnpm test:ui # visual modeSentium is designed to be self-hosted - typically by a single operator on a single host (or a single private network), with all AI inference running locally via Ollama. This deployment model is what justifies several of the security choices below, so it is worth making the trust boundary explicit:
- The Gateway is the only public surface. The browser talks exclusively to the Gateway BFF over an
HttpOnlycookie. The back-end services communicate with each other on the internal/orchestrated network and are not intended to be exposed to the public internet. - Inside that boundary, services authenticate to each other with a pre-shared internal token (
X-Internal-Token, theSystemCallerpolicy). For a single-operator, single-host deployment this is a right-sized, low-friction choice: there is no untrusted co-tenant on the internal network, and it avoids forcing operators to provision per-service certificates, a PKI, or a service mesh. The internal token acts as defense-in-depth on top of network isolation, not as the sole control. - The internal token is generated per-installation as a secret (managed as an Aspire secret parameter) - it is not a shipped default. Operators should treat it like any other deployment secret and rotate it if it is ever exposed.
- Out of scope for the default posture: multi-tenant hosting on a shared/hostile network, where transport-level mutual identity (mTLS), per-service OAuth2 client-credential tokens, or a service mesh would be the appropriate next step. The Identity server already supports the
client_credentialsgrant, so per-service token identities are a natural future upgrade path if the deployment model changes.
Security is a first-class concern, enforced at several layers:
- Authentication - OpenID Connect (authorization-code + PKCE) via the Identity server. The Gateway BFF holds tokens server-side; the browser only receives a
Secure,HttpOnlycookie. - Authorization - a two-tier role model (
Member,Sovereign). Privileged operations (user/role management, model pull/delete, global settings, audit log) requireSovereign. - Data isolation - every user-scoped entity is filtered by
UserIdthrough EF Core global query filters; cache keys and vector searches are user-scoped. - Policy Decision Point (Sentinel) - every agent tool call is authorized before execution. The policy stack performs sliding-window rate limiting and an LLM-based semantic-intent check that compares the attempted action against the user's original prompt to catch prompt injection and agent hallucination. The engine is fail-closed: any error denies the action, and every decision is written to a forensic audit log.
- Human-in-the-loop - high-risk tools (e.g. code execution) pause the stream and require explicit user approval before running.
- Sandbox isolation - code runs in Docker containers with networking disabled, a read-only root filesystem, all Linux capabilities dropped,
no-new-privileges, a non-root user, a seccomp profile,noexectmpfs, and hard CPU / memory / PID / file-descriptor limits, plus an execution timeout. - Internal API hardening - service-to-service endpoints (PDP evaluation, sandbox execution) require a pre-shared internal token and are never exposed to the browser.
src/
aspire/Sentium.AppHost/ # Aspire orchestration - the single entry point
services/
AgentRuntime/ # core engine (agents, RAG, orchestration, scheduling)
Gateway/ # YARP BFF
Identity/ # OpenIddict OIDC server
Registry/ # centralized configuration
Sandbox/ # hardened Docker code execution
Sentinel/ # Policy Decision Point
Watchdog/ # health monitoring
clients/
sentium-portal/ # main React app
sentium-identity-ui/ # login / consent UI
shared/ # shared infrastructure, service defaults, constants
tests/
Sentium.Tests.Unit/
Sentium.Tests.Integration/
Sentium.Tests.AppHost/
e2e/ # Playwright end-to-end suite
docs/ # architecture docs & diagrams
Sentium - All AI inference runs locally; no external AI provider is required.