Skip to content

Cluster Manifest Reference

The cluster manifest is the YAML file the CLI reads to provision hosts, services, and interfaces.

version: v1
type: cluster
profile: production
channel: stable # optional: stable (default), candidate, or legacy rc
root_domain: example.com # optional: used for Caddy routing and cookie domains
env_files:
- config/production.env
- secrets/production.env
hosts_file: clusters/production/hosts.enc.yaml # optional: SOPS-encrypted host inventory
bootstrap_overlay: gitops/clusters/production/bootstrap.yaml # optional desired-state overlay
hosts: {}
clusters: {}
wireguard: {}
infrastructure: {}
services: {}
interfaces: {}
observability: {}
geoip: {}
tls_bundles: {}
ingress_sites: {}
webhooks: {} # optional: destination policy for tenant webhooks

Manifest schema version (required).

Example:

version: v1

Cluster manifest type (required).

Values:

  • cluster - Full multi-service deployment.
  • edge - Single edge node deployment (use the dedicated edge manifest format for multi-node edge fleets; see below).

Example:

type: cluster

Environment/profile name. This is used to form the default cluster ID during provisioning ({type}-{profile}), so choose a stable slug (e.g., production, eu-1).

Common values: development, staging, production.

Example:

profile: production

Release channel to track (optional). Defaults to stable.

Values: stable (latest production-ready release), candidate (newest release, including an RC), and legacy rc (newest RC only). Use candidate for staging and release-like development environments.

Example:

channel: candidate

Paths to shared environment files for all services (optional). Relative to the manifest directory. Files are loaded in order. Shared env files override auto-generated environment variables, but are overridden by per-service env_file and inline config entries.

Example:

env_files:
- config/production.env
- secrets/production.env

Base domain used by Caddy routing, public interface metadata, and production cookie-domain derivation.

Example:

root_domain: example.com

Optional path to a SOPS-encrypted host inventory. When set, frameworks cluster provision merges sensitive host fields such as external_ip, user, and WireGuard private-key material from this file before validation.

Example:

hosts_file: clusters/production/hosts.enc.yaml

Optional path to a bootstrap desired-state overlay. The CLI renders manifest-derived bootstrap state and then merges this overlay for operator-authored tenants, billing, cluster pricing, and service registry overrides that do not belong directly in the manifest.

Example:

bootstrap_overlay: gitops/clusters/production/bootstrap.yaml

Operator-declared commodore.pull_streams and commodore.mist_native_streams in the overlay declare where their source may run with source_location:

commodore:
pull_streams:
- playback_id: warehouse-cam
owner_tenant: { ref: quartermaster.tenants[acme] }
title: Warehouse Camera
source_uri_ref: { sops: gitops/secrets/acme.env, key: WAREHOUSE_CAM_URI }
enabled: true
source_location:
clusters: [warehouse-edge] # cluster keys from this manifest
nodes: [warehouse-edge-1] # optional: host keys inside a listed cluster
avoid_nodes: [warehouse-edge-3] # optional: hosts the source must never run on
mist_native_streams:
- playback_id: demo
owner_tenant: { ref: quartermaster.system_tenant }
# source, source_kind, always_on, ...
source_location:
clusters: [media-eu-1] # exactly one cluster
  • Omit source_location to let a public pull source run on any media cluster. A private or multicast source_uri requires clusters, each with allow_private_pull_sources: true.
  • nodes and avoid_nodes require clusters; every entry must be a host of a listed cluster, and a host cannot appear in both. Listing nodes narrows only that host’s cluster.
  • mist_native_streams require exactly one cluster.
  • The previous allowed_cluster_ids key is rejected with an error naming source_location. Replace allowed_cluster_ids: [a, b] with source_location: { clusters: [a, b] }.

commodore bootstrap writes each location as the stream’s own ingest placement rules and reports stream source_locations: updated=N noop=N. Streams declared in the overlay take their location from the overlay: a change made to such a stream’s ingest rules through the API is replaced on the next bootstrap run. A location that lists nodes or avoid_nodes needs every media cell serving the stream’s owner to support node placement: until then commodore bootstrap (and its --dry-run) exits with an error before writing anything. Upgrade Foghorn in every cell, then rerun bootstrap. See media placement.


