Skip to main content
GUARD::ACTIVE

ACCESS
IS NOT
A STRING CHECK

Policies are data.

Compile-time safe.

Fully auditable.

npm install @hex-di/guard
EVALUATION TRACE

policy: canPublish
subject: user-042
decision: ALLOW

trace:
  allOf 
   hasRole("admin") 
   hasPerm("Write") 
   not(suspended) 

duration: 0.08ms
10policy kinds
0runtime deps
full audit trails
TS 5.6+TypeScript native

:: comparison

Before & After

RUNTIME_FAIL
// Scattered across codebase
if (user.role === "admin"
    || user.permissions
      .includes("Write")) {
  if (!user.suspended) {
    // allow... maybe?
  }
}
// No audit trail
// No composition
// No type safety
// Changes require grep + pray
COMPILE_OK
// Defined once, enforced everywhere
const policy = allOf(
  anyOf(hasRole("admin"),
    hasPermission(Write)),
  not(hasAttribute("suspended", true)),
);

const { granted, trace } = evaluate(policy, ctx);
// Full audit trail
// Composable & reusable
// Compile-time type safe
// Changes in one place

:: policy kinds

10 Policy Kinds

Every authorization pattern you need, as a composable value.

1

hasPermission

hasPermission(Permissions.Write)

Check if subject holds a specific permission token.

2

hasRole

hasRole("admin")

Check if subject has been assigned a role.

3

hasAttribute

hasAttribute("department", "engineering")

Match a subject attribute against an expected value.

4

hasResourceAttribute

hasResourceAttribute("visibility", "public")

Match a resource attribute against an expected value.

5

hasSignature

hasSignature("electronic")

Verify electronic signature type — 21 CFR Part 11.

6

hasRelationship

hasRelationship("owner")

Check subject-resource relationship — ReBAC pattern.

7

allOf

allOf(policy1, policy2, policy3)

All child policies must grant. Short-circuits on first deny.

8

anyOf

anyOf(policy1, policy2)

At least one child must grant. Short-circuits on first allow.

9

not

not(hasAttribute("suspended", true))

Inverts the child policy decision.

10

withLabel

withLabel("audit-check", policy)

Attaches a label for trace identification and filtering.

:: composition

Compose Into Any Shape

access-policy.ts
import {
  allOf, anyOf, not,
  hasPermission, hasRole, hasAttribute,
  hasRelationship, withLabel,
} from "@hex-di/guard";

const canPublish = withLabel("publish-access",
  allOf(
    // Must be an editor or admin
    anyOf(
      hasRole("editor"),
      hasRole("admin"),
    ),
    // Must have publish permission
    hasPermission(Permissions.Publish),
    // Must not be suspended
    not(hasAttribute("suspended", true)),
    // Must own the resource OR be admin
    anyOf(
      hasRelationship("owner"),
      hasRole("admin"),
    ),
  ),
);

:: evaluation

Full Decision Objects

  • granted — boolean result
  • trace — full evaluation path with timing
  • durationMs — total evaluation time
  • Every node in the tree is individually traceable
evaluate.ts
const decision = evaluate(canPublish, {
  subject: { id: "u-1", roles: ["editor"] },
  resource: { id: "doc-42", owner: "u-1" },
});

// decision.granted    → true
// decision.durationMs → 0.12
// decision.trace:
//   allOf [ALLOW 0.12ms]
//   ├── anyOf [ALLOW 0.03ms]
//   │   ├── hasRole("editor") → ALLOW
//   │   └── hasRole("admin") → DENY (skipped)
//   ├── hasPermission("Publish") → ALLOW
//   ├── not(hasAttribute("suspended")) → ALLOW
//   └── anyOf [ALLOW 0.02ms]
//       └── hasRelationship("owner") → ALLOW

:: react

First-Class React Support

<Can> Component

import { Can } from "@hex-di/guard/react";

function DocumentActions({ doc }) {
  return (
    <Can policy={canPublish} resource={doc}>
      <button>Publish</button>
    </Can>
  );
}
// Renders children only if policy grants

Declarative gate. Renders children only when the policy grants.

useCan() Hook

import { useCan } from "@hex-di/guard/react";

function DocumentPage({ doc }) {
  const { granted, loading } =
    useCan(canPublish, { resource: doc });

  if (loading) return <Spinner />;
  if (!granted) return <Forbidden />;

  return <Editor doc={doc} />;
}

Imperative hook for conditional rendering and loading states.

:: capabilities

Why Guard?

Compile-Time Policies

Policies are data, not decorators. Define authorization rules as composable values with full type safety at compile time.

Role DAG Inheritance

O(1) permission lookup via directed acyclic graph flattening. Define role hierarchies that resolve instantly.

10 Policy Kinds

hasPermission, hasRole, hasAttribute, hasResourceAttribute, hasSignature, hasRelationship, allOf, anyOf, not, withLabel.

Full Evaluation Traces

Every authorization decision is auditable. The Decision object carries the full evaluation trace, timing, and policy metadata.

Framework Agnostic

No decorators, no reflection, no magic. Pure functions and data structures that work everywhere TypeScript runs.

GxP Ready

21 CFR Part 11 compliance support. Electronic signatures, audit trails, and tamper-evident records.

:: ecosystem

Part of the HexDI Stack

Guard integrates seamlessly with the HexDI dependency injection ecosystem. Use it standalone or compose it with other libraries.

hex-diDI Core
@hex-di/resultResult Type
@hex-di/flowState Machines
@hex-di/sagaOrchestration
@hex-di/guardAuth & Permissions
@hex-di/queryData Fetching

Ready to secure your stack?

Replace scattered permission checks with composable, typed policies. Zero dependencies. Full TypeScript inference.