Skip to content

Deploy Horizon

Horizon is the central intelligence hub. This guide covers deploying the API server, connecting databases, and registering Synapse sensors.

Architecture

Environment Configuration

Create a .env file for the Horizon API. All variables are set via environment.

Server

VariableDefaultDescription
NODE_ENVdevelopmentproduction for deployed environments
PORT3100HTTP listener port
HOST0.0.0.0Bind address
LOG_LEVELinfotrace, debug, info, warn, error

Database

VariableDefaultRequiredDescription
DATABASE_URLYesPostgreSQL connection string
CLICKHOUSE_ENABLEDfalseNoEnable ClickHouse for historical queries
CLICKHOUSE_HOSTlocalhostNoClickHouse hostname
CLICKHOUSE_HTTP_PORT8123NoClickHouse HTTP port
CLICKHOUSE_DBsignal_horizonNoDatabase name
CLICKHOUSE_USERdefaultNoUsername
CLICKHOUSE_PASSWORDNoPassword
CLICKHOUSE_COMPRESSIONtrueNoEnable compression
CLICKHOUSE_MAX_CONNECTIONS10NoConnection pool size

WebSocket

VariableDefaultDescription
WS_SENSOR_PATH/ws/sensorsSensor ingestion endpoint
WS_DASHBOARD_PATH/ws/dashboardDashboard push endpoint
WS_HEARTBEAT_INTERVAL_MS30000Heartbeat interval
WS_MAX_SENSOR_CONNECTIONS1000Max concurrent sensors
WS_MAX_DASHBOARD_CONNECTIONS100Max concurrent dashboards

Signal Processing

VariableDefaultDescription
SIGNAL_BATCH_SIZE100Signals per aggregation batch
SIGNAL_BATCH_TIMEOUT_MS5000Max wait before flushing
BLOCKLIST_PUSH_DELAY_MS50Delay before broadcasting blocklist updates
BLOCKLIST_CACHE_SIZE100000In-memory blocklist capacity

Security

VariableDefaultDescription
API_KEY_HEADERX-API-KeyHeader name for API key auth
CORS_ORIGINSComma-separated allowed origins
CONFIG_ENCRYPTION_KEYEncryption key for sensitive config fields
TELEMETRY_JWT_SECRETJWT secret for sensor telemetry auth

Database Setup

PostgreSQL

Run Prisma migrations to create the schema:

sh
cd apps/signal-horizon/api
pnpm prisma migrate deploy

ClickHouse (Optional)

Apply the ClickHouse schema if enabled:

sh
clickhouse-client --host localhost \
  --query "$(cat apps/signal-horizon/clickhouse/schema.sql)"

Key tables: signal_events, campaign_timeline, signal_hourly_mv (materialized view).

Starting the Server

Native

sh
cd apps/signal-horizon/api
NODE_ENV=production node dist/server.js

With PM2 for process management:

sh
pm2 start dist/server.js --name horizon -i max

Docker

See the Docker guide for containerized deployment.

Registering Sensors

Create a sensor via the REST API:

sh
curl -X POST https://horizon.example.com/api/v1/fleet/sensors \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "US East Primary", "region": "us-east-1"}'

The response includes a sensor ID and authentication token. Configure the Synapse instance to connect:

yaml
# In Synapse config.yaml
telemetry:
  enabled: true
  horizon_url: "wss://horizon.example.com/ws/sensors"
  sensor_id: "sensor-abc123"
  token: "sensor-token-xyz789"

Health Endpoints

EndpointDescription
GET /healthBasic health check
GET /health/readyReadiness probe (DB connections verified)
GET /health/liveLiveness probe

Monitoring

Enable Prometheus metrics:

sh
METRICS_ENABLED=true
METRICS_PORT=9090

Key metrics: signal_horizon_sensors_total, signal_horizon_ws_connections, signal_horizon_query_duration_seconds.

High Availability

  • PostgreSQL: Use streaming replication with a read replica
  • Horizon API: Scale horizontally behind a load balancer with sticky sessions (WebSocket affinity)
  • Redis: Required for shared state across multiple API instances
  • ClickHouse: Use ReplicatedMergeTree for data durability

Licensed under AGPL-3.0 · atlascrew.dev