Map of host definitions for SSH access and mesh identity. The CLI connects to each host via SSH using external_ip; internal service traffic uses wireguard_ip or .internal names after Privateer starts.

Format:

hosts:
<hostname>:
external_ip: <public-ip>
user: <ssh-user>
cluster: <cluster-id>
wireguard_ip: <mesh-ip>
wireguard_public_key: <base64-public-key>
wireguard_port: 51820
roles: [infrastructure, services] # optional
labels: # optional
environment: prod

Fields:

  • external_ip (required unless provided by hosts_file) - Public IP address used for SSH/bootstrap and WireGuard endpoint dialing
  • user (required unless provided by hosts_file) - SSH username
  • cluster (optional) - Explicit cluster membership for this host. When one cluster is declared, hosts implicitly belong to it
  • wireguard_ip (required for Privateer-covered hosts) - GitOps-owned internal mesh address
  • wireguard_public_key (required for Privateer-covered hosts) - Public half of the host mesh identity
  • wireguard_port (required for Privateer-covered hosts) - Host WireGuard UDP listen port
  • wireguard_private_key_file (optional) - On-disk private-key path for adopted-local nodes
  • wireguard_private_key_managed (optional) - Set to false for adopted-local nodes whose key is preserved on disk
  • roles (optional) - Arbitrary tags used for human grouping
  • labels (optional) - Free-form metadata

Map of cluster definitions for multi-cluster deployments. Each key is the cluster ID that will be registered in Quartermaster.

Format:

clusters:
prod-platform:
name: Production Platform
type: central
owner_tenant: frameworks
roles: [control, data, analytics, support, infra, mesh, interface, observability]
prod-media:
name: Production Media Plane
type: edge
cell: media-eu
class: platform
control_cell: media-eu
eligible_serving_cells: [media-eu]
default: true
platform_official: true
allow_private_pull_sources: false
owner_tenant: frameworks
region: us-east
roles: [media]
pricing:
model: tier_inherit
required_tier_level: 0
allow_free_tier: true

Fields:

  • name (required) - Display name for the cluster
  • type (required) - Cluster type: central, edge, etc.
  • cell (optional) - Stable media-cell identity. Defaults to the cluster ID.
  • class (optional) - Placement class used when grouping compatible media cells.
  • control_cell (optional) - Foghorn cell that owns this cluster’s control-plane projection. Defaults to cell.
  • eligible_serving_cells (optional) - Explicit Foghorn cells allowed to receive and serve this cluster’s signed media authority. Defaults to [control_cell]. Every listed cell must contain an enabled Foghorn deployment; production rendering fails instead of silently omitting an unknown cell.
  • region (optional) - Region identifier
  • roles (optional) - Service roles that belong to this cluster. Used for automatic cluster resolution when services don’t have an explicit cluster field.
  • default (optional) - Mark as the default cluster. New tenants auto-subscribe to this cluster on signup. At most one cluster may have default: true.
  • platform_official (optional) - Mark as a platform-operated cluster. Used by billing tier access and unauthenticated service discovery.
  • allow_private_pull_sources (optional) - Capability flag allowing pull-input streams on this media cluster to use private literal source addresses such as RFC1918, ULA, or non-link-local multicast. Keep this false for platform-managed clusters and enable it only on self-hosted edges that can reach those sources. This does not place a pull stream by itself; a private source must also restrict its source location to consenting clusters (source_location.clusters in the bootstrap overlay, sourceLocation with mode RESTRICTED through the API).
  • public_topology (optional) - Show this cluster on public map and coverage views.
  • env_files (optional) - Env files merged into every cluster-scoped service replica for this cluster, after the top-level env_files and before per-service env_file. Use them for per-cluster credentials such as region-specific STORAGE_S3_* access keys. Paths are relative to the manifest directory; absolute paths are rejected. SOPS-encrypted files are decrypted during provisioning.
  • s3_bucket, s3_endpoint, s3_region, s3_prefix (optional) - Artifact storage descriptor reconciled into the Quartermaster cluster row. When s3_bucket is set, s3_prefix must be set explicitly (use s3_prefix: "" for no prefix). Access keys stay in env files. See Media storage.
  • owner_tenant (optional) - Owning tenant. Use frameworks for the system tenant, or a tenant UUID.
  • pricing (optional) - Purser cluster pricing config. When present, provision reconciles it authoritatively. When absent, existing pricing is left untouched.
    • model (required) - free_unmetered, metered, monthly, tier_inherit, or custom
    • required_tier_level (optional) - Minimum billing tier level (0-5)
    • allow_free_tier (optional) - Allow free-tier tenants
    • default_quotas (optional) - Cluster pricing metadata for marketplace/self-host offers. Platform Free-tier stream/viewer caps are billing-tier entitlements, not static cluster capacity.

