Auth & RBAC
SemiLayer uses a layered security model: API keys authenticate the tenant (which environment you're operating against), and optional user JWTs enforce per-row access rules. Together they support everything from a simple public read to complex row-level security.
API Key Types
Each Environment has its own set of API keys. Keys are scoped by environment and by role.
| Prefix | Type | Can do |
|---|---|---|
sk_dev_ | Secret, development | Full read + write access in development |
sk_live_ | Secret, production | Full read + write access in production |
pk_dev_ | Public, development | Read-only, subject to Lens access rules |
pk_live_ | Public, production | Read-only, subject to Lens access rules |
ik_dev_ | Ingest, development | Ingest webhook trigger only — cannot query |
ik_live_ | Ingest, production | Ingest webhook trigger only — cannot query |
Secret keys (sk_) bypass all Lens-level access rules. Use them on your backend.
Never expose them client-side.
Public keys (pk_) enforce every access rule you declare on the Lens. Safe to
include in frontend bundles. If a rule is not met, the request returns 403.
Ingest keys (ik_) can only hit the ingest webhook endpoint (POST /v1/ingest/:lens).
They cannot call search, similar, query, or stream.
Manage keys in the Console under Environments → API Keys, or with the CLI:
Access Rules
Access rules are declared per-Lens in sl.config.ts under rules. They gate search,
similar, query, and subscribe operations.
Rule Types
'public' — No authentication required. Requests with pk_ keys and no user token
are allowed.
'authenticated' — A valid user JWT (X-User-Token header or ?userToken=) is required.
The JWT is validated against your JWKS endpoint.
ClaimCheck — The user JWT must contain specific claims.
Function rule — Arbitrary logic. Returns true (allow), false (deny), or
{ filter: {...} } (allow with a metadata filter applied to results).
Function rules are serialized and stored in the database. They are evaluated server-side on every request. Only use serializable logic (no closures over external variables).
Per-Operation Rules
You can set different rules for different operations:
query is disabled by default and must be explicitly enabled. All other operations
default to 'public' when no rule is set, but require the lens to have the appropriate
facet declared.
Dual-Token Pattern
SemiLayer uses a dual-token pattern that separates the tenant identity (API key) from the end-user identity (JWT). This lets you use a single API key per environment while enforcing per-user access rules.
Your auth provider issues the user JWT (Auth0, Clerk, Supabase Auth, your own system — anything that implements OIDC). Configure the JWKS URL once per Environment, and SemiLayer validates every user token against it.
Configure JWKS
In sl.config.ts:
BeamClient — Attaching a User Token
When using the generated Beam client, call withUser on the BeamClient instance inside
Beam, then pass it to a fresh Beam construction. Or use BeamClient directly:
Row-Level Security
Access rule functions can return a filter object that is merged into every query's
metadata WHERE clause. This restricts results to rows the user is allowed to see.
Now any search request returns only records where metadata.ownerId === claims.sub.
The filter is applied server-side and cannot be bypassed by the client.
Combined with the query operation:
RBAC Decision Flow
Frontend Safety Checklist
- Use
pk_keys in browser-side code, neversk_ - Declare explicit
rulesfor every operation you expose publicly - Set
minScoreon search to prune low-relevance results client-side - Use
ik_keys in ingest webhook callers — they cannot query your data - Never log or expose
sk_orik_keys in client-side bundles