Configuration reference

Every hopskip-server setting, its environment variable, and its default.

Every hopskip-server setting. Each one has an environment variable (the canonical name) and a matching key in the optional TOML config file passed with --config. An environment variable always wins over the file.

Validate a deployment’s configuration without starting it:

hopskip-server check-config --config /etc/hopskip/server.toml

Server

Config keyEnvironment variableTypeDefaultDescription
server.bindHOPSKIP_SERVER_BINDhost:port127.0.0.1:50051gRPC bind address serving clients and workers.
server.internal_bindHOPSKIP_INTERNAL_BINDhost:portunsetSecurity-relevant. Second gRPC bind serving the same API to callers inside the deployment. Set = an mTLS listener is opened, which requires internal_tls.cert_path, internal_tls.key_path and internal_tls.client_ca_path. Unset = one listener, unchanged.
server.ops_bindHOPSKIP_OPS_BINDhost:portunsetHealth/metrics surface (/healthz, /readyz, /metrics). Unset = off.
server.console_bindHOPSKIP_CONSOLE_API_BINDhost:portunsetConsole HTTP/JSON API bind address. Unset = off.
server.console_authHOPSKIP_CONSOLE_API_AUTHone of: on, offonSecurity-relevant. Set to off to serve the console API unauthenticated (loopback development only).
server.access_sync_msHOPSKIP_ACCESS_SYNC_MSinteger1000Security-relevant. How often a node re-reads the access-control journals (user directory, ACL grants) for records another node wrote - i.e. the worst-case delay before a revocation made on one node is enforced here. Only runs under a replicated log store (raft/sharded), where more than one process writes them. 0 disables it, leaving this node’s tables stale until it restarts.
server.scim_bindHOPSKIP_SCIM_BINDhost:portunsetSCIM 2.0 inbound provisioning endpoint (Okta, Entra ID, …), on its own listener. Unset = off; also requires scim_token.
server.scim_tokenHOPSKIP_SCIM_TOKENtextunsetSecurity-relevant. Shared bearer token an identity provider presents to the SCIM endpoint. Required: with no token the endpoint does not bind, since it writes the user directory.
server.scim_group_prefixHOPSKIP_SCIM_GROUP_PREFIXtextunsetOnly pushed groups carrying this prefix map to namespaces, with the prefix stripped (hopskip-checkout -> checkout). Unset = every pushed group maps to its own name.
server.shard_admin_bindHOPSKIP_SHARD_ADMIN_BINDhost:portunsetSharded-backend admin surface. Unset = off; requires the sharded log store.
server.shard_admin_tokenHOPSKIP_SHARD_ADMIN_TOKENtextunsetSecurity-relevant. Shared token every shard-admin request must present. Set = the surface authenticates (and may bind beyond loopback); unset = loopback-only unless explicitly overridden.
server.shard_admin_allow_remoteHOPSKIP_SHARD_ADMIN_ALLOW_REMOTEbooleanfalseSecurity-relevant. Permit a non-loopback shard-admin bind without a token. Without shard_admin_token that surface is unauthenticated and can transfer group leadership; prefer setting the token.

TLS