Role-based resolution: When a service doesn’t specify cluster, the CLI matches the service’s role (from the built-in service registry) against each cluster’s roles list. For example, foghorn has role media, so it resolves to whichever cluster lists media in its roles. Available roles: control, data, analytics, media, support, infra, mesh, interface, observability.

Resolution priority:

  1. Explicit cluster field on the service
  2. Role match against cluster roles
  3. If only one cluster is defined, use it
  4. Auto-generated {type}-{profile} when no clusters section is declared

WireGuard mesh configuration for backend nodes managed by Privateer (api_mesh). Production/staging managed clusters keep this block in GitOps and generate per-host identity with frameworks mesh wg generate.

Format:

wireguard:
enabled: true
mesh_cidr: 10.88.0.0/16
listen_port: 51820

Fields:

  • enabled (required)
  • mesh_cidr (required when enabled) - CIDR used by generated host wireguard_ip values
  • listen_port (required when enabled) - Default UDP listen port used by mesh wg generate

Edge nodes do not join the mesh.

Destination policy for every tenant-supplied webhook the platform dials: outbound event webhooks (Bosun) and playback-auth webhooks (Commodore validates the URL, Foghorn calls it).

Format:

webhooks:
allow_private_destinations: true

Fields:

  • allow_private_destinations (default false) - lets webhook receivers use private (RFC 1918 / ULA) addresses, plain http, and .local host names, for an isolated staging or development cluster whose receivers live on its own network. It never opens the platform itself: loopback, link-local, cloud metadata, *.internal mesh names, the service host’s own addresses, and addresses routed into the WireGuard mesh stay blocked. The CLI renders it as BOSUN_ALLOW_PRIVATE_DESTINATIONS on Bosun and PLAYBACK_WEBHOOK_ALLOW_PRIVATE_DESTINATIONS on Commodore and Foghorn; an env file or a service’s config map still overrides it. Never enable it on a cluster that serves untrusted tenants.

Database and messaging infrastructure.

Version Reference: See config in the monorepo for tested/recommended versions.

PostgreSQL or YugabyteDB configuration.

Format:

infrastructure:
postgres:
enabled: true
engine: postgres
mode: native
version: "15.14"
host: central-01
port: 5432
databases:
- name: quartermaster
owner: quartermaster_user
runtime_role: quartermaster_runtime

Fields:

  • enabled (required)
  • engine (optional) - postgres by default, or yugabyte
  • mode (required) - native
  • version (required)
  • host (required for single-node Postgres) - Host name from hosts
  • nodes (required for multi-node YugabyteDB) - List of {host, id, rpc_port} entries
  • instances (optional) - Additional named vanilla PostgreSQL instances. Each entry takes name, host, and optional port, version, mode, password, databases (same shape as below), tuning, and config
  • port (required)
  • replication_factor (optional) - Defaults to the number of nodes
  • databases (optional) - Each entry declares name and its schema/migration owner. Optional runtime_role opts service DSNs into the separately provisioned least-privilege login. It authenticates with DATABASE_RUNTIME_PASSWORD, never the owner credential. Provision the role, verify it with cluster doctor --deep, then enable the application cutover. Runtime secrets resolve in this order: POSTGRES_<DATABASE>_RUNTIME_PASSWORD, the equivalent owner-name alias, a named Postgres instance override, the matching cluster’s DATABASE_RUNTIME_PASSWORD, then the shared DATABASE_RUNTIME_PASSWORD. The same resolution feeds provisioning and service DSNs, and provisioning rejects owner/runtime credential collisions. On YugabyteDB, table placement is declared in the repository rather than here: provisioning creates each platform database with its colocated layout, and existing databases move with frameworks cluster yugabyte relayout.
  • tuning (optional) - key/value overrides for server tuning
  • sql_access (optional) - direct by default, or ssh
  • password (optional) - Database password override

