sqlib Docs HomeGet startedGitHub
Reference

Configuration reference

Every environment variable the server reads, with the default that applies when it is unset and what setting it changes. Defaults here are taken from the code that reads each variable.

Two processes read this environment and they are not the same process. The API (packages/api) reads PORT and FASTIFY_ADDRESS; the MCP runtime (packages/mcp-server), which mounts the API in-process, reads HTTP_PORT, HTTP_HOST and the MCP_* variables. The container starts one of them according to APP_MODE. The web app is a separate static build with its own NUXT_PUBLIC_* variables, listed at the end.

For the reasoning behind these choices rather than their values, see running and configuring and deploying.

Server and transport

Name Default Effect
APP_MODE dual-http (set in the image) Read only by the container's start command. api runs packages/api/dist/index.js; mcp-http runs the MCP runtime with MCP_TRANSPORT=streamable-http; dual-http, and any other value, runs it with MCP_TRANSPORT=dual-http. Outside the container nothing reads it.
MCP_TRANSPORT unset: streamable-http when NODE_ENV=production, otherwise stdio Transport for packages/mcp-server. Accepts stdio, http or streamable-http, and dual-http or http+api. dual-http serves the REST API and MCP at /mcp on one port.
HTTP_PORT 3000 Listening port in dual-http mode.
HTTP_HOST 0.0.0.0 Listening address in dual-http mode. Falls back to MCP_HTTP_HOST when unset.
MCP_HTTP_PORT 3333 Listening port in streamable-http mode (MCP only, at /mcp).
MCP_HTTP_HOST 0.0.0.0 Listening address in streamable-http mode.
MCP_CATALOGUE both Which surfaces the server offers: both, catalogue or published. catalogue serves no published tools (/mcp/{slug} and /tools/… are 404). published serves only the published tools: /mcp lists them alone, every authoring route is 404, POST /execute runs only their pinned versions, and an invalid published tool stops the server at startup instead of being skipped. Anything else throws. See publishing tools.
SQLIB_CORS_ORIGINS unset: http://localhost:3001,http://127.0.0.1:3001 (the SPA dev server) when NODE_ENV=development, otherwise none Browser origins allowed to read API and /mcp responses, comma-separated. A listed origin is echoed in access-control-allow-origin with access-control-allow-credentials: true; * allows every origin and never sends credentials; an origin not on the list gets no CORS headers, and its preflight gets 404. Set to the empty string to allow none even in development. A production deployment whose SPA is served from another origin than the API must list that origin. The API (packages/api/src/config/cors.ts) and the MCP transport read it the same way, since in dual-http and streamable-http modes they share one instance; /mcp answers its own OPTIONS /mcp preflight, with the MCP headers, and the API's plugin leaves that path alone.
PORT 3000 Listening port when packages/api is started directly (APP_MODE=api). A value that does not parse as a number falls back to 3000.
FASTIFY_ADDRESS 0.0.0.0 Listening address when packages/api is started directly.
APP_BASE_PATH empty Mounts every API route under this prefix. A leading slash is added and trailing slashes are stripped; / means no prefix.
APP_PUBLIC_BASE_PATH value of APP_BASE_PATH The prefix the server advertises in generated URLs, for a deployment behind a proxy that rewrites the path.
NODE_ENV unset Standard Node convention. It selects the MCP transport default above, switches OTLP export on in development (see telemetry), and suppresses OpenTelemetry logging when it is test.
MCP_DEBUG_PORT 3334 Port for packages/mcp-server's echo server (pnpm --filter @sparql-query-lib/mcp-server dev:debug-http), a development aid, not part of the served application.
MCP_DEBUG_HOST 127.0.0.1 Address for the same.

Library storage

The library — every entity you author — lives in a store chosen by INTERNAL_BACKEND_TYPE. This is separate from the backends your queries run against, which are entities you create through the API.

