mcp server
Aethra
One platform to deploy, route, certify, monitor and operate your infrastructure — with an embedded MCP server so your agent can run it. Replaces Coolify + Traefik + Uptime Kuma + Beszel. .NET 10, Next
Set up
Runs on your computer. Pick your agent and operating system.
This project does not publish an install method we can turn into a config. Follow the README instead.
README
synced Oct 21, 2026Aethra
One platform to deploy, route, certify, monitor and operate your infrastructure.
A fresh install, start to finish: sign in → three pending setup items → create the first environment → the checklist drops to two. Recorded against a throwaway local instance with an empty database — the software actually running, not a mockup.
Aethra unifies — in a single system, with one shared database and one UI — what today forces you to jump between four separate tools: Git→Docker deploys (instead of Coolify), reverse proxy + automatic TLS (instead of Traefik), uptime monitoring (instead of Uptime Kuma) and VM/container metrics (instead of Beszel). The project, its public URL, its environment variables, the monitor watching it and the note holding its credentials all live in the same place — not in four places nobody keeps in sync.
Natively multi-tenant. One Template (a Git repo) can run for N clients (Client) across M
environments (Instance), each with its own variables, secrets, domain and independent deploy. One image
is built once and deployed to every client that uses it, without duplicating configuration.
Built to be operated by agents, not just by people. The embedded MCP (Model Context Protocol) server
exposes the critical operations as typed tools, and every tool response carries
next_actions: [{ tool, why, suggested_args }] — so an agent knows what to do next instead of
reverse-engineering your data model. That is the part most deploy platforms leave to luck.
Your AI can run most of this
This is not a roadmap item. Point Claude — or any MCP-capable agent — at https://aethra/mcp, give it a
scoped API key, and you operate your infrastructure by asking:
"Deploy the latest main to the staging instance of the billing template." →
aethra_list_contextto find it, thenaethra_deploy_instance_native, and it reports the real healthcheck result instead of assuming it worked.
"Which of my projects is unhealthy right now?" →
aethra_get_monitor_statusacross every monitor, grouped by project.
"Is that new VM actually reporting? What is its disk at?" →
aethra_query_metrics— CPU, RAM, disk and per-container stats coming off the satellite.
"Attach shop.acme.com to that instance." →
aethra_attach_domaincreates the Cloudflare CNAME, provisions the certificate and swaps the YARP route.
"This app needs a database." →
aethra_bind_serviceprovisions a real Postgres with its own user and password and injects the connection string as an env var and a secret.
Two design decisions make this safe enough to actually leave on:
- The agent's key cannot escalate. API keys carry granular scopes (
deployments:write,projects:read). The endpoints that mint API keys or read secrets are cookie-only, so an agent can deploy to production and still cannot grant itself anything. - Results are real, not optimistic. Deploy tools return the healthcheck outcome; metrics come from the satellite. An agent reporting "deployed successfully" is repeating what the platform observed, not narrating what it hoped.
What it does under the hood
| Capability | How it works |
|---|---|
| Git→Docker deploys | An HMAC-signed webhook triggers a Build (shallow clone → docker/podman build → push to the internal registry). On completion it fans out to N Deployments (pull → run → healthcheck → atomic swap of the YARP route). 1 build, N deployments. |
| Reverse proxy + TLS | YARP embedded in the central process. Routes live in the database; on change, IProxyConfigService.Reload() updates YARP hot. Let's Encrypt via Certes with automatic renewal (hourly worker, configurable window). |
| Multi-tenant | Auto-hostname {template}-{client}-{env}.{base-domain} when an Instance is created. Optional custom domain with a Cloudflare CNAME. Variables and secrets resolve in cascade: Instance > Client > Template > Project. |
| Monitoring | MonitorWorker runs HTTP probes on a configurable tick, each probe in its own scope so one cannot block the others. State changes emit integration events that reach SignalR (live UI) and the project timeline. |
| VM + Docker metrics | A lightweight .NET satellite connects to the central over SignalR (persistent WebSocket — outbound 443 only, no inbound ports). It reports OS-level CPU/RAM/disk/network plus per-container stats, buffering to local SQLite while the network is down and draining on reconnect. |
| Managed services | One-click Postgres/Redis/RabbitMQ from templates. A ServiceBinding provisions the real database/user/password and injects them as env vars and secrets into the apps that consume them. |
| Cloudflare DNS | HTTP client against API v4. Automatic A/CNAME records when a custom domain is attached. Token encrypted in Settings and referenced by name. |
| Notes and PinnedFacts | Markdown and images per project/template/instance. PinnedFacts (IPs, credentials, commands) surface on the main card and are encrypted at rest. |
| REST + MCP | REST with OpenAPI 3.1 and dual auth: cookies for humans, scoped API keys for programs. MCP server at https://aethra/mcp with typed tools. |
Architecture
┌────────────────────────────────────────────────────────────────┐
│ VM-Central │
│ ┌──────────────┐ ┌──────────────────────────────────────┐ │
│ │ apps/web │ │ apps/api │ │
│ │ Next.js 16 │◄──┤ ASP.NET Core (.NET 10) │ │
│ │ App Router │ │ • YARP (reverse proxy + TLS) │ │
│ └──────────────┘ │ • SignalR Hub (satellite + UI) │ │
│ │ • MCP server (tools for agents) │ │
│ │ • Background workers │ │
│ │ Build, Deployment, Monitor, │ │
│ │ CertRenewal, OutboxDispatchers │ │
│ └──────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌────────────────┐ ┌──────────────┐ │
│ │ PostgreSQL │ │ Docker daemon │ │ Internal │ │
│ │ 12 schemas, │ │ local builds │ │ registry │ │
│ │ 1 per module │ │ and services │ │ (registry:2) │ │
│ └──────────────┘ └────────────────┘ └──────────────┘ │
└────────────────────────────▲───────────────────────────────────┘
│ SignalR (wss, egress only)
┌────────────────┴─────────┬─────────────────────┐
│ VM-Satellite 1 │ VM-Satellite N │
│ apps/satellite (.NET) │ ... │
│ IContainerRuntime │ │
│ ├─ DockerContainerRt │ │
│ └─ PodmanContainerRt │ │
│ OS + container metrics │ │
└──────────────────────────┴─────────────────────┘
A modular monolith with strict boundaries. Each Modules.<X> is a bounded context with its own
PostgreSQL schema, its own DbContext, its own aggregates and its own local outbox. Cross-module
communication happens only through IIntegrationEvent in Aethra.Shared.Contracts — never by direct
reference. Violations are caught by tests/Aethra.ArchitectureTests using NetArchTest: code that crosses
a module boundary through the back door does not merge.
Why SignalR instead of pull-based agents. Beszel (WebSocket + CBOR) and Netdata (HTTP streaming replication) — the two closest references — both use agent-initiated push over a persistent connection. On Oracle Cloud and similar providers the satellites sit behind firewalls, and push only needs outbound 443. Bidirectionality comes for free: the central can send commands back down the same socket (build, run, stream logs). SignalR is the native .NET equivalent, with automatic reconnect and backoff, heartbeats and streaming.
Stack
- Backend — .NET 10, ASP.NET Core, EF Core 10, YARP, SignalR, MediatR, FluentValidation, Polly,
Docker.DotNet, Certes (ACME/Let's Encrypt), the
ModelContextProtocolSDK. - Frontend — Next.js 16 (App Router), TypeScript, Tailwind,
@microsoft/signalr. - Database — PostgreSQL 16, 12 schemas, one per bounded context (
projects,deployments,proxy,monitoring, …). - Secrets at rest — ASP.NET Data Protection with per-domain purposes (
aethra-integration-creds,aethra-webhook-secrets,aethra-cert-pfx,aethra-secrets-store, …). - Tests — NetArchTest (architectural fences), xUnit (handlers), Testcontainers (integration).
Getting started
With Docker
git clone https://github.com/Authoritt/Aethra.git
cd Aethra/deploy
cp .env.example .env # set POSTGRES_PASSWORD and AETHRA_ADMIN_PASSWORD
docker compose up -d --build
Panel on http://localhost:3000, API on http://localhost:5080. The first build compiles the .NET and Next images and is not fast.
Migrations are applied on boot because the compose sets Aethra__ApplyMigrationsOnStart=true. That is
opt-in on purpose: a managed deployment runs them from its pipeline instead, and you do not want two
instances migrating at once. /openapi/v1.json is always served and always requires an admin session or admin API key,
whatever the environment.
If the API also fronts your apps (the YARP proxy), set Panel__Hosts__0=<panel hostname>: Aethra's own routes
(/auth, /api, hubs, /mcp, webhooks, /openapi) then answer only on that host and on localhost/127.0.0.1,
and every other hostname goes straight to its app. Unset, they answer on any host (a warning is logged at boot).
/auth/login and /auth/login/totp are rate limited per client IP (Panel__LoginRateLimit__PermitLimit, default 10,
per Panel__LoginRateLimit__WindowSeconds, default 300). If the API runs in a container with its port published,
also set Panel__LocalPeers__0=<bridge gateway> (docker network inspect <network> -f '{{(index .IPAM.Config 0).Gateway}}'):
only loopback and that exact address may use Host: localhost, skip the login limiter, and send CF-Connecting-IP
(override the latter with Panel__TunnelPeers__N). Other containers on the network are not trusted.
Compose brings up four containers: the central API, the Next.js panel, Postgres, and a local image registry the build pipeline pushes to. The satellite is deliberately not one of them — it belongs on each machine you want Aethra to manage, and you install it from the UI once the central is up.
Two things worth knowing before you point this at anything real:
- The API container mounts
/var/run/docker.sock. That is what lets Aethra build and run your containers, and it is also root-equivalent access to the host. It is the same trade every Docker-based deploy tool makes, but you should make it knowingly. - Compose refuses to start without both passwords set. There is no
changemefallback.
Honesty note: this compose was wired on 2026-07-31. It is statically consistent with the Dockerfiles and with the configuration keys the code actually reads, but it has not yet been run end to end on a clean machine. If you are the first to try it, an issue either way — it worked, or here is where it broke — is the single most useful thing you can send right now.
Your first login
There is no sign-up page, and there should not be one on a box that can deploy to your production. You do not create an account — the first one is created for you.
On first boot, if the users table is empty, Aethra seeds an admin from AETHRA_ADMIN_EMAIL
and AETHRA_ADMIN_PASSWORD in your .env and gives it the admin role. Log in with those.
That is also why compose refuses to start when either is missing: no default account, nothing
guessable.
After that, everyone else gets created from Settings → Users, with roles (admin,
developer, viewer), per-endpoint scopes and optional TOTP two-factor. No curl required.
If the users table is ever empty again, login falls back to validating against those same environment variables and issues admin-equivalent claims, purely so the first session can create real users. The moment one exists in the database, that fallback stops being used.
From source
You will need .NET 10 SDK, Node 24+, and a PostgreSQL 16 you can reach.
createdb -U postgres aethra
export Identity__AdminEmail="you@example.com"
export Identity__AdminPasswordSeed="a-strong-password"
dotnet run --project apps/api # central, http://localhost:5000
cd apps/web && npm install && npm run dev # panel, http://localhost:3000
dotnet run --project apps/satellite # optional: a local satellite
If you do not set Identity__*, development falls back to admin@aethra.local / aethra-dev.
Change that before exposing Aethra to anything.
Coming from Coolify? See docs/migration-from-coolify.md and the
assisted scripts in scripts/migrate-from-coolify.{sh,ps1}.
Repository layout
apps/
api/ ASP.NET Core (central server)
web/ Next.js 16 (UI)
satellite/ Lightweight ASP.NET Core (per-VM agent)
src/
Aethra.Shared.Kernel/ primitives: Result<T>, AethraId, IClock
Aethra.Shared.Contracts/ integration events + cross-module interfaces
Aethra.Shared.Infrastructure/ MediatR pipelines, outbox base, shared persistence
Aethra.Modules.Projects/ Project, Template, Client, Instance, EnvVars, Secrets
Aethra.Modules.Deployments/ Build, Deployment, Webhooks, orchestrators
Aethra.Modules.Services/ ManagedService, ServiceBinding, provisioners
Aethra.Modules.Proxy/ Routes (YARP), Certificates (ACME)
Aethra.Modules.Vms/ Vm, SatelliteHub, registry
Aethra.Modules.Metrics/ VmMetric, ContainerMetric
Aethra.Modules.Monitoring/ Monitor, MonitorCheck
Aethra.Modules.Cloudflare/ Zone, DnsRecord
Aethra.Modules.Notes/ Note, PinnedFact, images
Aethra.Modules.Identity/ User, ApiKey, scopes
Aethra.Modules.Settings/ IntegrationCredential, BaseDomain, EnvironmentDefinition
Aethra.Modules.Mcp/ MCP server + agent tools
tests/ architecture fences, unit and integration tests
scripts/ smoke tests, Coolify migration
deploy/ docker-compose + Dockerfiles
docs/
REST API and MCP
Full REST surface with OpenAPI 3.1 at /openapi/v1.json. The critical operations are also exposed as
MCP tools at https://aethra/mcp:
aethra_list_context— aggregated snapshot (projects, VMs, services, domains).aethra_create_template/aethra_create_client/aethra_create_instance.aethra_deploy_instance_native,aethra_list_deploys,aethra_get_deploy_logs,aethra_explain_failed_deploy.aethra_attach_domain,aethra_set_env_vars,aethra_list_secrets.aethra_bind_service— provisions Postgres/Redis/RabbitMQ and injects the credentials.aethra_query_metrics,aethra_get_monitor_status,aethra_add_note.
Every response includes next_actions: [{ tool, why, suggested_args }] so the agent can propose the next
step without guessing the data model.
Security
- Dual auth: HttpOnly cookie for the human UI, scoped API keys for agents and external clients.
- Every REST endpoint requires a
<resource>:<read|write>scope. A cookie is equivalent to admin; an API key is limited to the scopes it declares. - Sensitive endpoints (
/auth/me,/auth/logout, API-key management, Settings integrations) are cookie-only — an API key cannot escalate into minting more keys or reading secrets. - HMAC SHA-256 over the raw body on Git webhooks, compared with
CryptographicOperations.FixedTimeEquals. - Webhook secrets, integration credentials, PinnedFacts and service-binding passwords are encrypted at rest with Data Protection, using separate purposes so one compromise does not expose the rest.
- Automatic TLS via Let's Encrypt; the HTTP-01 challenge is served by YARP itself.
- Architecture tests keep the Domain free of EF/ASP.NET references and stop modules importing each other's internals.
Found a vulnerability? Please read SECURITY.md — do not open a public issue.
Contributing
Contributions are welcome. CONTRIBUTING.md covers how to build it, how to run the
tests, and the two rules that keep this codebase alive: module boundaries and a pure domain layer.
By participating you agree to the Code of Conduct.
Supporting the project
Aethra is built and maintained in the open. If it saves you a VPS, an afternoon, or a subscription,
sponsoring it keeps the work going — see the Sponsor button on GitHub, or
.github/FUNDING.yml.
License
GNU Affero General Public License v3.0 — Copyright 2026 Authorit.
Use it, modify it and self-host it freely. If you run a modified Aethra as a service other people reach over a network, the AGPL asks you to publish those changes under the same license.