ClickHouse analytics database.

Format:

infrastructure:
clickhouse:
enabled: true
mode: native
version: "25.9.2.1"
nodes:
- host: central-01
id: 1
port: 9000
databases: [periscope]

Fields:

  • enabled (required)
  • mode (required) - native
  • version (required)
  • nodes (required, at least one) - Replicated cluster nodes as {host, id} entries. host is a key from hosts; id (1 or higher) drives task names, the Keeper server ID, and the replica name. Every node uses the cluster-level port. The removed host field is rejected with a message pointing to nodes.
  • read_endpoint (optional) - Host key that periscope-query uses instead of nodes
  • write_endpoint (optional) - Host key that periscope-ingest and periscope-metering use instead of nodes. Both endpoint overrides exist for moving the ClickHouse node.
  • port (required)
  • databases (optional)
  • sql_access (optional) - direct by default, or ssh

Redis or Valkey cache instances. Use named instances when services need separate logical caches.

Format:

infrastructure:
redis:
enabled: true
engine: valkey
mode: native
version: "8.1"
instances:
- name: foghorn
host: central-01
port: 6379
- name: chatwoot
engine: redis
host: central-01
port: 6380

Fields:

  • enabled (required)
  • engine (optional) - Default engine for instances, valkey or redis
  • mode (required) - docker or native
  • version (optional)
  • instances (required)
    • name (required)
    • engine (optional) - Instance-level engine override
    • mode (optional) - single (default) or sentinel for a primary with replicas and a Sentinel quorum
    • host (required) - Host name from hosts; the initial master in sentinel mode
    • port (required)
    • password (optional)
    • cluster (optional) - Limit the instance to consumers in this cluster; empty applies globally
    • master_name (optional) - Sentinel master name; defaults to name
    • replica_hosts (optional) - Sentinel replica hosts, excluding host
    • sentinels (optional) - Sentinel quorum members as {host, port} entries (port defaults to 26379); consumers connect through these
    • config (optional) - key/value overrides such as maxmemory or appendonly

Apache Kafka message broker. Kafka native mode is KRaft-only.

Two modes are supported:

  • Dedicated controllers (recommended for production): Add a controllers list. Controllers run the metadata quorum as separate processes; brokers handle client traffic only. Controllers are provisioned first, then brokers join via dynamic discovery.
  • Combined mode (suitable for dev): Omit controllers. Each broker runs both broker and controller roles in a single process.

Production format (dedicated controllers):

infrastructure:
kafka:
enabled: true
mode: native
version: "4.2.0"
cluster_id: "iwNix2BLQny7Z9tlyna7Qw"
min_insync_replicas: 2
controllers:
- host: central-01
id: 100
port: 9093
dir_id: "2uBL_MXZSAe-9agF6p5aMQ"
- host: media-01
id: 101
port: 9093
dir_id: "2AyDvMsASN26Tk7joXgJ6Q"
- host: media-02
id: 102
port: 9093
dir_id: "ZTJH65OoRGqcxUYwht1RSw"
brokers:
- host: central-01
id: 1
port: 9092
- host: media-01
id: 2
port: 9092
- host: media-02
id: 3
port: 9092
topics:
- name: analytics_events
partitions: 10
replication_factor: 3
config:
retention.ms: "604800000" # 7 days
- name: domain.events
partitions: 12
replication_factor: 3
config:
retention.ms: "15552000000" # 180 days
cleanup.policy: delete
# ...plus every other platform topic in the table below

