> For the complete documentation index, see [llms.txt](https://he-said.tdreyno.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://he-said.tdreyno.com/package-guides/acl-guide.md).

# ACL Guide

This guide covers the ACL package in `@tdreyno/he-said/acl`.

ACL here means:

* You define explicit `allow(...)` and `deny(...)` rules.
* Rules compile to core algebra constraints.
* Decisions use `can(actionToken, { subject, resource, environment? })`.
* Deny precedence is deterministic: deny first, then allow, then default deny.

## Getting Started

```ts
import {
  action,
  actionIs,
  allow,
  deny,
  enforcer,
  eq,
  failure,
  policy,
} from "@tdreyno/he-said/acl"

type User = {
  id: string
  suspended: boolean
}

type Document = {
  id: string
  ownerId: string
}

const READ = action("read")

const denySuspended = deny(
  eq((subject: User) => subject.suspended, true),
  {
    failure: failure("Suspended users cannot access documents."),
    priority: 100,
  },
)

const allowOwnerRead = allow([
  actionIs(READ),
  eq(
    (subject: User) => subject.id,
    (resource: Document) => resource.ownerId,
  ),
])

const acl = enforcer(policy(denySuspended, allowOwnerRead))

const decision = await acl.can(READ, {
  subject: {
    id: "u1",
    suspended: false,
  },
  resource: {
    id: "d1",
    ownerId: "u1",
  },
})

console.log(decision.allowed)
```

## Core Concepts

### Identity Tokens

Create identity tokens with optional labels:

```ts
const READ = action("read")
```

Action tokens are symbols and compare by reference.

### Rule Builders

* `allow(ruleOrRules, options?)`
* `deny(ruleOrRules, options?)`

`ruleOrRules` accepts:

* `Rule`
* `Rule[]` (normalized to `and(...rules)`)

### Generic Comparator Mapping

ACL v1 keeps one comparator primitive:

* `eq(leftSelector, value)`
* `eq(leftSelector, rightSelector)`

This avoids alias helpers and keeps ACL surface minimal.

### Policy and Evaluation

Compose rules with:

```ts
const aclPolicy = policy(ruleA, ruleB, ruleC)
```

Evaluate with:

```ts
const decision = await acl.can(READ, {
  subject,
  resource,
  environment,
})
```

`environment` is optional.

## Authoring Patterns

### 1. Direct Deny Rule

```ts
const denySuspended = deny(eq((subject: User) => subject.suspended, true))
```

### 2. Rule Array (AND shorthand)

```ts
const allowOwnerRead = allow([
  actionIs(READ),
  eq(
    (subject: User) => subject.id,
    (resource: Document) => resource.ownerId,
  ),
])
```

### 3. Priority and Failure Metadata

```ts
const denyAfterHours = deny(rule, {
  name: "after-hours",
  priority: 100,
  failure: failure("Access blocked outside allowed hours."),
})
```

## Decision and Trace

A decision includes:

* `allowed`
* optional `failureToken`
* optional `reason`
* `trace` with checked and matched rule entries

## Examples

* `src/acl/examples/example-basic.ts`
* `src/acl/examples/example-inheritance.ts`
* `src/acl/examples/example-multitenancy.ts`

## When To Use ACL vs Core

Use `@tdreyno/he-said/acl` when you want explicit allow/deny semantics with ACL vocabulary. Use core algebra directly when you need full custom rule shapes and relation modeling.

For conceptual relation-backed ACL modeling, see [Implementing ACL](/conceptual-from-scratch/acl-implementation.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://he-said.tdreyno.com/package-guides/acl-guide.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