Name Default Effect
INTERNAL_BACKEND_TYPE sqlite sqlite, http, oxigraph-memory or oxigraph-persistent. Unset or empty means sqlite. Any other value throws at startup.
LIBRARY_STORAGE_SPARQL_ENDPOINT http://localhost:3030/sqlib/ With INTERNAL_BACKEND_TYPE=http, the SPARQL endpoint holding the library. The default is a local Fuseki on its conventional port, so an unconfigured server fails against localhost rather than reaching an external host.
LIBRARY_STORAGE_SPARQL_QUERY_ENDPOINT value of LIBRARY_STORAGE_SPARQL_ENDPOINT Query URL, when the store separates query from update.
LIBRARY_STORAGE_SPARQL_UPDATE_ENDPOINT value of LIBRARY_STORAGE_SPARQL_ENDPOINT Update URL, when the store separates query from update.
LIBRARY_STORAGE_SPARQL_USERNAME unset Basic-auth username for the library endpoint.
LIBRARY_STORAGE_SPARQL_PASSWORD unset Basic-auth password for the library endpoint.
LIBRARY_STORAGE_DIR ./storage/library-store, resolved against the process working directory With INTERNAL_BACKEND_TYPE=sqlite, the directory holding library.sqlite (and its -wal and -shm files); :memory: keeps the database in the process, for tests. With oxigraph-persistent, the directory holding the .nq serialisation of the library.
INTERNAL_OXIGRAPH_STORE_ID library-store Name of the persistent store within that directory.
INTERNAL_OXIGRAPH_CHECKPOINT_INTERVAL_MS 60000 How often the in-memory store is written back to .nq.
INTERNAL_OXIGRAPH_LOAD_METHOD none How the store is populated at boot: file, remote-sparql, remote-file or none. An unrecognised value falls back to none.
INTERNAL_OXIGRAPH_BOOTSTRAP_SOURCE unset A JSON object describing the source for that load method. Invalid JSON is logged as a warning and ignored.
OXIGRAPH_STORAGE_DIR ./storage/oxigraph Directory for in-process Oxigraph backends you create as entities — the stores behind oxigraphEphemeral and oxigraphMemory backends. It is not where the library lives unless INTERNAL_BACKEND_TYPE=oxigraph-persistent, in which case LIBRARY_STORAGE_DIR takes over for the library store.
ENABLE_OXIGRAPH false Set to true to initialise the Oxigraph store manager even when the library is not in Oxigraph, so that Oxigraph-backed backend entities work. It is initialised unconditionally when INTERNAL_BACKEND_TYPE=oxigraph-persistent.
DEBUG_OXIGRAPH false Set to true to log every SPARQL operation the in-process Oxigraph executor runs.

What oxigraph-persistent actually does

The Oxigraph JavaScript/WebAssembly bindings support in-memory stores only. There is no RocksDB-backed store as there is in the Rust and Python bindings. oxigraph-persistent is an in-memory store that is serialised to .nq files in LIBRARY_STORAGE_DIR on shutdown and every INTERNAL_OXIGRAPH_CHECKPOINT_INTERVAL_MS milliseconds, and restored from those files at startup. A process killed between checkpoints loses the writes made since the last one. oxigraph-memory keeps nothing.

For a store that is durable in the usual sense, use the default sqlite, or run a SPARQL server such as Oxigraph's own or Fuseki alongside and use INTERNAL_BACKEND_TYPE=http.

INTERNAL_OXIGRAPH_DB_PATH is no longer read. It named a RocksDB directory that these bindings cannot open; serialisation goes to LIBRARY_STORAGE_DIR.

See storage and caching for how the store and the in-memory cache relate.

Execution backend credentials

A Backend entity does not store credentials. It stores an authEnvKey, and the server looks up the credentials in the environment under that key. This keeps secrets out of the library store, which is exported, imported and copied.

authEnvKey must match ^[A-Z0-9_]+$. For a backend whose authEnvKey is <KEY>, the server reads, in this order:

Name Effect
SQLIB_BACKEND_<KEY>_USERNAME and SQLIB_BACKEND_<KEY>_PASSWORD Both present: HTTP Basic. The pair is base64-encoded into Authorization: Basic ….
SQLIB_BACKEND_<KEY>_AUTH_HEADER Used when the username/password pair is incomplete. Sent verbatim as the Authorization header, so it must include its scheme (Bearer …).
neither No Authorization header is sent.

