---
title: permit
description: A type-safe, declarative authorization library for TypeScript with Standard Schema validation and composable conditions.
sidebar:
  label: Overview
type: package
package: "@zap-studio/permit"
---

`permit` is a type-safe, declarative authorization library for TypeScript with [Standard Schema](https://standardschema.dev/) support.

## Motivation

Authorization checks written by hand, like `if (user.role === "admin")`, spread through a codebase over time. After a while, nobody can answer "who is allowed to delete a post?" without searching the whole app.

A framework like CASL solves the spreading problem, but it comes with its own vocabulary to learn (`subject`, `can`, `cannot`, `rules`), and its rules are not checked against your actual data shapes — you can write a rule that references a field your resource does not have, and it will only fail once that code runs.

`permit` keeps all rules in one place, through `createPolicy(...)` with `allow()`, `deny()`, and `when(condition)` — one file answers "who can do what."

And because resources come from your Standard Schema schemas, policy types are derived straight from your real data shapes. Reference a field that does not exist, and you get an error while writing the code, not a silent `undefined` in production.

## Features

- **Full type safety** — actions, resources, and permissions are inferred from your schemas and `satisfies` declarations, from [Getting Started](/permit/getting-started) onward.
- **Standard Schema support** via `Resources` — works with Zod, Valibot, ArkType, or any compatible library. See [Standard Schema Support](/permit/standard-schema).
- **Declarative policies** through `createPolicy(...)` with `allow()`, `deny()`, and `when(condition)`. See [Declarative Policies](/permit/declarative-policies).
- **Role hierarchy support** via `hasRole(role, hierarchy?)`, with inheritance resolved by `collectInheritedRoles`. See [Role-Based Access Control](/permit/roles).
- **Composable conditions** via `and`, `or`, and `not`. See [Conditions](/permit/conditions).
- **Policy merging strategies** via `mergePoliciesAnd` and `mergePoliciesOr`. See [Merging Policies](/permit/merging-policies).
- **Structured errors** with `PolicyError` for invalid configuration or evaluation failures. See [Error Handling](/permit/errors).
- **[Optional logging](/permit/logging)** through `createPolicy({ logger })` from [`logger`](/logger) — omit it and there's zero added logging overhead.
- **[Native OpenTelemetry](/permit/opentelemetry)** — an `INTERNAL` span per check with the allow/deny decision as an attribute, plus a `permit.checks` counter, no-op until an SDK is registered.
- **Tree-shakeable** — policies and conditions are plain functions; unused exports are dropped by any modern bundler.

## Quick Start

```ts
import { z } from "zod";
import { ConsoleLogger } from "@zap-studio/logger";
import { createPolicy, allow, deny, when } from "@zap-studio/permit";
import type { Resources, Actions } from "@zap-studio/permit";

const resources = {
  post: z.object({ id: z.string(), authorId: z.string() }),
} satisfies Resources;

const actions = {
  post: ["read", "write", "delete"],
} as const satisfies Actions<typeof resources>;

type AppContext = { user: { id: string } };

const logger = new ConsoleLogger({ minLevel: "debug" });

const policy = createPolicy<AppContext>({
  resources,
  actions,
  rules: {
    post: {
      read: allow(),
      write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
      delete: deny(),
    },
  },
  logger,
});

const ctx: AppContext = { user: { id: "user-1" } };
const post = { id: "1", authorId: "user-1" };

await policy.can(ctx, "post:write", post); // true, inferred as boolean
```

Continue with [Getting Started](/permit/getting-started) for a full walkthrough of resources, actions, context, and checking permissions.

## Runtime Support

| Runtime            | Minimum version                                  |
| ------------------ | ------------------------------------------------ |
| Node.js            | 18.0.0                                           |
| Bun                | 1.0.0                                            |
| Deno               | 1.42                                             |
| Cloudflare Workers | Any current release                              |
| Browsers           | Latest evergreen (Chrome, Edge, Firefox, Safari) |

The package ships standard ESM only and uses no runtime-specific APIs. Deno 1.42 is the first release that can install packages from JSR (`deno add jsr:@zap-studio/permit`).
