Direct application access
How browsers and platform clients can execute or subscribe to operator-published queries without an application query proxy.
NYXDB can expose a deliberately narrow, read-only application surface directly to a browser, mobile application, edge worker, or other platform client. The operator publishes a typed query, configures how an application token becomes a trusted claims row, and optionally attaches per-user limits. The client invokes the published surface by name; it never sends the SQL body.
Application endpoints, auth providers, access policies, rate limits, and the
WebSocket protocol are Experimental. The native server speaks raw TCP and
ws:// without TLS and accepts WebSocket origins at the transport layer.
Direct public or zero-trust production exposure is therefore unsupported.
Keep the engine on a trusted network and terminate TLS plus origin and network
policy at a reviewed edge before any untrusted client can reach it. Start the
engine with --require-auth for every browser-facing or otherwise untrusted
listener; without that flag, a client can skip OP_AUTH and retain the
implicit operator SQL and write surface.
What “no proxy” means
The application does not need a custom service whose only job is to accept an API request, interpolate parameters, run SQL, and relay rows. The database owns that query boundary:
browser / mobile / edge client
|
| WebSocket binary frames
v
external TLS + origin/network policy required on an untrusted network
|
v
NYXDB OP_AUTH
|
+-- token -> trusted claims row
v
OP_ENDPOINT_EXEC or OP_ENDPOINT_SUB
|
+-- typed client parameters
+-- server-injected auth claims
v
published SELECT / STREAM SELECT
|
+-- one-shot TSV result
+-- or snapshot followed by live deltasRemoving the application query proxy does not remove the need for a security perimeter. Native TLS termination, a stable public wire contract, Origin enforcement, cross-node continuity, and a zero-trust deployment profile do not ship in the current engine.
Two identity planes
Application users and database operators are intentionally different principals.
| Plane | Identity source | Connection behavior | Authorization and limits |
|---|---|---|---|
| Application end user | A row returned by an operator-defined auth-provider query over the customer data model | May use one-time capability setup, ping, auth, endpoint execute, and endpoint subscribe; raw SQL and block ingest are refused | Published endpoints carry their pre-authorized query; invocable views require application grants. Endpoint and query-driven limits apply. |
| Database operator | An engine-managed system.users entry authenticated with a password | May use the SQL and operational wire surfaces after operator authentication | admin has implicit management access. Other named operators are default-deny against the separate base-relation operator-grant catalog for raw query/stream and endpoint/view execute/subscribe operations. All operator-class calls remain exempt from application endpoint quotas. |
The application role claim and an operator role with the same spelling share
no state. A grant in one plane cannot authorize the other plane.
If --require-auth is omitted, an unauthenticated connection remains an
implicit operator with the complete SQL surface. Creating auth providers for
application users does not close that operator path. Operator authentication
and end-user authentication solve separate problems. --require-auth is a
mandatory launch control for every listener reachable by an untrusted or
browser client.
Published endpoint versus granted view
NYXDB provides two application-facing invocation models through the same wire operations.
Published endpoint
CREATE ENDPOINT is an explicit API contract. It bundles:
- a one-shot
SELECTor liveSTREAM SELECTbody; - an ordered, typed client-parameter signature;
- an optional set of trusted auth claims;
- a provider reference used to validate the claim schema at definition time, when claims are declared; and
- six static governance settings.
An endpoint is pre-authorized by its definition. The operator must put every
tenant or visibility predicate in that body and bind the sensitive values from
$auth.*. The client cannot inspect the body or supply an auth claim.
USING provider does not pin invocation to that provider. A session admitted
by any provider can invoke the endpoint when its claim names and types are
compatible. Treat the provider reference as schema validation, never as an
authorization boundary.
Granted invocable view
A logical view may declare typed ${name:Type} parameters and reference
${auth.claim} values. An authenticated application user can invoke that view
by name or relation UUID only when the application grant catalog contains the
required verb:
readfor one-shot execution; orsubscribefor a live subscription.
The default is deny. GRANT ... TO ROLE writes the application domain;
GRANT ... TO OPERATOR ROLE writes a different operator domain.
Endpoint names resolve before view names. If both catalogs contain the same name, the endpoint wins and its pre-authorized definition bypasses the view grant the caller may have expected. Keep names collision-free and use a relation UUID when a client must target one logical view unambiguously.
The current application wire exposes read and subscribe operations only. It
does not expose arbitrary SQL or an application-user write operation, even
though the policy catalog accepts table, view, and materialized-view targets and
reserves other verbs. Only logical-view read and subscribe grants are
consumable by the end-user wire today; other target/verb rows are future-facing
catalog vocabulary.
That application default-deny statement applies to end-user view invocation.
Named non-admin operators use the separate operator policy: raw SQL and the
canonical stored query behind OP_ENDPOINT_EXEC or OP_ENDPOINT_SUB require
read or subscribe on every scanned base relation. Admin and default-open
implicit operators bypass this check. All operator-class invocations remain
exempt from application endpoint quotas.
Trusted claims and untrusted parameters
The parameter boundary is the central isolation property:
| Value | Supplied by | Binding rule |
|---|---|---|
Endpoint $symbol or view ${symbol:String} | Client | Positional text value, validated and coerced against the declared type before execution |
Endpoint $auth.tenant_id or view ${auth.tenant_id} | Server | Taken from the authenticated claims row; absent or incompatible claims fail the invocation |
| Endpoint or view SQL body | Operator | Stored in the catalog and never accepted from the end-user connection |
For endpoints, client parameters occupy the declared leading slots and claims are appended server-side. For invocable views, the engine builds the complete slot vector from the parameter and claim declarations. In both cases, a client cannot forge a claims slot by sending an extra parameter.
Every provider should return a stable, non-secret user_id claim. It is used
for rate-limit keys and subscriber attribution. If it is absent, the engine
falls back to the presented bearer token as the application-user identity, and
that fallback value can surface in subscriber and governance telemetry. Never
use a bearer token as a display identity. A role claim is the key for
application grants; a session without one cannot match a non-empty role and is
therefore denied on a granted view.
One-shot and live query paths
OP_ENDPOINT_EXEC executes a published one-shot query and returns a header plus
rows. OP_ENDPOINT_SUB establishes a stream and returns an initial snapshot
followed by updates until the subscription ends.
Channel identity includes the canonical query and the full binding vector, including server-injected claims. Subscribers with the same effective query and bindings can share one execution. Different tenant claims produce different bindings and do not share that channel.
Shared execution does not create a durable cursor. A connection loss, revocation, source drop, server restart, or terminal error ends the subscription. Reconnect, authenticate again, open a new subscription, and treat its initial snapshot as a replacement for the abandoned client replica.
Catalog mutation is not subscription revocation. Replacing or dropping an endpoint does not rebind or terminate an existing subscription; a drop also removes its governor and metering state. Operators must drain or terminate live subscriptions before changing a query, provider, claims, or limits when the old behavior must stop.
Authentication choices
| Provider form | Behavior | Revocation boundary |
|---|---|---|
| Lookup provider | Runs a parameterized point query where $token is the presented token; exactly one row becomes the claims set | The result is cached for cache_ttl_ms and revalidated after that age when the connection invokes again |
| Streaming auth gate | Maintains a token-keyed live replica from STREAM SELECT; the remaining columns are claims | A token leaving the replica revokes every admitted connection at event latency; time predicates are also re-evaluated by the gate ticker |
No configured provider means no successful end-user authentication. Dropping a streaming provider revokes its sessions, but a provider referenced by an endpoint cannot be dropped until those endpoints are removed or replaced.
Changing a streaming-gate claim row affects future invocations, not an existing
subscription's frozen bindings, user_id, tenant, or role. Remove the old token
to revoke its connections, issue a new token, and require reconnect when an
identity or authorization claim changes. Likewise, OP_AUTH is not logout: a
failed re-authentication does not clear the socket's prior principal. Close the
socket and authenticate once on a fresh connection for every principal or token
change.
Governance placement
Static endpoint settings and query-driven rate limits are enforced in the
single nyxdb process. They cover endpoint/user admission and per-subscription
delivery. Gateway placement, cross-node totals, and cluster-wide quota
coordination are not shipped capabilities.
Operators can inspect live definitions and counters through
system.endpoints, access policies through system.grants, and enforcement
events through the volatile system.governance_log. See
Application access SQL,
Browser and platform clients, and
Operator governance for executable procedures.