GitHub
10/31/2026, 1:25 PMdb.read in tls.yml / api.yml)
• enabled (default false), host, port, username, password, sslmode, maxIdleConns, maxOpenConns.
• Matching flags and environment variables: --db-read-* and DB_READ_*.
• Empty fields inherit the primary's value. Type, database name and connection lifetime always follow the primary.
• Not supported with SQLite (the service refuses to start).
Backend (pkg/backend)
• DBManager.Read is the replica pool when enabled. Otherwise it is the primary *gorm.DB itself, so callers can use it unconditionally.
• If the replica can't be reached at startup, startup fails or retries the same way as for the primary. The primary pool is closed on that failure, so retries don't leak connections.
• Check(), and therefore the DB health monitor, pings both pools.
Routing (explicit, per call site)
• NodeManager, Queries and AuditLogManager gain a ReadDB field and a Replica() method. Replica() returns a read-only copy backed by the replica, or the manager itself when no replica is configured.
• The copies carry no node cache, no query dispatch cache and no event publisher, and audit recording is off, so a replica read can't cause side effects.
• osctrl-api routes these handler calls to the replica:
• Dashboard: node stats, platform counts, osquery version counts, active query and carve counts, and the environment activity chart.
• Node lists: active, inactive and all nodes, and the paged and searchable list.
• Everything else stays on the primary: authentication, enrollment, node-key lookups, permissions, query and carve lifecycle, TLS alerts, query target expansion and the CLI. That includes other callers of the same manager methods.
• osctrl-tls accepts the setting and connects, but routes nothing to the replica yet, and logs a warning saying so.
Security and operational impact
• With the setting off, behaviour is unchanged.
• Replica reads may be a moment out of date. Only display paths use them, never authorization or a read in a write flow.
• A replica outage marks the service degraded through the DB health check, even though the primary still serves writes.
• The replica password is handled the same way as the primary DB password. The existing exposure of the db section through service_config, and the debug-level config log in osctrl-tls, is not addressed here.
Validation
• go vet ./... and go test ./... pass.
• New tests:
• replica config inheritance and overrides, and refusal with SQLite;
• Read defaulting to the primary connection when disabled;
• startup failing when the replica is unreachable;
• NodeManager.Replica() reading from the replica while the manager keeps reading the primary;
• the replica copies of Queries and AuditLogManager carrying no cache or publisher, and never recording audit entries.
• Not validated: a real Postgres or MySQL replica, and replication lag behaviour.
jmpsec/osctrlGitHub
10/31/2026, 1:30 PM