Fields:

  • enabled (required)

  • mode (required) - native

  • version (required) - Apache Kafka version (4.x+)

  • cluster_id (required) - KRaft cluster UUID. Generate with kafka-storage.sh random-uuid

  • region_id (optional) - Region of this Kafka cluster, for example eu-west. Empty infers it from broker host labels

  • role (optional) - aggregator or regional. The top-level cluster defaults to aggregator

  • regional (optional) - Additional Kafka clusters in other regions. Each entry is an independent KRaft deployment with region_id (required), role (default regional), cluster_id, and the same controllers, controller_port, brokers, topics, and replication settings as the top level

  • mirrormaker (optional) - MirrorMaker2 links between Kafka regions; nothing is mirrored when absent or disabled

    • enabled (required)
    • mode (optional) - native (default)
    • heap_opts (optional) - JVM heap; default -Xmx1G -Xms1G
    • replicas (optional) - Replication factor for mirrored topics on the target; defaults to the target broker count
    • task_count (optional) - Default tasks.max for links that do not set one
    • links (required when enabled) - One entry per ordered pair of regions, each with source and target region IDs, hosts (worker hosts, all in the target region), optional topics (empty uses the canonical set for that direction), and optional task_count
  • controllers (optional) - dedicated controller nodes. If present, requires at least 3. Controller IDs must not overlap with broker IDs

    • host (required) - host name from hosts map
    • id (required) - controller node ID
    • port (optional) - controller listener port, default 9093
    • dir_id (required) - directory UUID for dynamic quorum bootstrap. Generate with kafka-storage.sh random-uuid
  • controller_port (optional) - combined mode only, default 9093

  • brokers (required) - at least 1; use 3+ for production

  • topics (required) - every topic must set config.retention.ms. Each Kafka cluster must declare the platform topics below: the aggregator cluster all of them, and each regional cluster all except lookout.incidents. The platform topics have fixed settings, and the CLI refuses a manifest that omits one or whose values differ, printing the entry to add:

    Topicretention.msOther required settings
    analytics_events604800000 (7 days)
    service_events15552000000 (180 days)
    analytics.raw_mist_triggers2592000000 (30 days)
    billing.usage_reports31536000000 (365 days)
    decklog_events_dlq7776000000 (90 days)
    lookout.incidents604800000 (7 days)
    domain.events15552000000 (180 days)partitions: 12, replication_factor: 3 (or the broker count if lower), cleanup.policy: delete

    Provisioning creates missing topics and applies the declared config to topics that already exist, without restarting brokers.

  • min_insync_replicas (optional)

  • delete_topic_enable (optional)

  • offsets_topic_replication_factor (optional)

  • transaction_state_log_replication_factor (optional)

  • transaction_state_log_min_isr (optional)

All three sections share the same shape. Use them to define application services, public interfaces (reverse proxy + web surfaces), and observability stack (VictoriaMetrics, vmagent, Grafana, etc.).

Format:

services:
commodore:
enabled: true
mode: docker
version: v0.0.1
image: livepeerframeworks/frameworks-commodore:latest
host: central-01
port: 18001
grpc_port: 19001
env_file: /etc/frameworks/commodore.env
depends_on: [postgres, kafka]
config:
EXAMPLE_KEY: value

Fields:

  • enabled (required)
  • mode (required) - docker or native
  • version (optional) - Version or channel to track
  • image (optional) - Docker image (docker mode)
  • binary_url (optional) - Binary download URL (native mode)
  • deploy (optional) - Override deploy slug (container/binary name)
  • cluster (optional) - Explicit cluster assignment. Must reference a key in clusters. When omitted, the service’s role determines its cluster automatically
  • clusters (optional) - List form of cluster for cluster-scoped media services that serve several clusters. Cannot be combined with cluster. Foghorn and Chandler deployments must declare exactly one cluster; use one service entry per cell
  • host (optional) - Single host
  • hosts (optional) - Multiple hosts for replicas. The CLI creates one task per host (e.g., bridge@regional-eu-1). Commodore, Quartermaster, Purser and Periscope-query keep serving when one replica host is lost: their callers spread requests over every healthy replica that <service>.internal resolves to, and Privateer is given every Quartermaster address and fails over between them. Run each of them on at least two hosts in the aggregator region for control-plane redundancy; the hosts may belong to different clusters, because every caller resolves every replica
  • replicas (optional) - Replica count
  • port (optional)
  • grpc_port (optional)
  • env_file (optional) - Per-service env file (overrides the top-level env_files)
  • depends_on (optional)
  • public (optional) - Marks the service as having a public-facing endpoint
  • update_strategy (optional) - Rollout override for cluster apply: max_unavailable, canary, region_stagger, primary_last
  • config (optional) - Inline key/value overrides (highest priority)

