> 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/conceptual-from-scratch/abac-implementation.md).

# Implementing ABAC

This is the conceptual/core-algebra ABAC implementation guide. For the dedicated ABAC facade package, see [ABAC Guide](/package-guides/abac-guide.md) and [ABAC API](/package-guides/abac-api.md). If you're starting from scratch, prefer `@tdreyno/he-said/abac` and use this page when you need the underlying attribute-based model.

This guide shows a simple, real-world ABAC model in he-said.

Scenario:

* A support team works with customer records.
* Access depends on user and resource attributes.
* Analysts can read records only for their assigned region.
* Suspended users cannot access records.
* High-risk records can be read only by users with a risk clearance.

The example keeps ABAC centered on attributes and predicates, not role tables or ACL entries.

## Domain Model

### Types

```ts
type User = {
  id: string
  suspended: boolean
  region: string
  hasRiskClearance: boolean
}

type CustomerRecord = {
  id: string
  region: string
  riskLevel: "normal" | "high"
}
```

### Terms and Predicates

```ts
import { and, atMost, eq, not, oneOf, or, term } from "@tdreyno/he-said"

const actor = term<User>()
const record = term<CustomerRecord>()

const activeUser = actor.is(user => !user.suspended)

const sameRegionRecord = record.is((value, env: { actorRegion: string }) => {
  return value.region === env.actorRegion
})

const highRiskRecord = record.is(value => value.riskLevel === "high")
const lowRiskRecord = record.is(value => value.riskLevel === "normal")

const actorHasRiskClearance = actor.is(user => user.hasRiskClearance)
```

## ABAC Rules

This section maps business attribute rules directly into policy rules.

```ts
const canReadNormalRiskRecord = and(activeUser, sameRegionRecord, lowRiskRecord)

const canReadHighRiskRecord = and(
  activeUser,
  sameRegionRecord,
  highRiskRecord,
  actorHasRiskClearance,
)

const canReadRecord = or(canReadNormalRiskRecord, canReadHighRiskRecord)
```

If your request includes explicit action values, group read-like actions with oneOf:

```ts
type Action = "read" | "download" | "write"

const action = term<Action>()

const canAccessRecord = and(oneOf(action, ["read", "download"]), canReadRecord)
```

If your policy has explicit deny attributes, keep deny logic clear and local:

```ts
const deniedBySuspension = actor.is(user => user.suspended)

const canReadRecordWithExplicitDeny = and(
  not(deniedBySuspension),
  canReadRecord,
)
```

When multiple boolean checks are optional but bounded, cardinality helpers keep intent explicit:

```ts
const hasBreakGlass = term<boolean>()
const hasManagerApproval = term<boolean>()
const hasRiskOverride = term<boolean>()

const canUseOverride = and(
  atMost(
    1,
    eq(hasBreakGlass, true),
    eq(hasManagerApproval, true),
    eq(hasRiskOverride, true),
  ),
  canReadRecord,
)
```

## Suggested Migration Path

1. Pick one endpoint with attribute-heavy conditions.
2. Extract each condition into a named derived term using term.is(...).
3. Compose one endpoint rule with and/or/not.
4. Move request context values into environment fields used by predicates.
5. Repeat for adjacent endpoints and share predicates where useful.

## Common Pitfalls

* Mixing role checks into ABAC predicates instead of composing a separate RBAC rule.
* Reading current time directly in predicates instead of passing a deterministic value through environment.
* Writing large inline predicates instead of naming smaller, reusable ones.
* Forgetting that term predicates can use both bound term values and environment values.

## Practical Rule of Thumb

Keep ABAC readable by naming attribute checks first:

* Derived terms describe attribute truth.
* Composed rules describe business decisions.
* Environment values provide request-time context.


---

# 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/conceptual-from-scratch/abac-implementation.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.
