Reference: Feature flags
Start
OverviewConceptsGuides
Run and configureREST API walkthroughCommand lineQuery groupsNotebooksRules and SRLPrefixesTestingMCP clientsPublish as toolsQuery bench in chatStatic exportETLDeployingReference
ConfigurationFeature flagsEntity modelSRL languageScriptsExplanation
ArchitectureVersioningEnvironmentsStorage and cachingSecurity modelRDF PatchInline inputsExamples
Sudoku solverFeature flags
Twenty boolean flags decide which sections of the product a build offers. A flag is read on the server, where it decides whether routes are registered, and again in the browser build, where it decides whether a section and the controls leading to it are drawn. The two read separate environments, so a deployment sets both.
The list, the environment variable names and the defaults live in
packages/types/src/featureFlags.ts, which both halves
import.
Values
A flag's value is parsed leniently, case-insensitively, after trimming:
0,false,off,no,disabledmean off.1,true,on,yes,enabledmean on.- Unset, empty, or anything else falls back to the flag's default.
The flags
| Key | Environment variable | Default | What is absent when it is off |
|---|---|---|---|
notebook |
FEATURE_NOTEBOOK |
on | The Notebook rail section and the links into it.
/notebook stays reachable by URL |
queries |
FEATURE_QUERIES |
on | The Query rail section; the /queries,
/execute and /sparql routes, and
/detect-inputs / /detect-outputs. The Notebook
has its own flag above |
queryGroups |
FEATURE_QUERY_GROUPS |
on | The Groups rail section; the /query-groups routes |
rulesSuite |
FEATURE_RULES_SUITE |
on | The Rules rail section; the /rules,
/data-blocks and /rule-sets routes |
benchmarks |
FEATURE_BENCHMARKS |
on | The Bench rail section; the /benchmark-experiments
routes |
tests |
FEATURE_TESTS |
on | The Tests rail section, the Tests tab on record pages, the run bar's
"create test" target and the rules screen's "Save as test"; the
/tests routes |
dataGraphs |
FEATURE_DATA_GRAPHS |
on | The Graphs rail section and the doors into it. The
/data-graphs routes stay registered |
tupleSets |
FEATURE_TUPLE_SETS |
on | The Tuples rail section and the doors into it. The
/tuple-sets routes stay registered |
argumentSets |
FEATURE_ARGUMENT_SETS |
on | The Argument sets rail section and the doors into it. The
/argument-sets routes stay registered while
queries, queryGroups or the
/tests routes are on |
build |
FEATURE_BUILD |
on | The Build rail section and the links into it. /build
stays reachable by URL, and what it shows still follows the section
flags |
etl |
FEATURE_ETL |
off | The /etl-jobs routes, and the startup writability check
on the ETL output directory |
backends |
FEATURE_BACKENDS |
on | The Backends rail section. The /backends routes stay
registered unconditionally |
settings |
FEATURE_SETTINGS |
on | The settings entry in the rail |
rulesAllowInvalidSave |
FEATURE_RULES_ALLOW_INVALID_SAVE |
off | The ability to save a rule, data block or rule set version that does
not parse. With it off, allowInvalidSave: true in a request
body has no effect |
ruleAggregates |
FEATURE_RULE_AGGREGATES |
off | SRL GROUP aggregation, until w3c/data-shapes#1347 is
merged — see below |
playgroundQueries |
FEATURE_PLAYGROUND_QUERIES |
on | The unsaved query playground, which runs through
/execute; it has no route of its own |
playgroundRules |
FEATURE_PLAYGROUND_RULES |
on | The unsaved rules playground;
POST /playground/rules/execute |
playgroundEtl |
FEATURE_PLAYGROUND_ETL |
off | The ETL rail section; POST /playground/etl/execute |
assistant |
FEATURE_ASSISTANT |
off | The in-app build assistant; the /assistant routes |
Route-level refusal is a 404 Not Found: a request to a
route behind a flag that is off reaches nothing, which is the same
answer a route that was never added gives.
Why some flags default off
The off-by-default flags are off for stated reasons, not by oversight.
etl and playgroundEtl.
Both ETL surfaces accept arbitrary DuckDB SQL. That is a read primitive
over the host filesystem, and with the httpfs extension
loaded it is an outbound-request primitive as well. ETL is an operator
flow that someone sets up deliberately, so it should not appear on a
deployment because that deployment was upgraded. See the ETL guide.
assistant. The in-app assistant is
unauthenticated, it calls a paid provider, and it can reach a configured
backend. See the security
model.
The assistant's provider SDK (ai,
@ai-sdk/*) is an optional dependency of the api package.
Nothing else uses it: MCP clients bring their own model. To build a
server image without it, pass
--build-arg WITH_ASSISTANT=false to
docker build. If FEATURE_ASSISTANT is on and
the packages are missing, the server still starts, logs a warning, and
each assistant turn fails with a message that names the missing
packages.
ruleAggregates. Off because
GROUP is not yet in SPARQL RL.
rulesAllowInvalidSave is off because saving a document
that does not parse is a deliberate choice rather than a normal one. It
is on in the rules-conformance recipe, because about a third of the W3C
suite's documents are meant to be unparseable and are stored anyway.
The flags that default on grant no capability the deployment did not
already have. A test runs a callable that is runnable from its own
screen. A data graph is reference RDF you paste into your own library. A
tuple set is tabular input you register in your own library, spliced
into a VALUES clause at execution rather than loaded into a
store. Argument sets have been reachable from the query screen since
they existed; their flag exists so the rail section can be hidden
without touching the query screen's Inputs tab, which is the same entity
seen from its callable.
Two rules the codebase follows
A switched-off feature has no doors. A control that leads into a section this build does not draw is absent, not disabled. A greyed control reads as "broken" where the truth is "not a thing here", and a link into a section that is not drawn navigates nowhere. This is why the flag has to be consulted where the control is written, not only where the section is.
An input is not a feature. tupleSets,
dataGraphs and argumentSets name entities that
are consumed from screens which are not their own section: a query fills
a VALUES clause from a tuple set, a rule set runs against a
data graph, a callable is called with an argument set. Turning one of
these off hides its rail section and the doors into it, and does not
stop a callable that names one from resolving and running. This is why
their routes stay registered: a saved query whose argument set is
unreachable is a query that cannot run.
Two test files enforce these rules by inventory — each door is listed, with the condition that holds it, so a new door fails the suite until someone says which condition applies:
packages/web/test/inputSectionDoors.test.ts— the doors intotupleSets,dataGraphsandargumentSets, and the reads that must keep working.packages/web/test/testsFeatureDoors.test.ts— every door into the tests feature.
ruleAggregates:
SRL GROUP aggregation
FEATURE_RULE_AGGREGATES, default
off.
The flag decides whether the server parses GROUP, the
aggregation element of w3c/data-shapes#1347.
See the SRL reference.
The flag exists because the pull request is not merged yet; once it is,
GROUP becomes part of SRL and the flag goes. There is no
per-version switch beside it, because a GROUP is entirely
inside the rule text.
With the flag off, GROUP is a syntax error wherever the
server parses a rule: saving a rule, the rule set analyzer and SRL
preview, the SRL import, the rules playground and stratification. The
message names the flag's parse option:
SRL syntax error: GROUP requires the rule-aggregates extension (parse with { aggregates: true })
A rule saved while the flag was on keeps its compiled program, and
its rule set version keeps a stored stratification report, so a run can
reach the rule without parsing it. The executor checks each rule for
GROUP before running it, and with the flag off the run ends
with status failed and the error:
RuleVersion <id> uses the rule-aggregates extension, which is not enabled on this server
The W3C conformance checks parse with GROUP off whatever
the flag says, so a document using it is not reported as conformant
while the pull request is open.
The SPA reads NUXT_PUBLIC_FEATURE_RULE_AGGREGATES but
draws nothing different on it.
The browser build
packages/web is built ahead of time, so its flags are
resolved at build time by packages/web/nuxt.config.ts. For
each flag it reads NUXT_PUBLIC_FEATURE_<NAME> first
and falls back to the plain FEATURE_<NAME>, then
hands the result to the same buildFeatureFlags the API
uses, so an unset variable lands on the same default in both halves.
NUXT_PUBLIC_FEATURE_ETL ?? FEATURE_ETL ?? the default (off)
The Justfile recipes show the pairing:
run-local-rules-tests sets FEATURE_* for the
API and run-frontend-rules sets the matching
NUXT_PUBLIC_FEATURE_* for the SPA, because the two are
separate processes and each reads its own environment. Setting them only
on the API would leave the rail showing sections the API no longer
serves.
Overriding a deployed static build
A built SPA can be re-flagged without rebuilding. The plugin
packages/web/src/plugins/runtime-config.client.ts fetches
/config.json at startup and merges what it finds over the
build-time values:
{
"apiBaseUrl": "https://sqlib.example.org",
"featureFlags": { "etl": false, "assistant": false }
}Keys are the flag keys, not the environment variable names, and a
partial object is merged over the build-time flags rather than replacing
them. The same file also overrides authIssuer,
authClientId, authAudience,
authScope, authTokenStorage and
authRequireLogin. A missing file, a response that is not
JSON, or a parse failure logs a warning and leaves the build-time values
in place, so a deployment that serves no /config.json
behaves exactly as it was built.
This changes the browser only. The server's own flags come from its
environment, and a flag turned on in /config.json that the
API has off produces a section whose requests answer
404.