Config keyEnvironment variableTypeDefaultDescription
tls.cert_pathHOPSKIP_TLS_CERT_PATHpathunsetSecurity-relevant. PEM server certificate. Set together with tls.key_path to terminate TLS.
tls.key_pathHOPSKIP_TLS_KEY_PATHpathunsetSecurity-relevant. PEM private key for the server certificate.
tls.client_ca_pathHOPSKIP_TLS_CLIENT_CA_PATHpathunsetSecurity-relevant. PEM CA that client certificates must be signed by. Set = mTLS required, for every caller of this listener - including customers. To require certificates from in-cluster callers only, open the internal listener (server.internal_bind) instead.
internal_tls.cert_pathHOPSKIP_INTERNAL_TLS_CERT_PATHpathunsetSecurity-relevant. PEM server certificate for the internal listener. Its names are in-cluster ones, so a private CA issues it.
internal_tls.key_pathHOPSKIP_INTERNAL_TLS_KEY_PATHpathunsetSecurity-relevant. PEM private key for the internal listener’s certificate.
console_api_tls.cert_pathHOPSKIP_CONSOLE_API_TLS_CERT_PATHpathunsetSecurity-relevant. PEM certificate the console API terminates TLS with. Set together with the key; unset leaves it plain HTTP. It serves cross-namespace data and raw SQL, so the hop carrying its bearer token is worth encrypting.
console_api_tls.key_pathHOPSKIP_CONSOLE_API_TLS_KEY_PATHpathunsetSecurity-relevant. PEM private key for the console API’s certificate.
console_api_tls.client_ca_pathHOPSKIP_CONSOLE_API_TLS_CLIENT_CA_PATHpathunsetSecurity-relevant. Optional. PEM CA that client certificates on the console API must be signed by. Set = mTLS on that surface, which requires every caller of it to hold a certificate.
scim_tls.cert_pathHOPSKIP_SCIM_TLS_CERT_PATHpathunsetSecurity-relevant. PEM certificate the SCIM endpoint terminates TLS with. Set together with the key; unset leaves it plain HTTP. It authenticates with a single long-lived static token, which is what identity providers support.
scim_tls.key_pathHOPSKIP_SCIM_TLS_KEY_PATHpathunsetSecurity-relevant. PEM private key for the SCIM endpoint’s certificate.
scim_tls.client_ca_pathHOPSKIP_SCIM_TLS_CLIENT_CA_PATHpathunsetSecurity-relevant. Optional. PEM CA that client certificates on the SCIM endpoint must be signed by. Set = mTLS on that surface, which requires every caller of it to hold a certificate.
ops_tls.cert_pathHOPSKIP_OPS_TLS_CERT_PATHpathunsetSecurity-relevant. PEM certificate the ops surface terminates TLS with. Set together with the key; unset leaves it plain HTTP. Turning this on means probes need scheme HTTPS and scrapes need scheme https; a kubelet cannot present a client certificate, so do not set a client CA here without moving probes to exec.
ops_tls.key_pathHOPSKIP_OPS_TLS_KEY_PATHpathunsetSecurity-relevant. PEM private key for the ops surface’s certificate.
ops_tls.client_ca_pathHOPSKIP_OPS_TLS_CLIENT_CA_PATHpathunsetSecurity-relevant. Optional. PEM CA that client certificates on the ops surface must be signed by. Set = mTLS on that surface, which requires every caller of it to hold a certificate.
raft_tls.cert_pathHOPSKIP_RAFT_TLS_CERT_PATHpathunsetSecurity-relevant. PEM certificate this node presents to Raft peers, and to which peers’ certificates are compared. Needs both server auth and client auth: every node is both ends. Set = the peer transport is mutually authenticated, which every node in the cluster must also be.
raft_tls.key_pathHOPSKIP_RAFT_TLS_KEY_PATHpathunsetSecurity-relevant. PEM private key for the peer certificate.
raft_tls.client_ca_pathHOPSKIP_RAFT_TLS_CLIENT_CA_PATHpathunsetSecurity-relevant. PEM CA that peer certificates must be signed by, checked in both directions. Required whenever raft_tls.cert_path is set.
raft_tls.peer_nameHOPSKIP_RAFT_TLS_PEER_NAMEtexthopskip-raft-peer.internalThe name a peer’s certificate must carry. One name for the whole cluster: a handshake proves membership of the deployment rather than which member, which is all Raft acts on.
internal_tls.client_ca_pathHOPSKIP_INTERNAL_TLS_CLIENT_CA_PATHpathunsetSecurity-relevant. PEM CA that client certificates on the internal listener must be signed by. Required whenever server.internal_bind is set: an internal listener without it is a second public listener.

Authentication

