Skip to main content

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.

CarriesUsed by
All built-in RPC servicesomnitron CLI
The OmnitronDaemon serviceWebapp dev (when running locally)
MCP server requestsAgent 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:

TaskDefault interval
Health probe sweep across all apps15 s
Metrics aggregation tick5 s
State persistence flushon transition + 30 s baseline
Crash-loop backoff timerper-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 launchernode bootstrap.js is forked once; bootstrap is responsible for creating all Applications 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 runs Application.create directly. 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(...)).

ServicePurpose
OmnitronDaemonApp lifecycle — start / stop / restart / status / inspect
OmnitronAuthJWT issue / verify / RBAC
OmnitronSecretsEncrypted secret CRUD
OmnitronInfraDocker container management (Postgres / Redis / etc.)
OmnitronDeployDeployment workflows
OmnitronFleetCross-node fleet operations
OmnitronPipelinesCI/CD pipeline runs
OmnitronBackupsDatabase backup / restore
OmnitronProjectProject + stack registry
OmnitronKubernetesk8s integration (apply / scale / observe)
OmnitronNodesInfrastructure node inventory (node-manager)
OmnitronLogsPer-app log streaming + filtering
OmnitronMetricsMetrics aggregation (from titan-metrics)
OmnitronTracesDistributed trace ingestion
OmnitronTelemetryTelemetry-relay (titan-telemetry-relay) aggregator
OmnitronHealthActive health-check runs
OmnitronDiscoveryService discovery state
OmnitronEventsCross-process event bus (WebSocket subscriptions)
OmnitronAlertsAlert rules + delivery
OmnitronSyncCross-daemon state synchronisation
OmnitronSystemInfoHost CPU / RAM / disk inventory
OmnitronClusterLeader 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:

  1. Checks if the requirement is already satisfied (running container, registered bare-metal service).
  2. Resolves connection parameters (host / port / credentials).
  3. Provisions if missing — Docker for dev/test, bare-metal hooks for prod.
  4. 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:

ParameterDefault
discovery'redis' (or 'consul' / 'static')
electionTimeout5–15 s (jittered)
heartbeatInterval2 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:

ModeServed byCommand
Productionomnitron-nginx container fronting apps/omnitron/webapp/dist/omnitron webapp build then omnitron webapp start
DevVite dev server with HMRpnpm 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:

RoleWhat they can do
viewerRead-only: list / status / inspect / metrics / health / logs
operatorViewer + lifecycle: start / stop / restart / reload / scale / exec
adminOperator + 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:

  1. Daemon — the always-on process; its internals.
  2. Orchestrator — how apps launch.
  3. Services reference — every RPC method.
  4. CLI — the operator's daily interface.
  5. Console — the web UI.