scrml.dev v0.8.0
<auth> · scrml reference
Reference › Elements

<auth>

Per-role JS-mount / code-split constraint. Wraps page subtrees whose client behaviour should mount only for specific roles. With scrml compile --emit-per-route (opt-in) the compiler also splits the JavaScript per role, so anonymous visitors download 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">
  ...markup whose client code mounts only for 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 nav bar with an admin-only link and button. Routes come from the pages/ directory, not from attributes; <auth> wraps markup. With per-route output on, anonymous visitors get a chunk without the admin subtree's code.

<program auth="required" loginRedirect="/login">

  type Role:enum = { Anonymous, User, Admin }

  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
    <auth role="Admin">
      <a href="/admin">Admin</a>
      <button onclick=clearCache()>Clear cache</button>
    </auth>
  </nav>

  function clearCache() { console.log("cleared") }

</program>

It compiles today with W-AUTH-CONTENT-NOT-GATED (and, until a /login page exists, W-AUTH-LOGIN-MISSING). Under --emit-per-route the compiler analyzes the closure of each page across the three roles and emits a set of JS chunks per role. 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 function that 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. If loginRedirect= isn't set AND no /login page 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

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 (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 on topic= (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 with loginRedirect=.
  • <page> — individual page route. Inherits the wrapping the auth element's role unless it has its own auth=. See <page>.
  • chunks.json — the per-route per-role manifest the compiler emits. Inspect with bun scrml build output. Documents the per-role chunk variance.
  • scrml generate auth — CLI command that scaffolds a default /login page when auth="required" is set on <program> but no login route exists.

Availability

Surface Since Notes
<auth role=> first-class element v0.3 A-3 AuthGraph + per-gate classifier + redirect cross-ref
Per-route per-role chunks v0.3 7 sub-phases; FNV-1a content-addressed chunk filenames
Full interpolation grammar (role=${expr}) v0.3 First-class compiler state matches user-defined state surface
scrml generate auth scaffold v0.3 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 scrml repository.

Quick-lookup anchors: SPEC-INDEX.md entries — auth-graph, per-route artifact splitter, chunk-side runtime helpers, FNV-1a content-addressing.