Skip to main content

namespace.toml

The namespace descriptor declares the flag namespace's identity, an optional roster of named environments shared by every flag in the namespace, and global settings such as telemetry. The file is optional.

When absent, the linter runs the flag namespace in untyped-env mode: slug is inferred from the directory name, every other field takes its documented default, and the SDK accepts any pattern-valid env slug at evaluation time. Declaring namespace.toml is what earns you a typo-checked env list, dashboard metadata, and per-env public-evaluate gating.

The descriptor does not declare the tenant — tenant ownership is server-side metadata assigned at namespace-creation time and never expressed in the manifest.


When to declare namespace.toml

The minimum viable manifest is a directory containing one flags/<key>.toml and no namespace.toml. Declare namespace.toml once any of these becomes true:

  • You have more than one environment whose behavior diverges, and want lint to catch typos like prod instead of production (E010 only fires in typed-env mode).
  • You want a dashboard display_name or description for the flag namespace.
  • You want any environment to opt into public evaluation via public_evaluate = true (requires a typed env list).
  • You want to suppress telemetry (telemetry_enabled = false), declare per-namespace private_attributes, or set raw_entity_ids = true.

Until then, the file's absence is the right shape. The linter does NOT warn about a missing namespace.toml.


Complete example

schema_version = "0.1"

[namespace]
slug = "payments"
display_name = "Payments Team"
description = "Feature flags for the payments service and checkout domain"
telemetry_enabled = true

[namespace.environments]
development = { display_name = "Development" }
staging = { display_name = "Staging" }
production = { display_name = "Production" }

Top-level fields

schema_version

See schema-versioning. MUST be present and well-formed; otherwise E001.

[namespace] table

FieldTypeRequiredDefaultValidationDescription
slugstringnothe directory namePattern [a-z][a-z0-9-]*, max 63 chars; when present MUST equal the directory nameThe flag-namespace identifier. Unique within its tenant.
display_namestringnothe slugNon-empty when present (W010 on empty string)Human-readable name shown in dashboards and CLI output.
descriptionstringno""Free-form proseDescription of the flag namespace's purpose and owning team. Used for ownership tracking and dashboards.
telemetry_enabledboolnotrueWhen false, the server skips telemetry recording for every flag in this flag namespace.
raw_entity_idsboolnofalseWhen true, the SDK emits the raw bucketing identifier in unit_id_hash instead of its SHA-256 digest.
private_attributesarray of stringsno[]Each entry MUST be a stringNames of context attributes the SDK MUST strip from context_attributes and secondary_unit_ids before record emission. Applies to every flag in the flag namespace.

slug

The slug is the identifier for this flag namespace within its tenant. It appears in:

  • The manifest directory name (<slug>/).
  • The server URL path (/api/v1/tenants/<tenant>/namespaces/<slug>/...).
  • SDK initialization (namespace_uri = "https://.../<slug>/manifest.tar.gz").
  • Audit log entries.

The slug is immutable. To rename a flag namespace, create a new one with the new slug, migrate flags and segments, update SDK consumers, and delete the old one. Direct rename is not supported.

namespace.slug MAY be omitted; when omitted, the slug is inferred from the directory containing namespace.toml. When present, it MUST equal the directory name — E017 fires on mismatch so a stale slug after a directory rename is surfaced rather than silently honored. A slug that is present but malformed raises E030 independently of the directory-match check. The directory name itself MUST also be a valid slug; otherwise lint of an inferred slug fails with E030.

display_name and description

Both are optional metadata. They have no semantic effect on flag evaluation. They appear in dashboards, CLI listings, and server API responses (GET /api/v1/tenants/{tenant}/namespaces/{namespace} returns them as declared).

display_name, when declared, MUST NOT be empty — display_name = "" raises W010 (a blank dashboard label is almost always an oversight). To accept the default (the slug), omit the field entirely. description has no non-empty constraint.

telemetry_enabled