<KEY> is the entity's authEnvKey upper-cased. A backend with authEnvKey: "qms_fuseki_dev" reads SQLIB_BACKEND_QMS_FUSEKI_DEV_USERNAME and SQLIB_BACKEND_QMS_FUSEKI_DEV_PASSWORD. A backend with no authEnvKey sends no credentials.

Name Default Effect
HTTP_SPARQL_USER_AGENT sparql-query-lib/adhoc User-Agent sent to HTTP SPARQL endpoints.
SQLIB_BACKEND_PROBE_TIMEOUT_MS 10000 Timeout for the reachability probe run when a backend is created or checked. Values that are not a positive finite number fall back to the default.
SQLIB_BACKEND_SLOW_MS 250 Above this round-trip time a probed backend is reported as slow.
SQLIB_PREFIX_DETECT on Set to off to skip detecting a backend's namespace prefixes during the probe. Any other value leaves detection on.
SQLIB_PREFIX_TIMEOUT_MS 10000 Timeout for the prefix-detection query. See prefixes.

Caching

Name Default Effect
CACHE_WRITE_THROUGH true Every entity write goes to the store as well as the in-memory cache. Set to exactly false to keep writes in memory only; any other value leaves it on. With it off, background refreshes from the store are skipped too.
CACHE_PRELOAD true Load every entity into the cache at startup. Set to false (case-insensitive) to skip the preload; with it off, background per-type refreshes are also skipped.
ENABLE_TIMING_LOGS true, and not changeable Intended to switch per-request timing logs. The expression that reads it is process.env.ENABLE_TIMING_LOGS === 'true' || true, so config.enableTimingLogs is always true and setting the variable has no effect.

Entity time-to-live in the cache is not configurable by environment: it is set per entity type in packages/api/src/lib/EntityRegistry.ts, where immutable versions are held indefinitely. See storage and caching.

ETL

ETL is off by default; see feature flags and the ETL guide.

Name Default Effect
ETL_OUTPUT_DIR ./storage/etl-output, resolved against the process working directory Where an ETL execution writes its RDF output files. The container creates this path with the right ownership, so it is writable on a fresh volume. Not written on a read-only deployment.
ETL_RESPONSE_OUTPUT_MAX_BYTES 1048576 The most RDF a run returns in its response on a read-only deployment, where it writes no file. A run whose output passes it fails, naming the cap, rather than returning part of its output.
SQLIB_ETL_NODE_MAX_ROWS 1000000 The most rows an ETL node inside a query group may read from its source. A source with more fails the node, naming the cap, rather than handing the rest of the group a truncated result. Running a job directly is not affected.

Kept results (pass-by-reference)

A run that sets keepResult has its response kept in server memory, and the response carries a Sqlib-Result-Ref handle. A later request names the handle (argumentRefs or dataGraphs[].resultRef on /execute, dataGraphResultRef on a rule set run) instead of sending the content again. Only responses the server produced are kept, and a handle is readable only by the subject that made the run. A missing, expired or foreign handle returns 410.

Name Default Effect
RESULT_CACHE_MAX_BYTES 268435456 (256 MiB) All kept results together. The oldest are dropped first when a new one would pass it. 0 turns the feature off: nothing is kept, and every handle returns 410.
RESULT_CACHE_MAX_BYTES_PER_USER 10485760 (10 MiB) One subject's kept results together. A larger single result is not kept; otherwise the subject's oldest are dropped first.
RESULT_CACHE_TTL_MS 900000 (15 minutes) Time after its last use at which a kept result is dropped.

A restart empties the cache.

Entity size limits

Name Default Effect
DATA_GRAPH_MAX_VERSION_BYTES 1048576 (1 MiB) Largest single data graph version.
DATA_GRAPH_MAX_LIBRARY_BYTES 16777216 (16 MiB) Largest total of data graph content in one library.
TUPLE_SET_MAX_VERSION_BYTES 1048576 (1 MiB) Largest single tuple set version.
TUPLE_SET_MAX_LIBRARY_BYTES 16777216 (16 MiB) Largest total of tuple set content in one library.

A value that does not parse as an integer falls back to the default.

Rule execution and patches