Config keyEnvironment variableTypeDefaultDescription
auth.static_secretHOPSKIP_STATIC_AUTH_SECRETtextunsetSecurity-relevant. HS256 shared secret for bearer tokens. Prefer injecting via the environment or a secret mount rather than a config file.
auth.dev_allow_insecure_secretHOPSKIP_DEV_ALLOW_INSECURE_SECRETbooleanfalseSecurity-relevant. Permit booting on the publicly known development secret. Development only - every token is forgeable.
auth.oidc_issuerHOPSKIP_OIDC_ISSUERtextunsetOIDC issuer URL. Set = authenticate with OIDC instead of the shared secret.
auth.oidc_audienceHOPSKIP_OIDC_AUDIENCEtextunsetExpected aud claim. Required when the issuer is set.
auth.oidc_jwks_urlHOPSKIP_OIDC_JWKS_URLtextunsetJWKS endpoint for signing keys. Optional: discovered from the issuer’s OIDC discovery document when unset.
auth.oidc_jwks_refresh_secsHOPSKIP_OIDC_JWKS_REFRESH_SECSinteger300How often to re-fetch the JWKS.
auth.oidc_namespace_claimHOPSKIP_OIDC_NAMESPACE_CLAIMtextnamespacesToken claim listing the namespaces a principal may act in.
auth.oidc_subject_claimHOPSKIP_OIDC_SUBJECT_CLAIMtextsubToken claim that becomes the principal’s subject. Point it at email when the provider’s sub is opaque (Okta’s is 00u...): this string is what grants are keyed by and what the audit trail shows. Falls back to sub when the claim is absent.
auth.oidc_role_claimHOPSKIP_OIDC_ROLE_CLAIMtextunsetToken claim listing the roles or groups a principal holds (groups on Okta/Keycloak, roles on Entra, a namespaced URI on Auth0). Unset means principals carry no roles. Roles authorize nothing in Core - namespace membership still decides that - they are read by policy above it, such as a human-task gate inviting everyone holding DBA.
auth.worker_authzHOPSKIP_WORKER_AUTHZtextunsetSecurity-relevant. Task-type grants for worker principals: subject=pattern[,pattern];... (* wildcards; a * subject rule applies to everyone). Set = a worker registration declaring an ungranted task type is refused (default-deny for uncovered subjects); unset = task types unrestricted. Malformed = startup error.
auth.worker_authz_namespacesHOPSKIP_WORKER_AUTHZ_NAMESPACESone of: on, offoffSecurity-relevant. Set to on to only accept a worker’s completion (complete/fail/checkpoint) when the worker principal’s namespace memberships include the workflow’s namespace. Requires worker tokens to carry meaningful namespace claims.
auth.require_encrypted_payloadsHOPSKIP_REQUIRE_ENCRYPTED_PAYLOADStextunsetSecurity-relevant. Comma-separated namespaces that refuse plaintext user payloads (ADR 0071 end-to-end encryption); * covers every namespace. A payload-bearing RPC (StartWorkflow, Signal/Query/UpdateWorkflow, SendChannelMessage) into a listed namespace must arrive sealed (a HOPSKIP-E2EE payload envelope) or it is rejected. A policy tripwire for misconfigured clients, not a cryptographic verification: the server holds no payload keys.

Console accounts

