> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nonefivem.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Introduction

> Read feature flags from your FiveM/RedM server and client scripts

## Overview

A feature flag is a [named, typed value](/cloud/offers/feature-flags) you flip in the [dashboard](https://dash.nonefivem.com). A live server picks it up without a restart.

<CardGroup cols={2}>
  <Card title="Server Exports" icon="server" href="/cloud/sdks/cfx/flags/server-exports">
    Read every flag, refresh on demand, react to changes
  </Card>

  <Card title="Client Exports" icon="desktop" href="/cloud/sdks/cfx/flags/client-exports">
    Synchronous reads from replicated state
  </Card>
</CardGroup>

## Runtimes

| Runtime  | Who can read it                  |
| -------- | -------------------------------- |
| `shared` | Your server and players' clients |
| `server` | Your server only                 |

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

<Steps>
  <Step title="The server fetches">
    `nocloud` polls the configuration endpoint with the `ETag` it already holds.
  </Step>

  <Step title="The server publishes">
    Only the `shared` values are written to `GlobalState` under
    `nocloud_flags` (configurable).
  </Step>

  <Step title="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.
  </Step>
</Steps>

## What costs a request

<Note>
  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.
</Note>

| Situation                                  | Cost                                                          |
| ------------------------------------------ | ------------------------------------------------------------- |
| First start after installing               | One request, to fill the state bag                            |
| Restart with a stored snapshot             | Nothing — the cached snapshot fills the bag on the first tick |
| A server-side read inside the cache window | Nothing                                                       |
| A server-side read after the window        | One request, which refreshes for every later read too         |
| A client-side read                         | Nothing — it is a local state bag lookup                      |
| Polling, while at least one client reads   | One request per `polling.interval_ms`, skipped if still fresh |

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.

<Tip>
  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.
</Tip>

## 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.

<Warning>
  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.
</Warning>

## Configuration

See [Configuration](/cloud/sdks/cfx/configuration#feature-flags) for every setting: `enabled`, `cache_ttl_seconds`, `global_state_key`, `persist_last_known`, and the `polling` block.
