sudoX · deploy
Running it, nothing exposed.
Ten minutes on a laptop to see the whole loop, and the shape of the first real deployment: one instance, one tunnel, no inbound rule anywhere.
On a laptop
One container. Sign in with the gateway secret, connect a database, send an alert, and watch it become an analysis, a plan and — once you approve it — an executed fix with a runbook drafted from the result.
docker run -d --name sudox -p 3000:3000 \
-e GATEWAY_SECRET=$(openssl rand -hex 32) \
-e AGENT_ENABLE=true \
-e AGENT_AI_ENABLE=true \
-e AGENT_AI_PROVIDER=openai \
-e AGENT_AI_API_KEY=sk-... \
ghcr.io/oguzhankarahan/sudox-ai-platform:latest
Ollama works the same way with no API key: AGENT_AI_PROVIDER=ollama AGENT_AI_MODEL=qwen3:8b. From source, build the console first (cd ui && npm install && npm run build) and then go build -o sudox ./cmd. Then open http://localhost:3000.
1
Connect something
The Connections page in the console, or a preset in mcp_servers.yaml. A credential is tested before it is stored.
2
Send an alert
POST /api/incidents with any JSON and an API token that may send alerts (an integration token can do only that). It is persisted before anyone is notified, and shows up on Now.
3
Run the loop
Analyze → plan → approve → execute → verify. The impact report counts only what was measured.
The first real deployment
One EC2 instance running the container, Postgres on RDS beside it, nothing exposed to the internet, and Cloudflare Tunnel + Access in front as the only way in. The instance role is the AWS credential: no access key is created, stored or rotated for the AWS side. Other accounts are reached by assuming a role from it; GCP is reached through workload identity federation with an AWS provider — a credential that never exists as a file.
Why a single VM and not ECS or EKS. sudoX is one long-running process that spawns subprocesses — every MCP server is one — and must never sleep. A VM is the natural shape for that, it is the cheapest, and it is the easiest to debug for the first weeks. The Helm chart is there for when a second instance is needed.
Why not Cloudflare Workers or Containers for the product itself. The same reason: a process that forks child processes and holds a warm MCP cache does not fit a platform that scales to zero. Cloudflare's role is the front door. This site and the console share one hostname behind it — the site at /, the console at /console — and Access is scoped to the console path.
The runbook with every command, the IAM policies, the compose file and the corrections found by running it — an instance with no egress, a first boot that never wrote its unit, a region that never reached the container — is in the repository under deploy/ec2/.
One hostname, two doors
/
This site: public, cached
The files in site/, served by the same binary as the console and cached at Cloudflare's edge. No build step, no sign-in.
/console
The console: Access, then sudoX
The product, on the same hostname, behind Cloudflare Access scoped to this path. "Open the console" is a relative link; the sign-in screen links back here.
What the account has to grant
| For | AWS | GCP |
|---|---|---|
| discovery | describe/list on EC2, RDS, ELB, S3, Lambda — the compiled-in probe list, read-only | roles/viewer on the project |
| logs | logs:FilterLogEvents, and logs:DescribeLogGroups so sources can be derived rather than guessed | roles/logging.viewer |
| metrics | cloudwatch:GetMetricData | roles/monitoring.viewer |
| cost | ce:GetCostAndUsage (Cost Explorer is a single endpoint in us-east-1) | a BigQuery billing export, named in the console — GCP publishes its bill nowhere else |
| acting | only what an approved plan's tools are allowed; the passthrough's verb allowlist is compiled in | the same |
Where a permission is missing, sudoX says which one rather than guessing around it, because a source built on a guess fails on every tick, and the console fills with errors about a thing nobody asked for. Take one away and read what it says.
Try it
Take a permission away, read what it says.
in the console Connections (the test before storing) · Watching ("on, but not running") · Cost ("not measured") · Infrastructure (a failed probe is a gap)
Each sentence names the grant, in the provider's own vocabulary, and says what the product will not do in its absence — it will not guess a log group name, it will not render a bill as zero, it will not draw an unreadable cache tier as an empty one. The rule behind all of them is the fourth principle on the front page: a gap is shown as a gap.