Name Default Effect
RULE_EXECUTION_TIMEOUT_MS 30000 Per-rule timeout during a rule set run. A rule is judged against it after it finishes; to stop a run, use SQLIB_EXECUTION_TIMEOUT_MS.
SQLIB_EXECUTION_TIMEOUT_MS 300000 (5 minutes) The most one query group run or rule set run may take. Checked before every group node and every rule and DATA block, and aborts HTTP requests to backends in flight; a run past it fails at the node it was on, with status 504 from /execute. It cannot interrupt a single query against an in-process Oxigraph store, which runs synchronously, so such a run stops at the next boundary after the deadline. 0 turns it off. A client that disconnects from /execute or a rule set's execute route cancels its run the same way.
SQLIB_MAX_RULE_ITERATIONS 1000 The most fixpoint iterations a rule set run may take per stratum. A request asking for more maxIterations is refused by the route schema (read at startup); the executor clamps to the value in force whatever it is asked.
RULE_EXECUTION_SAMPLE_LIMIT 10 How many sample bindings a rule run reports per rule.
PATCH_PREVIEW_TTL_MS 3600000 (1 hour) How long a derived, unapplied RDF patch preview is retained. See RDF patch.
PATCH_SWEEP_INTERVAL_MS 600000 (10 minutes) How often expired previews are swept.

Telemetry

Name Default Effect
OTEL_ENABLED true Set to exactly false to skip OpenTelemetry SDK startup and OpenTelemetry log export. Any other value leaves it on. With it off the SDK is not imported at all, which is worth about 220ms of startup.
OTEL_EXPORTER_OTLP_ENDPOINT http://localhost:4318 OTLP HTTP collector base URL. Traces go to /v1/traces and metrics to /v1/metrics.

OTLP export only happens when NODE_ENV=development. In any other environment the SDK starts with console exporters, which means an endpoint set in production is not used. Metrics are exported every 30 seconds.

Startup time

Relevant to a deployment that scales to zero, where a cold start sits in front of a user request rather than happening once a week.

Name Default Effect
SQLIB_EAGER_SERIALIZERS unset (lazy) Set to exactly true to compile every route's response serialiser while the server starts, instead of on that route's first response.
NODE_COMPILE_CACHE unset in a local run, /app/.v8-compile-cache in the container Node's own variable: a directory where it caches compiled module bytecode and reuses it next boot. The image ships a populated cache, so the first container to start is already warm.

Response-schema compilation is the largest single term in startup and it scales with the number of routes, not with the amount of data: on a 135-route instance it was ~890ms of a ~1.4s ready(), whether or not anything ever called those routes. Compiling each route's serialiser on its first response instead moves that off the boot path for ~2ms on that first response, which is why it is the default. SQLIB_EAGER_SERIALIZERS=true is the way back for a long-lived instance that would rather pay the whole bill before it accepts traffic.

Two other things help and are ordinary configuration rather than switches: turning off feature flags you do not serve removes their routes, and each route is worth roughly 10ms of startup; and OTEL_ENABLED=false avoids loading the OpenTelemetry SDK.

What does not help much is anything about Oxigraph itself: the WASM module instantiates in about 25ms. The data-dependent part of startup is the snapshot, which loads at roughly 180,000 quads per second, plus the cache preload — so a library of a few thousand entities is lost in the noise and one of a few hundred thousand quads is not. See what oxigraph-persistent actually does.

Read-only deployments

SQLIB_READ_ONLY=true turns sqlib into a compute service: it still parses, validates, formats and executes, but it refuses every write to its own state. The catalogue changes by redeployment, and anything a visitor authors is kept in their browser instead. It is what a public demo site runs.

Name Default Effect
SQLIB_READ_ONLY false Exactly true turns it on; any other value, including unset, leaves it off.

It is not an auth mode and does not need one. An auth mode answers "who is this, and what may they reach", which needs principals, grants and an issuer; this answers "may this deployment's own state change at all", which gives every caller the same answer. The two compose — a public site runs SQLIB_AUTH_MODE=disabled beside this, and the full-access context that mode mints still cannot write, because the gate consults no context.