namespace.telemetry_enabled is the only telemetry switch defined by the manifest format. There is no per-flag telemetry_enabled; setting namespace.telemetry_enabled = false suppresses telemetry for every flag in the flag namespace.

telemetry_enabled = false is intended for flag namespaces handling regulated or privacy-sensitive data where evaluation-rate telemetry would itself be sensitive. It does NOT affect SDK-side OpenTelemetry spans or metrics; those are controlled by the SDK's own configuration.

raw_entity_ids

Controls how the bucketing identifier is rendered in evaluation records emitted by the SDK.

  • Default (false): the SDK hashes the value referenced by [segment.bucket].entity_id_attribute with SHA-256 and emits the lowercase-hex digest in unit_id_hash.
  • true: the SDK emits the raw identifier verbatim in the same field. The field name unit_id_hash is preserved for record-shape stability.

raw_entity_ids = true is intended for flag namespaces whose bucketing identifier is not personally identifying — internal account IDs, B2B tenant slugs, opaque request tokens — where the analytical convenience of joining records to upstream systems on the raw identifier outweighs the lack of pseudonymization. It SHOULD NOT be set on flag namespaces whose bucketing context could carry PII.

The field has no effect on evaluation behavior. It is read only at record-emission time and is independent of telemetry_enabled (when telemetry is disabled, raw_entity_ids is irrelevant — no records are produced).

private_attributes

Declares context-attribute names the SDK MUST strip from context_attributes and from secondary_unit_ids before emitting an evaluation record. The match is byte-exact on the attribute name; values are not inspected.

[namespace]
slug = "payments"
private_attributes = ["user.email", "user.phone", "request.ip"]

The list applies to every flag in the flag namespace. Per-flag additions live in flag.private_attributes (flag § private_attributes); the effective set is the union of the namespace list and the flag list. Removing an attribute from either list at record-emission time requires editing the manifest — there is no SDK or runtime override.

The linter validates only shape: a non-array value or any non-string entry produces E001. The linter does NOT validate that the named attributes appear in any predicate or bucket — listing an unused attribute is accepted (telemetry diagnostic T011 surfaces the inverse condition: a non-listed attribute that should have been listed).

This field has no effect on evaluation behavior.


[namespace.environments] table

Declares every environment that flag files may reference. Every key is an environment slug; every value is an inline table (or sub-table) of environment metadata.

[namespace.environments]
development = { display_name = "Development" }
staging = { display_name = "Staging" }
production = { display_name = "Production" }

Typed vs. untyped env mode

The [namespace.environments] table is OPTIONAL. Its presence selects one of two modes:

ModeTriggerEffect
Typed[namespace.environments] declared with ≥ 1 entryValid env slugs are exactly the table's keys. Flag files referencing any other env slug fail with E010. Per-env metadata (display_name, public_evaluate) is honored.
Untyped[namespace.environments] absent, or namespace.toml itself absentAny env slug matching [a-z][a-z0-9-]* is accepted; no typo protection. public_evaluate is unavailable; the server rejects namespace-client tokens against untyped-env flag namespaces with 403.

A [namespace.environments] table declared but empty (zero entries) raises E023 — that shape is almost always an in-progress edit and is more useful as an error than a silent reversion to untyped mode. To run untyped, omit the table entirely.

Environment metadata fields

Each environment value supports:

FieldTypeRequiredDefaultDescription
display_namestringnothe env slugHuman-readable label for this environment shown in dashboards.
public_evaluateboolnofalseWhen true, the server's evaluation endpoints accept namespace-client (public) tokens bound to this environment.

Unknown fields inside an environment value are silently ignored — this is the one forward-compatible site in the schema (see schema-versioning § Forward-compat policy).

public_evaluate — semantic effect

public_evaluate = true opts the environment in to public, browser-side evaluation through namespace-client tokens. These tokens are designed to be embedded in browser bundles or other untrusted clients: their secret value is assumed visible to any user of the application, so secrecy is not a defense.