Config keyEnvironment variableTypeDefaultDescription
console_accounts.database_urlHOPSKIP_CONSOLE_ACCOUNTS_DBtextunsetSecurity-relevant. Postgres URL of the Cloud control plane holding console accounts. Set = first-party signup/login (with TOTP, passkeys, email confirmation, password reset) is served on the console listener under /auth; unset = off, the pre-existing behavior. The database must already be migrated (sqitch --chdir db/console deploy): the server verifies the schema at startup and refuses to start otherwise, rather than creating tables itself. See db/README.md.
console_accounts.originHOPSKIP_CONSOLE_ORIGINtextunsetSecurity-relevant. The console’s external origin (e.g. https://console.hopskip.run). Required with the accounts database: it decides emailed links, the WebAuthn relying-party ID passkeys bind to (changing it strands them), and whether the session cookie is Secure.
console_accounts.signupHOPSKIP_CONSOLE_SIGNUPone of: on, offonSelf-service signup. off keeps login/reset/2FA working while new accounts come only from provisioning.
console_accounts.smtp_urlHOPSKIP_CONSOLE_SMTP_URLtextunsetSecurity-relevant. SMTP relay for confirmation and reset mail (smtp://user:pass@host:port or smtps://…). Unset = links are written to the server log instead: right for development, a credential leak into the log pipeline in production.
console_accounts.email_fromHOPSKIP_CONSOLE_EMAIL_FROMtextHopskip Cloud <no-reply@hopskip.run>From header on account mail.
console_accounts.admin_emailsHOPSKIP_CONSOLE_ADMIN_EMAILStextunsetSecurity-relevant. Comma-separated account emails granted the reserved admin namespace at authentication time - the bootstrap answer to “who administers a fresh install”, applied without a database write.
console_accounts.session_ttl_hoursHOPSKIP_CONSOLE_SESSION_TTL_HOURSinteger168Console session lifetime in hours. Absolute, not sliding: a stolen cookie ages out no matter how actively it is used.
console_accounts.oauth_github_client_idHOPSKIP_CONSOLE_OAUTH_GITHUB_IDtextunsetGitHub OAuth app client ID. Set together with the secret = “Continue with GitHub” on the console’s login and signup pages; the app’s authorization callback URL must be <origin>/api/auth/oauth/github/callback.
console_accounts.oauth_github_client_secretHOPSKIP_CONSOLE_OAUTH_GITHUB_SECRETtextunsetSecurity-relevant. GitHub OAuth app client secret, paired with the client ID.
console_accounts.oauth_google_client_idHOPSKIP_CONSOLE_OAUTH_GOOGLE_IDtextunsetGoogle OAuth client ID. Set together with the secret = “Continue with Google” on the console’s login and signup pages; the client’s authorized redirect URI must be <origin>/api/auth/oauth/google/callback.
console_accounts.oauth_google_client_secretHOPSKIP_CONSOLE_OAUTH_GOOGLE_SECRETtextunsetSecurity-relevant. Google OAuth client secret, paired with the client ID.
console_accounts.billing_urlHOPSKIP_CONSOLE_BILLING_URLtextunsetThe hopskip-billing gateway (e.g. http://hopskip-billing:8099). Set together with the token = onboarding asks a new organization for a payment method; unset = it asks for none and every organization is created waived. Not a Stripe endpoint: the console never holds a payment credential, see below.
console_accounts.billing_tokenHOPSKIP_CONSOLE_BILLING_TOKENtextunsetSecurity-relevant. Shared bearer token this console presents to that gateway, paired with the URL. Generate with openssl rand -hex 32; shorter than 32 characters is refused at the sidecar’s startup.

Onboarding, organizations, and why there is no Stripe key here

With console_accounts.database_url set, an account is not usable until it belongs to an organization - the thing that owns a namespace, a payment method, and a set of people. A new account is sent to /welcome, where it either creates one (details, a namespace nobody else holds, and a card when one is configured) or joins an existing one by invitation or by asking an organization discoverable to its confirmed email domain. Joining needs neither a namespace nor a card, because the organization already has both.

Card capture crosses a process boundary on purpose. hopskip-server executes untrusted tenant Wasm and does not depend on hopskip-billing, so no configuration can put a Stripe key in it (ADR 0050). The two billing variables above point at the sidecar that does hold one; the token authenticates this console to that sidecar and cannot charge, refund, or read a card. Card details are entered on the payment provider’s own hosted page, never on a page the console serves.

A self-hosted deployment leaves both unset. Onboarding still requires an organization - somebody has to own the namespace - and asks for no payment details.

Product analytics

Config keyEnvironment variableTypeDefaultDescription
analytics.posthog_api_keyHOPSKIP_POSTHOG_API_KEYtextunsetPostHog project key (phc_…). Set = server-authoritative console account lifecycle events (signed up, email verified, logged in, credential changed) are reported; unset = nothing is ever sent, the default. Delivery is off the request path and flushed at shutdown; a failure never fails a login. Write-only token, so it is configuration rather than a secret.
analytics.posthog_urlHOPSKIP_POSTHOG_URLtexthttps://us.i.posthog.comPostHog ingestion origin. https://eu.i.posthog.com for EU cloud, or a self-hosted equivalent. Malformed = startup error when a key is set.
analytics.enabledHOPSKIP_ANALYTICSone of: on, offonoff disables product analytics even when a PostHog key is configured - the operator opt-out, which wins over the key. The same variable turns the hop CLI’s anonymous usage analytics off.

Log store

Config keyEnvironment variableTypeDefaultDescription
log_store.backendHOPSKIP_LOG_STORE_BACKENDone of: disk, memory, raft, shardeddiskEvent-log backend. memory is the explicit ephemeral opt-in and loses history on restart.
log_store.disk_pathHOPSKIP_LOG_STORE_DISK_PATHpath./.hop/logRoot directory for the disk backend.
log_store.shard_rangesHOPSKIP_SHARD_RANGEStextunsetShard range assignment for the sharded backend.

Raft

Config keyEnvironment variableTypeDefaultDescription
raft.node_idHOPSKIP_RAFT_NODE_IDintegerunsetThis node’s raft id. Required for the raft/sharded backends.
raft.bind_addrHOPSKIP_RAFT_BIND_ADDRhost:portunsetAddress this node’s raft peer transport listens on.
raft.membersHOPSKIP_RAFT_MEMBERStextunsetBootstrap-time member list, as id@host:port entries. Steady-state membership lives in the node registry (ADR 0053).
raft.data_dirHOPSKIP_RAFT_DATA_DIRpathunsetDirectory for this node’s raft state.
raft.storage_formatHOPSKIP_RAFT_STORAGE_FORMATtextprotoOn-disk format under raft.data_dir: proto (length-prefixed, CRC-guarded protobuf frames; compact and cheap to encode), jsonl (human-inspectable reference files), or sqlite (WAL databases; raw-BLOB payloads, no rename dances). A directory keeps its format for life; the formats refuse each other’s directories - a node upgraded over an existing jsonl directory must set this to jsonl to keep reading it.
raft.heartbeat_msHOPSKIP_RAFT_HEARTBEAT_MSintegerunsetRaft heartbeat interval.
raft.bootstrapHOPSKIP_RAFT_BOOTSTRAPbooleanfalseForm the cluster from raft.members if it is not already formed. Idempotent; set on exactly one node.
raft.advertise_addrHOPSKIP_RAFT_ADVERTISE_ADDRtextunsetAddress peers dial this node on, advertised when it joins a cluster. Defaults to this node’s own raft.members entry, else its bind address; required when auto-scaling with a 0.0.0.0 bind.

Cluster

Config keyEnvironment variableTypeDefaultDescription
cluster.seedsHOPSKIP_CLUSTER_SEEDStextunsetAddresses to join a cluster through: host:port and/or dns:name:port, comma-separated. Any live member works, including a follower.
cluster.failure_detectorHOPSKIP_CLUSTER_FAILURE_DETECTORtextheartbeatCluster liveness detector: heartbeat (probe mesh), gossip (SWIM), or off.
cluster.sync_msHOPSKIP_CLUSTER_SYNC_MSinteger1000How often a node re-reads the node registry to learn new peers. 0 disables.
cluster.query_addrHOPSKIP_CLUSTER_QUERY_ADDRtextunsetThis node’s visibility-query endpoint, published into its registry record so cluster queries can reach it.
cluster.client_addrHOPSKIP_CLUSTER_CLIENT_ADDRtextthe HOPSKIP_SERVER_BIND addressThis node’s client-facing gRPC endpoint as peers should reach it, published into its registry record: a client RPC received by a node that does not service its target is forwarded here (ADR 0070). Set it when the bind address is a wildcard or otherwise unreachable from peers.
cluster.cold_segmentsHOPSKIP_CLUSTER_COLD_SEGMENTSbooleanfalseSnapshots reference cold history by content hash instead of shipping it inline; a catching-up replica fetches it from peers. Enable on every node.
cluster.hot_tail_recordsHOPSKIP_CLUSTER_HOT_TAIL_RECORDSinteger4096How many of the newest applied records stay inline in a snapshot when cold segments are on.
cluster.auto_scaleHOPSKIP_CLUSTER_AUTO_SCALEbooleanfalseRun the membership autopilot on the raft backend: nodes join through cluster.seeds as learners, are promoted to voters once caught up, and are evicted after cluster.evict_after_ms of silence. Scaling becomes starting or stopping nodes.
cluster.evict_after_msHOPSKIP_CLUSTER_EVICT_AFTER_MSinteger300000How long a member must be silent before the autopilot evicts it from membership. 0 disables eviction. A restart shorter than this is never mistaken for a departure.
cluster.min_votersHOPSKIP_CLUSTER_MIN_VOTERSinteger3The voter floor the autopilot never evicts below, so losing quorum margin requires an operator, not an outage.

Durability

Config keyEnvironment variableTypeDefaultDescription
durability.seal_thresholdHOPSKIP_SEAL_THRESHOLDinteger1000Events between event-horizon seals.
durability.spill_watermarkHOPSKIP_SPILL_WATERMARKratio 0.0-1.00.7Channel occupancy ratio at which buffered messages spill to the cold tier.
durability.group_commit_max_batchHOPSKIP_GROUP_COMMIT_MAX_BATCHinteger500Maximum appends coalesced into one group commit.
durability.group_commit_max_linger_msHOPSKIP_GROUP_COMMIT_MAX_LINGER_MSinteger5How long a group commit waits to accumulate a batch.

Deployment safety

Config keyEnvironment variableTypeDefaultDescription
deployment.suspension_trackingHOPSKIP_SUSPENSION_TRACKINGbooleanfalseTrack which host-call site each live workflow is suspended at, so hop deploy’s compat gate can downgrade a removed-await finding with zero live blast radius. Off by default: only turn it on once every worker serving these namespaces reports suspensions, because a partially-reported index answers “zero” to sites nobody reported.

Async activities

Config keyEnvironment variableTypeDefaultDescription
async_activity.secretHOPSKIP_ASYNC_ACTIVITY_SECRETtextunsetSecurity-relevant. Signing key for the task tokens that let an activity be completed out of band (CompleteActivity/FailActivity). Unset = a random per-process key, so tokens stop verifying across a restart and never verify on another node; set a shared secret on any deployment where an async activity may outlive one process. A separate key from auth.static_secret on purpose - this one signs a credential handed to third-party systems.
async_activity.heartbeat_msHOPSKIP_ASYNC_ACTIVITY_HEARTBEAT_MSinteger0How long a parked async activity may go without a HeartbeatActivity call before Core reclaims and redispatches it. 0 (the default) = never, because the motivating case is waiting on a human; the attempt’s propagated deadline bounds it either way.

Tiered storage

Config keyEnvironment variableTypeDefaultDescription
tiered.object_storeHOPSKIP_TIERED_OBJECT_STOREone of: local, s3localCold-tier backend.
tiered.recoverHOPSKIP_TIERED_RECOVERbooleanfalseRecover cold-tier state from the object store on a fresh node.
tiered.s3_bucketHOPSKIP_TIERED_S3_BUCKETtextunsetS3 bucket for the cold tier. Required when object_store is s3.
tiered.s3_regionHOPSKIP_TIERED_S3_REGIONtextunsetS3 region.
tiered.s3_endpoint_urlHOPSKIP_TIERED_S3_ENDPOINT_URLtextunsetOverride endpoint, for S3-compatible stores.
tiered.s3_force_path_styleHOPSKIP_TIERED_S3_FORCE_PATH_STYLEbooleanfalseUse path-style addressing (needed by most S3-compatible stores).

Visibility

Config keyEnvironment variableTypeDefaultDescription
visibility.db_pathHOPSKIP_VISIBILITY_DB_PATHpathhopskip-visibility.sqlite3SQLite file backing the visibility projection.
visibility.tick_msHOPSKIP_VISIBILITY_TICK_MSinteger1000Projection tick interval.
visibility.batch_sizeHOPSKIP_VISIBILITY_BATCH_SIZEinteger500Records projected per tick.

Observability

Config keyEnvironment variableTypeDefaultDescription
observability.log_formatHOPSKIP_LOG_FORMATone of: text, jsontextLog output format. json emits one object per line for a log shipper.
observability.otlp_endpointHOPSKIP_OTLP_ENDPOINTtextunsetOTLP/gRPC collector to export spans to (e.g. http://collector:4317). Unset = no export.
observability.otlp_service_nameHOPSKIP_OTLP_SERVICE_NAMEtexthopskip-serverService name reported to the trace backend.
observability.audit_log_pathHOPSKIP_AUDIT_LOG_PATHpathunsetAppend-only file receiving the control-plane audit trail (auth denials, admin actions) as JSON lines, independent of RUST_LOG. Unset = audit events flow only to the ordinary log stream.
observability.slo_configHOPSKIP_SLO_CONFIGpathunsetJSON file declaring burn-rate SLOs to evaluate. Unset = no SLO evaluation.

OpenLineage

Config keyEnvironment variableTypeDefaultDescription
openlineage.endpointHOPSKIP_OPENLINEAGE_ENDPOINTtextunsetBase URL of an OpenLineage receiver such as Marquez (e.g. http://marquez:5000); events post to {endpoint}/api/v1/lineage. Unset = no lineage export.
openlineage.api_keyHOPSKIP_OPENLINEAGE_API_KEYtextunsetSecurity-relevant. Bearer token sent with every lineage post. Unset = no Authorization header.
openlineage.fileHOPSKIP_OPENLINEAGE_FILEpathunsetAppend run events to this file as newline-delimited JSON (the OpenLineage file transport). Ignored when an endpoint is also set.
openlineage.tick_msHOPSKIP_OPENLINEAGE_TICK_MSinteger5000Emitter tick interval.
openlineage.batch_sizeHOPSKIP_OPENLINEAGE_BATCH_SIZEinteger500Records read per partition per tick.

Metering

Config keyEnvironment variableTypeDefaultDescription
metering.enabledHOPSKIP_METERINGone of: on, offoffRecord this node’s usage in an append-only, hash-chained ledger. Action counts are operational telemetry, not billable units; current Cloud pricing uses provider-specific CPU, RAM, storage, and network rates. Off by default for self-hosted deployments.
metering.ledger_pathHOPSKIP_METERING_LEDGER_PATHpath.hop/usageDirectory holding the usage ledger’s append-only, hash-chained files (one per namespace). Deliberately beside the event log rather than inside it: billing records outlive workflow history and must not inherit its retention, restore, or rebalancing lifecycle.
metering.tick_msHOPSKIP_METERING_TICK_MSinteger5000How often the meter folds new log records into usage buckets.
metering.batch_sizeHOPSKIP_METERING_BATCH_SIZEinteger500Records read per partition per metering tick.
metering.planHOPSKIP_METERING_PLANone of: developer, standard, business, dedicatedstandardLegacy Action-rate plan retained for ledger compatibility. It must not be used to invoice under the current resource-based Cloud pricing model.
metering.export_pathHOPSKIP_METERING_EXPORT_PATHpathunsetAppend flushed usage records to this file as newline-delimited JSON, for ingestion by an external billing system. Unset = no export; the usage ledger is written either way.

Placement

Config keyEnvironment variableTypeDefaultDescription
placement.reconcile_msHOPSKIP_PLACEMENT_RECONCILE_MSintegerunsetSplit/merge reconciler interval. Unset or 0 = off; requires the sharded backend.
placement.leader_imbalanceHOPSKIP_PLACEMENT_LEADER_IMBALANCEinteger2How far the busiest node’s leadership count may exceed the quietest’s before one is handed over. Leadership is the cheap lever: it costs one RPC and moves no data, and it is what fixes a write-path hotspot. 0 disables it. Values below 2 are raised to 2, since moving one leadership can only close a gap of two or more.
placement.replica_imbalanceHOPSKIP_PLACEMENT_REPLICA_IMBALANCEinteger0How far the busiest node’s replica count may exceed the quietest’s before a replica is moved. Off by default: a move is O(total history), so a deployment should opt into paying for it rather than discover it. Values below 2 are raised to 2.
placement.replication_factorHOPSKIP_PLACEMENT_REPLICATION_FACTORinteger3How many voters each shard group should have. The reconciler adds a replica on a registered node when a group falls below this, which is what makes adding a node fill it. 0 disables the repair and leaves placement entirely operator-driven.
placement.max_concurrentHOPSKIP_PLACEMENT_MAX_CONCURRENTinteger1Concurrent split/merge operations.
placement.split_write_rateHOPSKIP_PLACEMENT_SPLIT_WRITE_RATEintegerunsetWrite rate above which a shard is a split candidate.
placement.split_open_partitionsHOPSKIP_PLACEMENT_SPLIT_OPEN_PARTITIONSintegerunsetOpen-partition count above which a shard is a split candidate.
placement.merge_write_rateHOPSKIP_PLACEMENT_MERGE_WRITE_RATEintegerunsetWrite rate below which adjacent shards are merge candidates.

Example

[server]
# bind = "127.0.0.1:50051"
# internal_bind =
# ops_bind =
# console_bind =
# console_auth = "on"
# access_sync_ms = 1000
# scim_bind =
# scim_token =
# scim_group_prefix =
# shard_admin_bind =
# shard_admin_token =
# shard_admin_allow_remote = false

[tls]
# cert_path =
# key_path =
# client_ca_path =
# cert_path =
# key_path =
# cert_path =
# key_path =
# client_ca_path =
# cert_path =
# key_path =
# client_ca_path =
# cert_path =
# key_path =
# client_ca_path =
# cert_path =
# key_path =
# client_ca_path =
# peer_name = "hopskip-raft-peer.internal"
# client_ca_path =

[auth]
# static_secret =
# dev_allow_insecure_secret = false
# oidc_issuer =
# oidc_audience =
# oidc_jwks_url =
# oidc_jwks_refresh_secs = 300
# oidc_namespace_claim = "namespaces"
# oidc_subject_claim = "sub"
# oidc_role_claim =
# worker_authz =
# worker_authz_namespaces = "off"
# require_encrypted_payloads =

[console_accounts]
# database_url =
# origin =
# signup = "on"
# smtp_url =
# email_from = "Hopskip Cloud <no-reply@hopskip.run>"
# admin_emails =
# session_ttl_hours = 168
# oauth_github_client_id =
# oauth_github_client_secret =
# oauth_google_client_id =
# oauth_google_client_secret =
# billing_url =
# billing_token =

[analytics]
# posthog_api_key =
# posthog_url = "https://us.i.posthog.com"
# enabled = "on"

[log_store]
# backend = "disk"
# disk_path = "./.hop/log"
# shard_ranges =

[raft]
# node_id =
# bind_addr =
# members =
# data_dir =
# storage_format = "proto"
# heartbeat_ms =
# bootstrap = false
# advertise_addr =

[cluster]
# seeds =
# failure_detector = "heartbeat"
# sync_ms = 1000
# query_addr =
# client_addr = "the HOPSKIP_SERVER_BIND address"
# cold_segments = false
# hot_tail_records = 4096
# auto_scale = false
# evict_after_ms = 300000
# min_voters = 3

[durability]
# seal_threshold = 1000
# spill_watermark = 0.7
# group_commit_max_batch = 500
# group_commit_max_linger_ms = 5

[deployment]
# suspension_tracking = false

[async_activity]
# secret =
# heartbeat_ms = 0

[tiered]
# object_store = "local"
# recover = false
# s3_bucket =
# s3_region =
# s3_endpoint_url =
# s3_force_path_style = false

[visibility]
# db_path = "hopskip-visibility.sqlite3"
# tick_ms = 1000
# batch_size = 500

[observability]
# log_format = "text"
# otlp_endpoint =
# otlp_service_name = "hopskip-server"
# audit_log_path =
# slo_config =

[openlineage]
# endpoint =
# api_key =
# file =
# tick_ms = 5000
# batch_size = 500

[metering]
# enabled = "off"
# ledger_path = ".hop/usage"
# tick_ms = 5000
# batch_size = 500
# plan = "standard"
# export_path =

[placement]
# reconcile_ms =
# leader_imbalance = 2
# replica_imbalance = 0
# replication_factor = 3
# max_concurrent = 1
# split_write_rate =
# split_open_partitions =
# merge_write_rate =