Environment variable merge order (later wins):

  1. Auto-generated from manifest infrastructure (DATABASE_URL, KAFKA_BROKERS, QUARTERMASTER_GRPC_ADDR, etc.)
  2. Top-level env_files
  3. Per-service env_file
  4. Inline config map

The merged result is written to /etc/frameworks/{service}.env on each host.

For profile: production, provisioning also forces secure internal gRPC defaults:

  • BUILD_ENV=production
  • GRPC_ALLOW_INSECURE=false
  • GIN_MODE=release when not explicitly set or when set to debug

If navigator is enabled in a production manifest, the shared env_files must also provide:

  • one complete managed CA source for Navigator:
  • NAVIGATOR_INTERNAL_CA_ROOT_CERT_FILE
  • NAVIGATOR_INTERNAL_CA_INTERMEDIATE_CERT_FILE
  • NAVIGATOR_INTERNAL_CA_INTERMEDIATE_KEY_FILE
  • or:
  • NAVIGATOR_INTERNAL_CA_ROOT_CERT_PEM_B64
  • NAVIGATOR_INTERNAL_CA_INTERMEDIATE_CERT_PEM_B64
  • NAVIGATOR_INTERNAL_CA_INTERMEDIATE_KEY_PEM_B64

For multi-node edge fleets, use a dedicated edge manifest.

Format:

version: v1
channel: stable
root_domain: example.com
pool_domain: edge.media-eu.example.com
cluster_id: regional-production
cluster_manifest: cluster.yaml
enrollment_token: fw_edge_...
hosts_file: clusters/edge/hosts.enc.yaml
mode: container
capabilities: [ingest, edge, storage, processing]
bandwidth_mbps: 2000
max_transcodes: 4
storage_capacity_bytes: 500000000000
nodes:
- name: edge-us-east-1
subdomain: edge-us-east-1
region: us-east
# Private control/telemetry routing while retaining public certificate names.
foghorn_addr: 10.20.0.11:18029,10.20.0.12:18029,10.20.0.13:18029
foghorn_tls_server_name: foghorn.media-eu.example.com
telemetry_address: 10.20.0.11
apply_tune: true
register_qm: true

SSH auth for edge nodes resolves from the operator’s machine. Pass --ssh-key on the frameworks edge provision command when an explicit key is needed.

Fields:

  • channel (optional) - stable, candidate, legacy rc, or an explicit version such as v0.2.0-rc3
  • root_domain or pool_domain (at least one required)
  • email (ACME)
  • cluster_id (required when register_qm: true)
  • cluster_manifest (optional) - Cluster manifest used to load platform SERVICE_TOKEN for register_qm; relative paths resolve from the edge manifest directory. Can also be passed as --cluster-manifest.
  • enrollment_token (optional) - Token used for edge bootstrap
  • hosts_file (optional) - SOPS-encrypted edge host inventory
  • fetch_cert (deprecated) - Ignored for edge manifests; enrolled edge TLS is delivered by Foghorn ConfigSeed
  • mode (optional) - Default node mode: container (default; single edge image) or native. docker is a deprecated alias for container.
  • capabilities (optional) - Default enabled Helmsman capabilities: ingest, edge, storage, processing. Empty means all capabilities enabled.
  • bandwidth_mbps (optional) - Default node bandwidth limit advertised by Helmsman and reconciled into MistServer bwlimit.
  • max_transcodes (optional) - Default local transcoding concurrency limit reported by Helmsman.
  • storage_capacity_bytes (optional) - Default local storage capacity reported by Helmsman.
  • nodes (required)