When public_evaluate = true:

  • The server accepts namespace-client tokens bound to this (tenant, namespace, environment) triple on POST /api/v1/tenants/{tenant}/namespaces/{namespace}/evaluate{,/all}.
  • All other endpoints (manifest download, version history, lint, admin) reject namespace-client tokens with 403 Forbidden.
  • Evaluation requests bearing a namespace-client token are subject to a stricter per-(tenant, namespace, environment, Origin) rate limit and are tagged principal_type = "client" in telemetry.

When public_evaluate = false (the default), the server rejects every namespace-client token bound to this environment with 403 Forbidden regardless of the token's status. Toggling the field therefore acts as a kill switch even when issued tokens are still in circulation.

public_evaluate is only meaningful in typed-env mode. An untyped-env flag namespace cannot opt into public eval; the server rejects namespace-client tokens against such flag namespaces.

Authoring guidance

  • Treat any rule that branches on a request-supplied attribute (e.g., user.is_admin, user.entitlement_tier) as a public claim when the env has public_evaluate = true. Anyone holding the embedded client token can craft an evaluation context with arbitrary attributes and observe the resulting variant.
  • Production environments typically set public_evaluate = false and route browser traffic to a separate production-public environment that mirrors the relevant flag set.

Environment ordering

The TOML serialization preserves order within a single document (alphabetical when serialized back). The server canonicalizes environments by slug. Environment ordering has no semantic meaning at evaluation time — every flag's per-environment block looks up the environment by name.


Cross-file consistency

In typed-env mode, every environment slug referenced in a flag file's [flag.environments.<env>] block MUST appear as a key in [namespace.environments]. References to undeclared environments produce E010 on the offending flag file. Names are byte-exact case-sensitive.

The reserved name _ (the catch-all; see resolution § The _ catch-all) is always accepted in [flag.environments._] regardless of mode and is never reported by E010.

In untyped-env mode, E010 does not fire — any env slug matching [a-z][a-z0-9-]* is accepted. Pattern violations still raise E024 on the flag-side env block.


Common diagnostics

ScenarioDiagnostic
namespace.toml missingnone — untyped-env mode, defaults applied, slug = directory name
namespace.toml empty or unparseableE001
namespace.toml present, [namespace] table missingnone — equivalent to empty namespace.toml under untyped-env mode
namespace.slug missing inside a present [namespace] tablenone — slug inferred from directory name
namespace.slug declared but does not match directory nameE017
namespace.slug violates pattern or exceeds 63 charsE030
namespace.display_name = ""W010
[namespace.environments] declared but emptyE023
Environment slug malformed (typed mode)E024
Flag-side env block uses unrecognized env (typed mode only)E010
Unknown field at [namespace]E016
private_attributes non-array, or any entry non-stringE001

  • Skip namespace.toml for the first hour of a new flag namespace. Drop a single flag file under flags/ and iterate. Reach for the descriptor only when you cross one of the lines in § When to declare namespace.toml.
  • Once you declare namespace.toml, also declare at least the standard tier of environments (development, staging, production). Declared-but-unused environments cost nothing and let agents add per-env configuration without further editing of namespace.toml.
  • Keep public_evaluate = false (the default) on every environment unless you have a deliberate need to serve flag values to browsers or other untrusted clients. When you do enable it, prefer a dedicated environment (e.g., production-public) so the flag set exposed to browsers is reviewable in isolation from the server-side production flag set.
  • Use description for ownership and contact information rather than embedding it elsewhere — it is the first field humans read when investigating an unfamiliar flag namespace.

See also

  • 05-flag.md — what env slugs declared here let flag files reference.
  • 09-resolution.md — how the catch-all _ interacts with named environments.
  • evaluation-context — the attribute names private_attributes targets.
  • diagnosticsE010, E016, E017, E023, E024, E030, W010.
  • Tokens specnamespace-client tokens, the consumer of public_evaluate.