<auth>
Per-role JS-mount / code-split constraint. Wraps page subtrees whose client behaviour should mount only for specific roles. The compiler emits per-role JS bundles automatically — anonymous visitors download strictly smaller initial JS artifacts.
Not a content-secrecy control.
<auth role> gates JS
mount/behaviour only — it does not withhold
served HTML. Gated markup ships verbatim in the HTML payload to
every viewer (view-source), even under
--emit-per-route. The
compiler fires
W-AUTH-CONTENT-NOT-GATED
on every <auth role=>
site. To withhold sensitive content, gate it server-side
(see Semantics).
Syntax
<auth role="Admin">
<page path="/admin" />
...
</auth>
role=
takes a string literal, a variable reference, or a
${expr}
interpolation. Closed-form predicates (variant literals,
literal comma-OR, const-refs to role-set values, boolean
composition of statically-known operands) ship per-role
bundles via the artifact splitter. Anything else falls
back to a runtime gate and emits
W-AUTH-RUNTIME-FALLBACK.
Worked example
A small app with two public pages and one admin-only page. Anonymous visitors get a chunk without any of the admin subtree's atoms.
type Role:enum = { Anonymous, User, Admin }
<program auth="required" loginRedirect="/login">
<page path="/" />
<page path="/about" />
<auth role="Admin">
<page path="/admin" />
<page path="/admin/users" />
</auth>
</program>
The compiler analyzes the closure of each page across the
three roles and emits three sets of JS chunks. Anonymous
visitors load the public pages and the login redirect; the admin
subtree's JS atoms (mount + wiring) are absent from their
bundle. The served HTML, however, still carries the admin markup
verbatim — chunk variance role-splits behaviour, not
content. Per §40.9.9 the chunk variance is observable
in the generated
chunks.json
manifest.
Semantics
-
A JS-mount gate, not a content gate.
<auth role> is a code-split / mount constraint the closure
analysis honors at chunk-splitting time. It is NOT a
content-secrecy control and NOT a runtime check on its own
— the served HTML carries the gated markup for every
viewer. To withhold sensitive content, gate it
server-side: put it behind a
server functionthat resolves the authenticated role and returns/renders it only for admitted roles, so non-admitted markup never enters the response body. Request-boundary route guards (<program auth="required">) are the other server-side layer. -
Closed-form vs runtime-fallback.
Closed-form predicates ship per-role bundles. Runtime-
fallback predicates (reactive reads, async server-fn
calls, arbitrary
${expr}) emit W-AUTH-RUNTIME-FALLBACK and ship the gated component eagerly with a runtime gate. Per §40.9.5. -
Roles are an enum. The
role=value must resolve to a variant of an enum reachable from the entry file. Ambiguous role-enum discovery fires E-AUTH-GRAPH-002. Unknown role variants fire E-AUTH-GRAPH-003. -
<program auth=> sets the default.
<program auth="required">gates every page that isn't otherwise marked public. IfloginRedirect=isn't set AND no/loginpage exists, the compiler fires W-AUTH-LOGIN-MISSING. -
Page-level inference. A <page>
inside an <auth> without its own explicit
auth=attribute inherits the wrapper's role. The compiler emits W-AUTH-PAGE-INFERRED info-level so the inference is auditable. -
Redirect cross-ref check.
loginRedirect=strings that don't resolve to a known route emit I-AUTH-REDIRECT-UNRESOLVED info-level. Surfaces typos and missing scaffold pages.
Errors this feature can fire
- E-AUTH-GRAPH-002 — ambiguous role-enum discovery
- E-AUTH-GRAPH-003 — role variant not in resolved enum
- W-AUTH-RUNTIME-FALLBACK — non-closed-form predicate
- W-AUTH-PAGE-INFERRED — inherited role
- W-AUTH-LOGIN-MISSING — auth=required without login route
- I-AUTH-REDIRECT-UNRESOLVED — redirect to unknown route
- W-AUTH-CONTENT-NOT-GATED — security footgun: gates JS mount only, not served HTML content
Edge cases
-
Closed-form role predicates. Boolean
composition of literals
(
role="Admin"), literal comma-OR (role="Admin,Moderator"), or const-refs to role-set values are closed-form. Anything else — reactive reads, async calls, arbitrary${expr}— falls back. Per §40.9.5. -
Full interpolation grammar.
role=${expr}works (S90 OQ-A3-A ratification — first-class compiler state should have the same surface as user-defined state). The closure analyzer constant- folds where it can; non-foldable cases trigger runtime fallback. -
Channel auth is binary.
<channel auth="required">gates the WS endpoint to authenticated users only. Per-role channel partitioning rides ontopic=(per the <channel> reference page). -
Anonymous visitors. An anonymous
role variant
(e.g.,
Role.Anonymous) is treated like any other role — the chunks for anonymous visitors omit all subtrees gated to authenticated roles. Per OQ-A2-F implicit-anonymous ratification.
Related features
-
<program>
—
auth=on <program> sets the app-wide default (required / public). Pair withloginRedirect=. -
<page>
— individual page route. Inherits the wrapping
the auth element's role unless it has its own
auth=. Reference page queued. -
chunks.json
— the per-route per-role manifest the compiler
emits. Inspect with
bun scrml buildoutput. Documents the per-role chunk variance. -
scrml generate auth
— CLI command that scaffolds a default
/loginpage whenauth="required"is set on <program> but no login route exists.
Availability
| Surface | Since | Notes |
|---|---|---|
| <auth role=> first-class element | v0.3.0 (Approach A — S91) | A-3 AuthGraph + per-gate classifier + redirect cross-ref |
| Per-route per-role chunks | v0.3.0 (A-4 splitter — S91) | 7 sub-phases; FNV-1a content-addressed chunk filenames |
| Full interpolation grammar (role=${expr}) | S90 OQ-A3-A user override | First-class compiler state matches user-defined state surface |
| scrml generate auth scaffold | S94 (auth-redirect tightening) | Generates /login page when auth=required and route missing |
Specification
This page summarizes SPEC §40.1 (<auth> first-class element), §40.9 (AuthGraph + per-gate classifier), and §40.9.9 (worked example). The normative text lives at compiler/SPEC.md in the scrmlTS repository.
Quick-lookup anchors: SPEC-INDEX.md entries — auth-graph, per-route artifact splitter, chunk-side runtime helpers, FNV-1a content-addressing.