Architecture
Omnitron is decomposed into a handful of long-lived objects that all live inside a single daemon process. Operators talk to the daemon over Netron RPC; the daemon spawns and watches child processes; child processes report metrics, logs, and health back through structured pipes.
This page describes those pieces in enough detail to debug an incident at 2 AM.
Component map
The three planes
Omnitron exposes three independent transports — same Netron RPC surface, different network shape.
1. Management plane — unix://~/.omnitron/daemon.sock
Unix domain socket, file mode 0o600 (owner-only). The trust
boundary: if you can open the socket, you've already passed the
OS-level identity check. CLI calls auth-bypass through this
socket for ergonomics.
| Carries | Used by |
|---|---|
| All built-in RPC services | omnitron CLI |
The OmnitronDaemon service | Webapp dev (when running locally) |
| MCP server requests | Agent processes spawned by the CLI |
2. Public TCP plane — tcp://0.0.0.0:9700
Opt-in. Disabled by default; enable when:
- You run a fleet (other daemons connect for cluster membership).
- You operate
omnitron remote(alias-addressed daemons). - You expose a programmatic API to an external CI/CD.
JWT is required on this plane. RBAC roles
(viewer / operator / admin) gate every method.
3. HTTP / WS plane — http://0.0.0.0:9800
Opt-in. The daemon's HTTP plane is the Netron HTTP + WebSocket
bridge that browser clients use. With the default httpPort
of 9800, the daemon binds the HTTP bridge on httpPort + 1
(:9801) and the WebSocket transport on httpPort + 2
(:9802); the public :9800 is fronted by the omnitron-nginx
container, which serves the static bundle and reverse-proxies
/netron/* and /ws to those daemon ports.
JWT is required for RPC, except for static asset routes (served
by nginx) and the unauthenticated signIn / validateToken /
refreshSession methods.
Daemon lifecycle
The state-store is the persistent intent — what should be
running. On crash + restart, the daemon reads state.json and
relaunches anything that was alive at last write.
State store — state.json
A single JSON file capturing:
interface DaemonState {
apps: Record<string, {
name: string;
status: 'starting' | 'running' | 'stopped' | 'crashed';
pid?: number;
startedAt?: number;
lastError?: string;
restarts: number;
processes: Record<string, ChildProcessState>;
}>;
cluster?: {
role: 'master' | 'follower';
leader?: string;
term: number;
};
}
Persisted on every status transition, atomic-rename style
(write tmp → rename → fsync parent). If the file is corrupt at
boot, the daemon starts fresh — empty apps map — and logs a
warning.
PID manager
Owns ~/.omnitron/daemon.pid. Responsibilities:
- Lock acquisition at boot: open exclusive, write
process.pid. If lock fails, another daemon is running — abort. - Liveness sweep: periodically check whether the lock-holding PID is still alive; if not, reclaim the lock.
- Atomic ownership transfer during
cluster.step-down— the outgoing leader explicitly releases.
Child process PIDs are owned by the orchestrator (per IAppHandle),
not the PID manager.
Daemon scheduler
A small in-process scheduler bound to the daemon's lifecycle. Runs periodic tasks:
| Task | Default interval |
|---|---|
| Health probe sweep across all apps | 15 s |
| Metrics aggregation tick | 5 s |
| State persistence flush | on transition + 30 s baseline |
| Crash-loop backoff timer | per-app, exponential |
| Cluster heartbeat (if cluster.enabled) | 2 s |
| Health-monitor sweep (cluster nodes) | 60 s |
All scheduler timers are .unref()-ed so they don't block
shutdown.
Orchestrator subsystem
Owns the per-app launch pipeline. One AppHandle per running
app:
Two launch modes:
- Classic launcher —
node bootstrap.jsis forked once; bootstrap is responsible for creating allApplications and running its own subprocess management. Used for legacy apps. - Module-worker spawner — one fork per
IProcessEntry. Each child imports a single module file and runsApplication.createdirectly. Default for new apps; lower memory, faster boot.
→ Full pipeline: Orchestrator.
Built-in RPC services
A master daemon registers 21 Netron services at boot (22 when
cluster.enabled). A slave daemon registers a reduced core set —
the PostgreSQL-backed services (OmnitronAuth, OmnitronAlerts,
OmnitronTelemetry, OmnitronFleet, OmnitronDiscovery,
OmnitronPipelines, OmnitronTraces, OmnitronDeploy,
OmnitronNodes) are master-only. All services share
authentication / authorization with the OmnitronDaemon service.
The first column is the Netron service name (the identifier
clients resolve, e.g. daemon.OmnitronAuth.signIn(...)).
| Service | Purpose |
|---|---|
OmnitronDaemon | App lifecycle — start / stop / restart / status / inspect |
OmnitronAuth | JWT issue / verify / RBAC |
OmnitronSecrets | Encrypted secret CRUD |
OmnitronInfra | Docker container management (Postgres / Redis / etc.) |
OmnitronDeploy | Deployment workflows |
OmnitronFleet | Cross-node fleet operations |
OmnitronPipelines | CI/CD pipeline runs |
OmnitronBackups | Database backup / restore |
OmnitronProject | Project + stack registry |
OmnitronKubernetes | k8s integration (apply / scale / observe) |
OmnitronNodes | Infrastructure node inventory (node-manager) |
OmnitronLogs | Per-app log streaming + filtering |
OmnitronMetrics | Metrics aggregation (from titan-metrics) |
OmnitronTraces | Distributed trace ingestion |
OmnitronTelemetry | Telemetry-relay (titan-telemetry-relay) aggregator |
OmnitronHealth | Active health-check runs |
OmnitronDiscovery | Service discovery state |
OmnitronEvents | Cross-process event bus (WebSocket subscriptions) |
OmnitronAlerts | Alert rules + delivery |
OmnitronSync | Cross-daemon state synchronisation |
OmnitronSystemInfo | Host CPU / RAM / disk inventory |
OmnitronCluster | Leader election (only when cluster.enabled) |
→ Full reference: Services reference.
Infrastructure subsystem
When an app's omnitronConfig.infrastructure declares a
requirement, the daemon's infrastructure subsystem:
- Checks if the requirement is already satisfied (running container, registered bare-metal service).
- Resolves connection parameters (host / port / credentials).
- Provisions if missing — Docker for dev/test, bare-metal hooks for prod.
- Injects resolved env vars (
DATABASE_URL,REDIS_URL, …) into the app at startup.
→ Reference: omnitron infra status shows current containers;
the Infra CLI section covers commands.
Cluster subsystem
When cluster.enabled: true in daemon config, multiple daemons
form a cluster:
Election parameters:
| Parameter | Default |
|---|---|
discovery | 'redis' (or 'consul' / 'static') |
electionTimeout | 5–15 s (jittered) |
heartbeatInterval | 2 s |
Cluster operations are exposed via the cluster CLI command
group — see CLI Cluster section.
Webapp host
The React console is served by a dedicated omnitron-nginx
container — not by the daemon process itself. The daemon's
HTTP plane carries only the Netron RPC bridge; nginx serves the
static bundle and proxies RPC to the daemon:
| Mode | Served by | Command |
|---|---|---|
| Production | omnitron-nginx container fronting apps/omnitron/webapp/dist/ | omnitron webapp build then omnitron webapp start |
| Dev | Vite dev server with HMR | pnpm dev in apps/omnitron/webapp/ |
nginx (:9800) proxies /netron/* to the daemon's HTTP listener
(httpPort + 1, default :9801) and /ws to the daemon's Netron
WebSocket transport (httpPort + 2, default :9802). In dev mode
Vite serves the console on :9810 and proxies the same two paths to
the same daemon ports, so the browser asks for /netron/* and /ws
on its own origin either way and the client hard-codes neither. See
Console for the full port map.
Auth model — three roles
The OmnitronDaemon service and others use role-based access:
| Role | What they can do |
|---|---|
viewer | Read-only: list / status / inspect / metrics / health / logs |
operator | Viewer + lifecycle: start / stop / restart / reload / scale / exec |
admin | Operator + destructive: shutdown / reloadConfig / setMetricsEnabled |
The local Unix socket bypasses auth — local CLI calls run as the
implicit admin (OS-level trust). TCP/HTTP planes always require
JWT.
Data flow — log shipping
Logs flow through the daemon — no separate log shipper. The
ring buffer means omnitron logs --follow returns recent entries
immediately even if the file has been rotated.
Data flow — metrics aggregation
Apps push metrics to the daemon via Netron — the daemon stores and aggregates centrally. The webapp reads the same store.
Data flow — leader election (cluster mode)
Step-down is graceful: omnitron cluster step-down releases the
lock and demotes self before the next election.
Putting it together
Read these in order for a complete picture:
- Daemon — the always-on process; its internals.
- Orchestrator — how apps launch.
- Services reference — every RPC method.
- CLI — the operator's daily interface.
- Console — the web UI.