Introduction & Philosophy
Controller98 is in active experimental pre-release development. It is NOT a stable production release. Built strictly for homelabs, enthusiast experimentation, and self-hosted testing environments. Do NOT deploy in commercial, enterprise, or mission-critical production infrastructure. Interfaces, database schemas, and CLI commands are subject to breaking changes prior to v1.0. Notice: Tested exclusively on Ubuntu 26.04 so far.
Controller98 is an open-source infrastructure and ingress management platform. It combines the aesthetic charm and rapid keyboard-friendly ergonomics of the iconic Windows 98 desktop with high-performance modern server-side Go engineering.
- Zero Client-Side JavaScript: The UI renders entirely via server-side Go HTML templates (SSR) styled with pure CSS (
98.css). Browsers download zero megabytes of JS bundles. - Extreme Resource Efficiency: Consistently idles at approximately 10 to 12 MiB of RAM, guaranteeing operation on low-spec VPS, Raspberry Pis, and dense Kubernetes nodes.
- Unified Heterogeneous Control: A single pane of glass for local Docker sockets, Compose stacks, Kubernetes clusters, and remote bare-metal servers.
- Zero-Dependency Ingress: Native ECDSA TLS engine, Cloudflare Tunnels, ngrok, and ACME without requiring NGINX, Traefik, or Caddy.
System Architecture
Controller98 is compiled into a single static binary containing the web server, SQLite database layer, Docker API client, Kubernetes client, tunnel orchestrator, and Model Context Protocol (MCP) server:
| Component | Implementation | Purpose |
|---|---|---|
retro-infra-manager |
Pure Go (net/http, database/sql) | Primary web server, SSR desktop engine, API router, and ingress controller. |
retro-mcp |
Pure Go JSON-RPC 2.0 | Model Context Protocol server for AI coding assistants (Claude, Cursor, AGY). |
retro-entrypoint |
Pure Go Sub-process Proxy | Lightweight container entrypoint wrapper providing automatic TLS & health probes. |
internal/tunnel |
Go crypto/tls + Sub-process Runners | Automated Self-Signed ECDSA TLS, Cloudflare, ngrok, and Let's Encrypt manager. |
internal/db |
modernc.org/sqlite (Pure Go CGO-free) | Zero-dependency embedded database for RBAC, sessions, notes, tokens, and audit logs. |
Resource Optimization (< 20 MB Target)
Most modern DevOps dashboards (e.g., Portainer, Rancher, Lens) are built as heavy Single-Page Applications (SPAs) requiring hundreds of megabytes of RAM just to idle. Controller98 achieves its strict < 20 MB RAM target through:
- Zero-Allocation SSR: HTML is streamed directly to the socket without holding large in-memory DOM representations.
- Pure Go SQLite: No external database daemon (PostgreSQL/MySQL) running in the background.
- On-Demand Tunnel Monitoring: Child processes (cloudflared/ngrok) are executed concurrently with non-blocking pipe readers and minimal buffer allocations.
- GC Optimization: Goroutine pools and low object allocation keep Go runtime garbage collection pauses under 1 millisecond.
Installation & Deployment
Docker CLI Quickstart
The fastest way to deploy Controller98 is with the official Docker Hub image:
docker run -d \
--name controller98 \
--restart unless-stopped \
--network host \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ~/.kube/config:/root/.kube/config:ro \
-v c98-data:/app/data \
rafaeltre/controller98:latest
Using --network host allows Controller98 to bind to port 8080 (Web UI), port 443 (HTTPS Ingress), and port 80 (HTTP-01 ACME challenges) without complex port forwarding rules.
Note: Controller98 has been tested only on Ubuntu 26.04 so far. Although multi-platform container images (linux/amd64 and linux/arm64) are distributed, bare metal operations, socket mappings, and hardware metrics have exclusively been verified against Ubuntu 26.04 LTS environments.
Docker Compose Setup
For persistent production setups, save the following as docker-compose.yml:
services:
controller98:
image: rafaeltre/controller98:latest
container_name: controller98
restart: unless-stopped
network_mode: "host"
environment:
- PORT=8080
- DATA_DIR=/app/data
- DOCKER_HOST=unix:///var/run/docker.sock
- KUBECONFIG=/root/.kube/config
volumes:
- controller-data:/app/data
- /var/run/docker.sock:/var/run/docker.sock
- ~/.kube/config:/root/.kube/config:ro
volumes:
controller-data:
Launch the stack with:
docker compose up -d
Kubernetes & Helm Deployment
To deploy Controller98 directly into a Kubernetes cluster, use the following manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller98
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: controller98
template:
metadata:
labels:
app: controller98
spec:
hostNetwork: true
containers:
- name: controller98
image: rafaeltre/controller98:latest
env:
- name: PORT
value: "8080"
- name: KUBERNETES_SERVICE_HOST
value: "127.0.0.1"
volumeMounts:
- name: docker-sock
mountPath: /var/run/docker.sock
- name: data-volume
mountPath: /app/data
volumes:
- name: docker-sock
hostPath:
path: /var/run/docker.sock
- name: data-volume
persistentVolumeClaim:
claimName: controller98-pvc
Storage & Data Persistence
Controller98 stores all operational data in /app/data:
/app/data/retro.db: SQLite database storing users, password hashes, settings, notes, and audit logs./app/data/certs/: Cached SSL/TLS certificates (Let's Encrypt ACME and Self-Signed ECDSA pairs)./app/data/stacks/: Docker Compose YAML stack definitions.
Docker Engine & Compose Management
Container Lifecycle Control
Controller98 interacts with the Docker Engine through the native Unix domain socket at /var/run/docker.sock without invoking the Docker CLI as a subprocess for API operations:
- Start: Starts an existing stopped container.
- Stop: Gracefully stops a running container (SIGTERM followed by SIGKILL).
- Pause / Unpause: Freezes container cgroups without terminating processes.
- Restart: Restarts the container with minimal downtime.
- Delete: Removes stopped containers from the host.
- Live Logs: Opens real-time terminal output with automatic ANSI escape code filtering.
Image Management & Layer Inspection
The Images tab provides full registry pull capabilities and local image maintenance.
POST /docker/images/pull HTTP/1.1
Content-Type: application/x-www-form-urlencoded
image=busybox:latest&node=local
Images can also be deleted directly from the web table to reclaim host disk space.
Compose Studio & YAML Stacks
Controller98 includes an integrated Compose Studio that combines visual stack management with raw YAML editing:
- Stack Editor: Write standard Compose specifications with syntax checking.
- Save & Deploy: Saves stack YAML to SQLite and immediately executes
docker compose up -d. - Active Projects Auto-Detection: Discovers Compose projects already running on the host via Docker label inspection.
- Teardown: Shuts down deployed stacks with
docker compose down.
Kubernetes & Helm Orchestration
Cluster Connection & Mounting
Connect to local or remote Kubernetes clusters by mounting your kubeconfig file (~/.kube/config). Controller98 provides real-time state visualization and workload management:
Pods & Real-Time Streaming Logs
Inspect Pod health states (Running, Pending, CrashLoopBackOff), examine restart counts, and view streaming stdout/stderr logs with zero browser memory overhead.
Deployments & Replica Scaling
Scale replica counts with instant slider controls, trigger zero-downtime rollout restarts, and inspect revision history.
POST /k8s/deploy/scale HTTP/1.1
Content-Type: application/x-www-form-urlencoded
namespace=default&name=web-api&replicas=3&node=local
Triggering Rollout Restart (POST /k8s/deploy/restart) prompts Kubernetes to update the deployment pod template, cycling pods without downtime.
Services, Nodes & Helm Releases
Inspect cluster networking (ClusterIP, NodePort, LoadBalancer), host node resource allocations, and view active Helm charts installed via Helm v3.
Network Neighborhood (Heterogeneous Clustering)
Multi-Node Architecture
Controller98 introduces the Network Neighborhood: an ultra-lightweight multi-node clustering protocol designed for heterogeneous hardware (e.g. cloud VPS paired with local Raspberry Pis and bare-metal servers).
Installing Remote Agent Daemons
On any remote machine you wish to manage, run the standalone agent daemon:
docker run -d \
--name c98-agent \
--restart unless-stopped \
--network host \
-e MODE=agent \
-e AGENT_PORT=8081 \
-e AGENT_TOKEN=my-secure-cluster-token \
-v /var/run/docker.sock:/var/run/docker.sock \
rafaeltre/controller98:latest
Then, open Controller98 → Network Neighborhood → Add Remote Node, specify the remote IP/FQDN and token, and click Add Node. Controller98 will continuously probe latency and allow you to switch contexts between nodes instantly.
Ingress Gateway & HTTPS Tunnels
Gateway Architecture
Controller98 includes a high-performance built-in ingress proxy and tunnel orchestrator. It supports four automated HTTPS modes with zero external reverse proxies:
Pure Go Self-Signed TLS (Port 443)
Generates ECDSA P-256 certificates dynamically in memory on port 443 with Subject Alternative Names (SANs) for localhost and local network IP addresses.
- Generates an elliptic curve ECDSA P-256 certificate with Subject Alternative Names (SANs) for
localhost, the host IP, and your custom domain. - Listens on port 443 with HTTP/2 and TLS 1.3 support.
- Automatically proxies incoming HTTPS traffic to Controller98 and configured sub-routes.
Cloudflare Tunnels (Quick & Token)
The embedded cloudflared runner allows exposing your server to the global internet without opening firewall ports or configuring port forwarding:
- Quick Tunnel (Zero Key): Leave the token field blank. Controller98 requests an account-less tunnel and automatically captures the assigned
https://*.trycloudflare.comURL. - Named Tunnel (Token): Enter your Cloudflare Zero Trust tunnel token (
eyJh...) to bind to your pre-configured Cloudflare DNS domain.
ngrok Public Tunnels
Enter your ngrok authtoken in the web UI. Controller98 executes ngrok http 8080 --authtoken <token>, establishes an encrypted session with the ngrok cloud gateway, and displays the public https://*.ngrok-free.dev URL directly in your retro desktop status bar.
Automated Let's Encrypt (ACME HTTP-01)
Enter your domain name (e.g., infra.mycompany.com) and email. Controller98 automatically initiates an ACME HTTP-01 challenge on port 80, obtains signed Let's Encrypt certificates, caches them in /app/data/certs/, and handles renewals automatically.
5. Multi-Service Ingress Routing Table
Expose multiple services and containers behind a single secure HTTPS domain or tunnel:
| Path Prefix | Target Upstream | Example Service |
|---|---|---|
/ (Default) |
http://127.0.0.1:8080 |
Controller98 Web Desktop |
/grafana |
http://127.0.0.1:3000 |
Grafana Monitoring Dashboards |
/ollama |
http://127.0.0.1:11434 |
Ollama Local LLM API |
/open-webui |
http://127.0.0.1:8081 |
Open-WebUI LLM Interface |
6. Retro Entrypoint Assistant
The included retro-entrypoint executable can wrap around any arbitrary container or pod to instantly inject automatic TLS, public tunnels, and Kubernetes readiness/liveness probes.
Security, Authentication & RBAC
Role-Based Access Control (RBAC)
Controller98 enforces defense-in-depth principles: Argon2id password hashing, constant-time token verification, sliding-window IP rate limiting, strict CSRF validation, and granular Personal Access Tokens (PATs).
- Admin: Full permissions. Can create/delete users, generate API tokens, deploy Compose stacks, scale K8s workloads, and modify tunnel settings.
- Operator: Operational access. Can start/stop/restart containers, scale deployments, and view logs, but cannot create users or delete security tokens.
- Viewer: Read-only access. Can inspect dashboards, container lists, and metrics, but all mutating POST actions are blocked.
CLI Password Reset & Disaster Recovery
If you forget your administrator password or lose access, reset credentials directly using the CLI inside the container:
# Reset admin password
docker exec retro-infra-manager retro-infra-manager reset-password NewPassword123!
# Reset a specific user
docker exec retro-infra-manager retro-infra-manager reset-password operator1 NewPassword123!
# Reset all users to re-trigger the /setup wizard
docker exec retro-infra-manager retro-infra-manager reset-admin
Personal Access Tokens (PATs)
Generate scoped API tokens for automated scripts and MCP AI clients with granular permissions (*, read, docker, k8s, admin).
REST API Reference
Authenticate requests with the Authorization: Bearer <token> header:
curl -H "Authorization: Bearer c98_pat_..." http://localhost:8080/api/v1/status
Common endpoints:
GET /api/v1/status: Host CPU, RAM, disk, Docker, and K8s availability.GET /api/v1/docker/containers: List all containers with status and IDs.POST /api/v1/docker/container/action: Trigger start, stop, restart, pause.GET /api/v1/k8s/pods: List active cluster pods.
Model Context Protocol (MCP) AI Integration
MCP Protocol Overview
Controller98 exposes infrastructure automation tools to modern AI assistants (Claude Desktop, Cursor, AGY) through the Model Context Protocol (MCP).
Stdio Mode (retro-mcp)
The standalone retro-mcp binary implements standard JSON-RPC 2.0 over standard input/output:
{
"mcpServers": {
"controller98": {
"command": "docker",
"args": [
"exec",
"-i",
"retro-infra-manager",
"retro-mcp"
]
}
}
}
HTTP Server-Sent Events (/mcp/sse)
Remote agents can connect to http://localhost:8080/mcp/sse using Bearer token authentication.
Supported AI Tools
docker_list_containers: Inspect all containers, states, and ports.docker_container_action: Start, stop, restart, or pause containers.k8s_list_pods: List cluster workloads.k8s_scale_deployment: Autoscale replicas dynamically based on AI reasoning.tunnel_get_status: Check public tunnel URLs and ingress routing.
Operations, Diagnostics & FAQ
Diagnostics & Activity Audit Logs
Controller98 records all administrative actions in the Task Manager → Audit Logs tab. Every container restart, image pull, user login, and deployment change is timestamped and recorded with username and outcome.
FAQ & Troubleshooting
Windows 98 pioneered high-density, no-nonsense desktop ergonomics. By utilizing standard HTML tables, bevels, and native inputs with 98.css, we achieve an ultra-intuitive dashboard that loads in milliseconds with zero JavaScript overhead.
Ensure /var/run/docker.sock is accessible by the container user (UID 0 root by default). On Linux systems with SELinux, append :z to the volume mount: -v /var/run/docker.sock:/var/run/docker.sock:z.
Yes! Choose Custom TLS or Let's Encrypt in the Tunnels & Ingress configuration tab to use your own domain and certificates.