Skip to main content
Horizons is self-hostable. The APIs and SDKs documented in the Horizons section are served by your deployment.Synth-hosted Horizons is planned, but not available today.
This page describes what you need to run Horizons and how to get to a working deployment.

Deployment modes

Horizons supports two practical starting points:
  1. Dev mode (fastest): use horizons_rs dev backends.
    • No external services required.
    • Intended for local development and integration tests.
  2. Production wiring (recommended for real deployments): build a small binary that wires horizons_core implementations together.
    • You run and operate the backing services (Postgres, Redis, S3/MinIO, Helix).
    • You choose your auth model at the edge.
As of Horizons v0.0.6, horizons_rs ships a dev server entrypoint. Production deployments typically create a tiny binary that constructs horizons_rs::server::AppState using horizons_core implementations.

Resources required (production wiring)

Horizons is a protocol and a set of traits; the concrete implementations use common infrastructure. Required for the default OSS stack:
  • Postgres: Central DB and Event Store
  • Redis: cache and Event Sync router wakeups
  • S3-compatible object store: filestore (S3 or MinIO)
  • Helix: graph store (HTTP)
  • Disk: per-project databases via local libsql (or wire a remote libsql/Turso handle if you prefer)
Optional subsystems:
  • Memory: Voyager backends (graph + vector + summarization)
  • Optimization: MIPRO batch optimization
  • Evaluation: RLM reward verification

Dev mode quickstart

From the Horizons/ repo:
cargo run -p horizons_rs -- serve --addr 0.0.0.0:8000
Then call the API with an x-org-id header:
curl -sS http://localhost:8000/health
See Horizons Quickstart for example API calls.

Production wiring walkthrough

The horizons_rs crate defines the HTTP API surface and routing. It accepts an AppState built from trait objects. You can create a tiny binary (in your own repo) that:
  • Loads OnboardConfig from env
  • Connects to Postgres, Redis, S3/MinIO, Helix
  • Boots the Event Sync bus
  • Boots Context Refresh and Core Agents
  • Serves the API via horizons_rs::server::serve

Environment variables

The default horizons_core::onboard::config::OnboardConfig::from_env() reads:
  • HORIZONS_CENTRAL_DB_URL
  • HORIZONS_CENTRAL_DB_MAX_CONNECTIONS (optional; default 10)
  • HORIZONS_CENTRAL_DB_ACQUIRE_TIMEOUT_MS (optional; default 5000)
  • HORIZONS_PROJECT_DB_ROOT_DIR (optional; default ./data/project_dbs)
  • HORIZONS_S3_BUCKET
  • HORIZONS_S3_REGION (optional; default us-east-1)
  • HORIZONS_S3_ENDPOINT (optional; use for MinIO)
  • HORIZONS_S3_ACCESS_KEY_ID
  • HORIZONS_S3_SECRET_ACCESS_KEY
  • HORIZONS_S3_PREFIX (optional)
  • HORIZONS_REDIS_URL
  • HORIZONS_REDIS_KEY_PREFIX (optional)
  • HORIZONS_HELIX_URL (optional; default http://localhost:6969)
  • HORIZONS_HELIX_API_KEY (optional)
  • HORIZONS_HELIX_TIMEOUT_MS (optional; default 10000)
The Event Sync layer also needs:
  • Postgres URL (can be the same as Central DB)
  • Redis URL (can be the same as Cache)

Minimal Docker Compose (example)

This is a minimal example for local production-like wiring. Adjust ports, credentials, and persistence.
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_USER: postgres
      POSTGRES_DB: horizons
    ports:
      - "5432:5432"

  redis:
    image: redis:7
    ports:
      - "6379:6379"

  minio:
    image: minio/minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: minio
      MINIO_ROOT_PASSWORD: minio123456
    ports:
      - "9000:9000"
      - "9001:9001"
Helix is a separate service. Run your chosen Helix deployment and set HORIZONS_HELIX_URL (and optionally HORIZONS_HELIX_API_KEY).

Wiring example (Rust)

This snippet shows the shape of a production server. You can embed it in your own binary crate.
use std::sync::Arc;

use horizons_core::context_refresh::engine::ContextRefreshEngine;
use horizons_core::core_agents::executor::CoreAgentsExecutor;
use horizons_core::engine::local_adapter::{DockerLocalAdapter, DockerLocalAdapterConfig, TokioCommandRunner};
use horizons_core::onboard::config::OnboardConfig;
use horizons_core::onboard::helix_client::HelixGraphStore;
use horizons_core::onboard::postgres::PostgresCentralDb;
use horizons_core::onboard::redis::RedisCache;
use horizons_core::onboard::s3::S3Filestore;
use horizons_core::onboard::turso::LibsqlProjectDb;
use horizons_core::onboard::traits::VectorStore;
use horizons_core::events::bus::RedisEventBus;
use horizons_core::events::config::EventSyncConfig;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let onboard = OnboardConfig::from_env()?;

    let central = Arc::new(PostgresCentralDb::connect(&onboard.postgres).await?);
    central.migrate().await?;

    let project_db = Arc::new(LibsqlProjectDb::new(central.pool().clone(), &onboard.project_db));
    let filestore = Arc::new(S3Filestore::new(&onboard.s3).await?);
    let cache = Arc::new(RedisCache::new(&onboard.redis).await?);
    let graph = Arc::new(HelixGraphStore::new(&onboard.helix)?);

    // VectorStore is application-selected. For self-hosting, wire your chosen vector backend.
    let vector: Arc<dyn VectorStore> = Arc::new(MyVectorStore::new(/* ... */)?);

    let events = Arc::new(RedisEventBus::connect(EventSyncConfig {
        postgres_url: onboard.postgres.url.clone(),
        redis_url: onboard.redis.url.clone(),
        ..Default::default()
    }, None).await?);

    let refresh = Arc::new(ContextRefreshEngine::new(horizons_core::context_refresh::traits::ContextRefreshDeps {
        central_db: central.clone(),
        project_db: project_db.clone(),
        event_bus: events.clone(),
    }));

    let rhodes = Arc::new(DockerLocalAdapter::new(
        DockerLocalAdapterConfig::default(),
        Arc::new(TokioCommandRunner),
    )?);

    let agents = Arc::new(CoreAgentsExecutor::new(central.clone(), project_db.clone(), events.clone(), rhodes));

    let state = horizons_rs::server::AppState::new(
        central,
        project_db,
        filestore,
        cache,
        graph,
        vector,
        events,
        refresh,
        agents,
    );

    horizons_rs::server::serve("0.0.0.0:8000".parse()?, state).await?;
    Ok(())
}
The example above demonstrates wiring shape and boundaries. In production you should:
  • Implement a real VectorStore for your stack.
  • Enforce auth and tenant routing at the edge (map users to x-org-id).
  • Run migrations for the Central DB and Event Store during deployment.