The gate refuses every mutating method and then names its exceptions, so a route added later is refused until someone decides otherwise. A route that writes on a safe method declares it with config: { readOnlyMutating: true } in its route options, and is refused like an unlisted POST. The exceptions are the routes that compute an answer and store nothing: /detect-inputs, /detect-outputs, /validate, /validate-rule-data, /format, /substitute, /execute, /sparql, the SRL compile, analyse and preview routes, the rule and rule-set execution routes, the tuple-set preview, the two test-run routes, POST /etl-jobs/:id/execute, POST /benchmark-experiments/:id/versions/:version/run, and /mcp. The list lives in packages/api/src/config/readOnly.ts, and a test fails on an entry that names no registered route, on a mutating route that is neither listed nor refused, and on one of these exceptions losing its way back.

Four of those exceptions are worth their own sentence:

The caps on those runs, read only on a read-only deployment and reported by /health as readOnlyRunLimits:

Name Default Effect
SQLIB_READ_ONLY_BENCH_MAX_REQUESTS 200 The most requests one benchmark run may send: measured tasks plus warmups. A version over it is refused, not clamped, because a clamped benchmark reports numbers for a plan nobody saved.
SQLIB_READ_ONLY_BENCH_MAX_CONCURRENCY 2 The most a version's maxConcurrency may ask for.
SQLIB_READ_ONLY_BENCH_DEADLINE_MS 60000 The wall-clock limit for a whole benchmark run. When it passes, no task starts, the attempts in flight are aborted, and the run ends Aborted with what it finished.
SQLIB_READ_ONLY_BENCH_CONCURRENT_RUNS 1 Benchmark runs allowed at once on the deployment. disabled auth gives no client identity, so the count is deployment-wide.
SQLIB_READ_ONLY_ETL_MAX_ROWS 100000 The most source rows one ETL job run may read. A clamp rather than a refusal: a visitor running part of a source is asking for a sample.

Three things it deliberately does not do:

/health reports it as readOnly, beside the auth mode, so the SPA can offer browser-local authoring rather than a button the server will refuse.

The SPA draws no control whose only outcome is a refusal. Save and the entity ⋮ go from every work area, the benchmark and test chips go from the run bar, Save to Tuples, Save to Data and Save as test go from a rule set's Inputs tab, and the tag controls go from the Details panel — absent rather than disabled, the rule the rail already follows for a switched-off section. Everything acting on the browser's own copy stays: scratch queries, rule sets and argument sets, the values in them, Discard, and the inline data a rule set runs against. A parameterised query is authored and run the same way it is on a writable deployment, with the values sent inline to POST /sparql.

A visitor's own SPARQL endpoint is registered the same way. The backend picker in the run sentence takes a URL pasted straight into it — + SPARQL endpoint… at the foot of the menu — and keeps it as a browser backend: stored in that browser, named after its own URL, and never posted to the server. Renaming one, or changing its query method, is done under Backends like any other, and writes to the browser rather than to the server. Queries against it run from the browser to the endpoint, so the address, any headers and the results do not pass through sqlib. Browser backends are not a read-only feature — they work on any deployment — but they are what makes a read-only one usable against data it was not seeded with.

Before /health answers, and if it cannot be reached, the SPA assumes it may write. That direction is deliberate: a client that wrongly believes it may write shows a button and surfaces the server's refusal, while one that wrongly believes it may not hides a working feature and looks broken.

Authentication

Auth is off unless SQLIB_AUTH_MODE says otherwise. An invalid value for any of these throws at startup rather than falling back. See the security model for what the layer does and does not cover.