Edge telemetry ingress is control-plane managed. Enrolled edge nodes receive remote-write URL and auth material from Foghorn during preregistration and subsequent ConfigSeed updates. For a private staging network, telemetry_address pins only the address lookup; vmagent retains the URL hostname for HTTP routing, TLS SNI, and certificate verification.

Node fields:

  • name (required)
  • ssh (required unless provided by hosts_file) - user@host
  • subdomain (optional)
  • region (optional)
  • cluster (optional) - Per-node cluster override; useful when one edge manifest spans multiple media cells.
  • foghorn_addr (optional) - Private or otherwise explicit Foghorn gRPC endpoints: one host:port, or a comma-separated list. List every Foghorn instance of the node’s cell; Helmsman moves to the next one at once when an instance refuses, drops, or announces it is going away. Without it the node gets foghorn.<cluster>.<root_domain>:18029, which resolves to every Foghorn instance of the cell; Helmsman resolves it before every dial and tries each address.
  • foghorn_tls_server_name (optional) - Certificate name to verify when the foghorn_addr entries are IPs or private aliases.
  • telemetry_address (optional) - Private IP for the control-plane-derived telemetry hostname. This does not disable TLS verification or change the HTTP hostname.
  • apply_tune (optional)
  • register_qm (optional)
  • mode (optional) - Per-node override, container or native (docker is a deprecated alias for container)
  • capabilities (optional) - Per-node override for enabled capabilities
  • bandwidth_mbps (optional) - Per-node bandwidth override
  • max_transcodes (optional) - Per-node transcode limit override
  • storage_capacity_bytes (optional) - Per-node storage capacity override
  • labels (optional)

The CLI validates:

  • Required fields (version, type, hosts)
  • Host references used by Postgres, ClickHouse, Kafka, services, interfaces, and observability entries
  • If clusters is present, each entry must have name and type
  • If a service sets cluster, it must reference a defined cluster key
  • Cluster IDs (map keys) must be non-empty strings

Unknown keys are rejected by the parser. Keep manifests minimal and aligned with the schema above so typos and removed fields fail loudly.

Instead of managing manifests locally, you can store them in a private GitHub repository and fetch them at provision time using GitHub App credentials.

Credentials are written to the canonical CLI config ($XDG_CONFIG_HOME/frameworks/config.yaml by default; run frameworks config path --kind config to confirm):

Terminal window
frameworks config set github.app-id 12345
frameworks config set github.installation-id 67890
frameworks config set github.private-key /path/to/app-private-key.pem
frameworks config set github.repo your-org/infra-repo
frameworks config set github.ref main # optional, defaults to main

For CI or one-shot automation, the same values can be passed via environment variables: FRAMEWORKS_GITHUB_APP_ID, FRAMEWORKS_GITHUB_INSTALLATION_ID, FRAMEWORKS_GITHUB_PRIVATE_KEY, FRAMEWORKS_GITHUB_REPO, FRAMEWORKS_GITHUB_REF. Env values beat the stored config; per-invocation flags beat both.

Terminal window
# Provision using the remote manifest (cluster name required)
frameworks cluster provision --github-repo your-org/infra-repo --cluster production
# Override credentials via flags
frameworks cluster provision \
--github-repo your-org/infra-repo \
--cluster production \
--github-app-id 12345 \
--github-installation-id 67890 \
--github-private-key /path/to/key.pem

The CLI fetches clusters/<cluster>/cluster.yaml from the repo, along with all referenced env_files, per-service env_file paths, and hosts_file. The standard provision pipeline then runs with the fetched data inside a tempdir that’s cleaned up when the command exits.

KeyDescription
github.app-idGitHub App ID
github.installation-idGitHub App Installation ID
github.private-keyPath to PEM-encoded private key
github.repoRepository in owner/repo format
github.refGit ref (branch/tag), defaults to main