Skip to main content

Overview

A feature flag is a named, typed value you flip in the dashboard. A live server picks it up without a restart.

Server Exports

Read every flag, refresh on demand, react to changes

Client Exports

Synchronous reads from replicated state

Runtimes

Your server holds the API key, so it receives every flag. A server flag is never published to clients, and reads on the client behave exactly like a flag that does not exist — so a secret cannot reach a player by accident.

How values move

1

The server fetches

nocloud polls the configuration endpoint with the ETag it already holds.
2

The server publishes

Only the shared values are written to GlobalState under nocloud_flags (configurable).
3

Clients read locally

The game replicates the state bag on its own, so a client read is a local lookup — no round trip, no request, safe inside a tick.

What costs a request

Nothing is fetched until a flag is read. A server whose players never read a flag makes one request in its life — at the first start after installing, so clients are not left reading an empty state bag.
Server-side reads serve from memory for cache_ttl_seconds and go to the API when that has passed, so a read is usually free and never more than one request per window. They never start a background loop — a read refreshes itself. The cache window bounds the polling loop too: a tick that lands on a configuration still inside it skips rather than spending a request to be told nothing changed. A read and a poll are the same fetch, so whichever happens first covers the other.

Why polling exists

Clients read replicated state, which the server can neither see nor refresh on demand. So a client that is reading says so: the flags are refetched every polling.interval_ms from that point, and stop when the last such player disconnects or goes quiet. A client unsubscribes itself after five minutes without a read and resubscribes on the next one, so a player who checked one flag on spawn does not hold the server to polling all session. Reading in a tick sends nothing — the signals are two empty events at the edges, and they never carry values.
Set flags.polling.enabled to true to poll regardless of whether anyone is reading. That is what you want when something watches rather than reads — a AddStateBagChangeHandler on the state bag, or a nocloud.flags.updated handler — since a change nobody reads is a change nothing would otherwise go and find.

Surviving a restart or an outage

The last values received are kept in resource key/value storage when flags.persist_last_known is on (the default). A restart serves them straight away, and an unreachable API never empties them. These values are marked stale until a fetch replaces them — exports.nocloud:AreFlagsStale() tells you which you are reading.
The stored snapshot holds server-only values. It stays on the server host and is never published to clients, but it is written to disk — treat the host as you would any machine holding your API key.

Configuration

See Configuration for every setting: enabled, cache_ttl_seconds, global_state_key, persist_last_known, and the polling block.