Name Default Effect
SQLIB_AUTH_MODE disabled disabled, dry-run (or dryrun), required or anonymous. An empty value is disabled. Anything else throws. dry-run and required both require an issuer, and an audience for every issuer unless SQLIB_AUTH_AUDIENCE_UNCHECKED=true. anonymous reads no token: every caller is urn:sqlib:principal:anonymous, holds the grants on that principal, and is refused everything else. For a public deployment beside SQLIB_READ_ONLY=true; seed the grants with SQLIB_AUTH_SEED_GRANTS.
SQLIB_AUTH_ISSUER unset OIDC issuer URL for a single-issuer deployment.
SQLIB_AUTH_AUDIENCE unset Expected aud for that issuer.
SQLIB_AUTH_AUDIENCE_UNCHECKED false Set to exactly true (case-insensitive) to start dry-run or required with an issuer that has no audience. Without an audience the aud claim is not checked, so any token that issuer minted for any application is accepted; without this flag that configuration throws at startup. Applies to SQLIB_AUTH_ISSUERS_JSON entries too.
SQLIB_AUTH_JWKS_URI from discovery JWKS URL. Unset, the jwks_uri of the issuer's OIDC discovery document (<issuer>/.well-known/openid-configuration), fetched once on the first token; an issuer with no discovery document falls back to <issuer>/.well-known/jwks.json. Set it when sqlib reaches the broker at another address than the issuer URL, as the compose auth profile does.
SQLIB_AUTH_CLAIM_GROUPS groups Dot-path to the claim carrying group or role membership.
SQLIB_AUTH_CLAIM_CLIENT_ID azp Dot-path to the claim used to recognise a machine-to-machine caller.
SQLIB_AUTH_ISSUERS_JSON unset A JSON array of issuer objects (issuer, audience — required in an enforcing mode unless SQLIB_AUTH_AUDIENCE_UNCHECKED=true — and optionally jwksUri, claimGroups, claimClientId), for more than one issuer. When set it replaces the single-issuer variables above. Invalid JSON, a non-array, an empty array or an entry without a string issuer throws. Per-entry defaults are groups and azp.
SQLIB_AUTH_CLOCK_SKEW_S 60 Allowed clock skew in seconds when validating a token. A negative or non-numeric value throws.
SQLIB_AUTH_ADMIN_PRINCIPALS empty Comma-separated raw claim values treated as administrators at boot, in the form iss|sub or iss|group:value.
SQLIB_AUTH_SEED_GRANTS unset Path to a file of grants loaded at startup.
SQLIB_AUTH_ALLOW_LIBRARY_CREATE all all or admin. Anything else throws.
SQLIB_AUTH_PROTECT_DOCS false Set to exactly true (case-insensitive) to require a token for the API documentation routes (/docs and everything under it). /, /health and the protected-resource metadata stay public regardless.
SQLIB_AUTH_RESOURCE the first issuer's audience The resource identifier: the resource field of the protected-resource metadata, and the value an MCP client asks a token for. Use the canonical URL of the deployment's /mcp endpoint (for example https://sqlib.example.org/mcp), and configure the broker to put that value in aud. When it is an absolute URL, the 401 challenge names the metadata at <its origin>/.well-known/oauth-protected-resource<its path>; otherwise at the request's origin. A value that matches no issuer's audience logs a warning at startup.
SQLIB_AUTH_STORE_TYPE sqlite, or sparql when SQLIB_AUTH_SPARQL_ENDPOINT is set Where grants and principals are kept. Never the library store: sqlite is one SQLite file, file one N-Quads file, sparql the urn:sqlib:auth graph of a SPARQL dataset of its own. Anything else throws. Not read when SQLIB_AUTH_MODE is disabled; read in every other mode, anonymous included.
SQLIB_AUTH_STORE_DIR ./storage/auth The directory of the sqlite and file stores. sqlite keeps auth.sqlite (with its -wal and -shm files): one row per quad, written per change in one transaction, and an auth_events table that records every quad each change added or removed. file keeps auth.nq, written whole and renamed into place on each change.
SQLIB_AUTH_SPARQL_ENDPOINT unset The SPARQL dataset of the sparql store. Required for that type. Use a dataset of its own, not the library store's.
SQLIB_AUTH_SPARQL_UPDATE_ENDPOINT the endpoint Its update endpoint, when it differs.
SQLIB_AUTH_SPARQL_USERNAME / SQLIB_AUTH_SPARQL_PASSWORD unset Basic credentials for that dataset.
SQLIB_AUTH_POLICY_CACHE_SECONDS 60 How long a backend graph policy's result is kept per backend and caller. 0 evaluates the policy on every read. A change to the policy, or any update to an auth backend, clears the cache. A negative or non-numeric value throws.
SQLIB_AUTH_SCOPES empty Space- or comma-separated scopes. Published as scopes_supported in the metadata and as scope in the 401 challenge. Empty leaves both out.

The API serves OAuth 2.0 Protected Resource Metadata (RFC 9728) at /.well-known/oauth-protected-resource, at every path-suffixed form of it (/.well-known/oauth-protected-resource/mcp), and, under APP_BASE_PATH, at <base path>/.well-known/oauth-protected-resource too. The document lists the configured issuers as authorization_servers. It is public in every mode, and answers 404 when no issuer is configured. Every 401 carries WWW-Authenticate: Bearer resource_metadata="…", which is how an MCP client finds the authorization server with no client configuration.

Environments

Name Default Effect
SQLIB_DEFAULT_ENVIRONMENT unset The environment a run uses when the caller names none, and the one published tools serve. Unset means latest, the current version. A library that defines no environments ignores it. See environments.

Feature flags

Seventeen FEATURE_* variables switch sections of the product on and off, and the API unregisters the routes behind some of them. They have their own page: feature flags, which lists each key, its variable, its default and what disappears when it is off.

Development and seeding

These are read by development recipes and seeders, not by a normal deployment.

Name Default Effect
SEED_W3C_RULES_SUITE unset Set to exactly true to seed the W3C SPARQL RL suite as runnable tests at startup. Requires the rulesSuite and tests flags. Seeding is idempotent.
W3C_RULES_SUITE_DIR the suite directory inside packages/api Where that suite is read from. A relative path resolves against the API package root.
SEED_PATCH_DEMO unset Set to exactly true to seed the RDF patch demo library at startup. Requires the queries flag.
SYSTEM_STORE_ASSET_DIR system-store inside packages/api Where the preloaded system library's assets are read from.
VITEST set by vitest Presence suppresses OpenTelemetry log export during tests.

The web application

packages/web is a Nuxt SPA built ahead of time (ssr: false), so these are read at build time and baked into the bundle.

Name Default Effect
NUXT_PUBLIC_API_BASE_URL http://localhost:3000 The API the app calls.
NUXT_PUBLIC_AUTH_ISSUER empty OIDC issuer for the browser's login flow. Empty, the app takes the first authorization_servers entry of the API's protected-resource metadata; set, it overrides that.
NUXT_PUBLIC_AUTH_CLIENT_ID empty OIDC client id.
NUXT_PUBLIC_AUTH_AUDIENCE empty Requested audience.
NUXT_PUBLIC_AUTH_SCOPE openid profile email Requested scopes. Add offline_access for a broker that issues refresh tokens only on request, and groups for Rauthy's group claim.
NUXT_PUBLIC_AUTH_TOKEN_STORAGE memory Where the signed-in user and its tokens are kept: memory (no web storage; a reload signs in again through the broker, without a form while the broker's session lives), session (sessionStorage: survives a reload in that tab) or local (localStorage: every tab, the behaviour before this setting). Anything else is memory. authTokenStorage in /config.json sets the same thing.
NUXT_PUBLIC_AUTH_REQUIRE_LOGIN false Exactly true makes the app sign in even when the API reports auth disabled, for an API behind a gateway that does the authentication. authRequireLogin in /config.json sets the same thing at load time.
NUXT_APP_BASE_URL / The path the site is served from. Written into every page's asset links, so the bundle works only at that path. Dockerfile.web takes it as a build argument. See deploying.
NUXT_PUBLIC_FEATURE_* the API-side default for that flag Per-flag override for the browser build; see feature flags.
NUXT_PUBLIC_APP_VERSION the version in .release-please-manifest.json What the About block on the splash and the line in Settings report.
NUXT_PUBLIC_APP_COMMIT git rev-parse --short HEAD, or empty The commit reported beside the version. Shortened to seven characters.
NUXT_PUBLIC_BUILD_TIME the moment of the build ISO timestamp shown in the version's tooltip.

The three build-stamp variables exist for builds that cannot work the values out for themselves: an image built from a source copy with no .git directory finds no commit, and one built outside the repo finds no manifest. Pass what the build system knows.

A deployed static build can be re-pointed without rebuilding by serving a /config.json beside it. The client plugin packages/web/src/plugins/runtime-config.client.ts fetches it at startup and overrides apiBaseUrl, the four auth* values and any subset of featureFlags. A missing file, a non-JSON response or a parse failure logs a warning and leaves the build-time values in place. See deploying.