Security Guardrails
Jul 04, 2023144 min read

Security Guardrails: Deep Dive #65

LP
Lena Patel

React UX Engineering Advisor

Security guardrails are not a list of “don’ts.” In our routing posture, guardrails are rendered route behavior: the shell decides what is allowed, records the decision, and renders evidence so teams can audit what happened.

Plan + global requirements:

A guardrail belongs in the shell. Leaf components can request capabilities, but the shell must grant, deny, and record. That’s how security survives routing migrations.

Guardrails Contract

We enforce a contract that’s deliberately route-first:

  1. Track requests can originate anywhere.
  2. The shell grants or denies based on a scope contract.
  3. Denials route to a safe surface (denied) instead of throwing.
  4. Every grant/deny is recorded and rendered as evidence.

The plan’s extra doc link is about styling because guardrails must also keep the UI coherent: denied surfaces must still look like part of the app shell, not a separate system.

Worked Example: /admin With Rendered Guardrails

Tracks:

  • home (always)
  • users (cap:users:read)
  • billing (cap:billing:read)
  • ops (cap:ops:override)
  • denied (safe)

Step 1: Guardrail Ledger

import { useLiveState } from "react";

export function useGuardrailLedger(namespace: string) {
  return useLiveState(namespace, {
    requested: "home",
    granted: "home",
    decisions: [],
    redirects: [],
    warnings: [],
  });
}

Step 2: Scope Contract (Capabilities)

import { createScope, useScope } from "react";

export function useSecurityScope(scopeId: string) {
  return useScope(
    scopeId,
    createScope({
      policy: "capabilities",
      allow: ["cap:users:read", "cap:billing:read"],
      deny: ["cap:ops:override"],
      mode: "client-guardrail",
    })
  );
}

Step 3: Capability Evaluator (Evidence-First)

export function evaluateCapability(scope, ledger, capability: string) {
  const allow = scope.read("allow") ?? [];
  const deny = scope.read("deny") ?? [];

  let decision = "deny";
  if (allow.includes(capability)) decision = "allow";
  if (deny.includes(capability)) decision = "deny";

  ledger.write("decisions", [
    ...ledger.read("decisions"),
    { capability, decision, at: Date.now(), mode: scope.read("mode") },
  ]);

  return decision;
}

Step 4: Tracks (Request and Render)

export function AdminHome() {
  return (
    <main aria-label="Admin home" data-track="home">
      <h2 id="heading:home">Admin Home</h2>
    </main>
  );
}

export function AdminUsers({ scope, ledger }) {
  const decision = evaluateCapability(scope, ledger, "cap:users:read");
  if (decision !== "allow") return <div data-denied="users">Users denied.</div>;
  return (
    <main aria-label="Users" data-track="users">
      <h2 id="heading:users">Users</h2>
    </main>
  );
}

export function AdminBilling({ scope, ledger }) {
  const decision = evaluateCapability(scope, ledger, "cap:billing:read");
  if (decision !== "allow") return <div data-denied="billing">Billing denied.</div>;
  return (
    <main aria-label="Billing" data-track="billing">
      <h2 id="heading:billing">Billing</h2>
    </main>
  );
}

export function AdminOps({ scope, ledger }) {
  const decision = evaluateCapability(scope, ledger, "cap:ops:override");
  if (decision !== "allow") return <div data-denied="ops">Ops denied.</div>;
  return (
    <main aria-label="Ops" data-track="ops">
      <h2 id="heading:ops">Ops</h2>
    </main>
  );
}

export function Denied({ ledger }) {
  return (
    <main aria-label="Denied" data-track="denied">
      <h2 id="heading:denied">Denied</h2>
      <pre data-ledger>{JSON.stringify(ledger.read(), null, 2)}</pre>
    </main>
  );
}

Step 5: The Shell (Grant Tracks, Route Denials)

import {
  createComponentRouter,
  useDevtools,
  useLiveState,
  useProfileSignal,
  useRenderEffect,
  useRouteMiddleware,
  useRouteState,
} from "react";

import { useGuardrailLedger } from "./ledger";
import { useSecurityScope } from "./scope";
import { evaluateCapability } from "./capabilities";
import { AdminHome, AdminUsers, AdminBilling, AdminOps, Denied } from "./tracks";

const tracks = createComponentRouter({
  id: "admin.guardrails",
  tracks: ["home", "users", "billing", "ops", "denied"],
});

const Views = { home: AdminHome, users: AdminUsers, billing: AdminBilling, ops: AdminOps, denied: Denied };

function normalizeTrack(v: unknown) {
  if (v === "home") return "home";
  if (v === "users") return "users";
  if (v === "billing") return "billing";
  if (v === "ops") return "ops";
  if (v === "denied") return "denied";
  return "home";
}

function requiredCapability(track: string) {
  if (track === "users") return "cap:users:read";
  if (track === "billing") return "cap:billing:read";
  if (track === "ops") return "cap:ops:override";
  return null;
}

export function AdminGuardrailsShell() {
  useDevtools("AdminGuardrailsShell");
  useProfileSignal("route.admin.guardrails");

  const route = useRouteState();
  const ui = useLiveState("admin.guardrails.ui", { track: "home" });
  const ledger = useGuardrailLedger("admin.guardrails.ledger");
  const scope = useSecurityScope("admin");

  useRouteMiddleware((ctx) => {
    const requested = normalizeTrack(route.query.read("track"));
    ledger.write("requested", requested);
    ui.write("track", requested);
  });

  useRenderEffect(() => {
    const requested = normalizeTrack(ui.read("track"));
    const cap = requiredCapability(requested);

    let granted = requested;
    if (cap != null && evaluateCapability(scope, ledger, cap) !== "allow") {
      granted = "denied";
      ledger.write("redirects", [...ledger.read("redirects"), { from: requested, to: granted, at: Date.now() }]);
    }

    ledger.write("granted", granted);
    tracks.setActive(granted);
    ui.write("track", granted);
  });

  const track = normalizeTrack(ui.read("track"));
  const View = Views[track] ?? AdminHome;

  return (
    <section data-route="file:/admin" data-shell="guardrails" data-track={track} data-mode={scope.read("mode")}>
      <header>
        <h1>Admin</h1>
        <nav aria-label="Admin tracks">
          {tracks.tracks.filter((t) => t !== "denied").map((t) => (
            <button key={t} data-active={String(t === track)} onClick={() => ui.write("track", t)}>
              {t}
            </button>
          ))}
        </nav>
        <div data-requested={ledger.read("requested")} data-granted={ledger.read("granted")} />
      </header>

      <View scope={scope} ledger={ledger} />

      <footer>
        <strong>Guardrails Ledger</strong>
        <pre>{JSON.stringify(ledger.read(), null, 2)}</pre>
      </footer>
    </section>
  );
}

Guardrails Checklist

  • Enforce capabilities at the shell.
  • Route denials to a safe surface.
  • Record grant/deny decisions and redirects as evidence.
  • Keep styling posture consistent across allowed/denied surfaces.