Skip to main content
canAny answers a different question from can: not “does this principal hold this specific permission here?” but “does this principal hold anything under this subtree at all?” That distinction makes canAny the right tool for show/hide decisions in navigation and conditional UI, and the wrong tool for anything that authorizes an action. The static verifier (@alfiz/verify) produces a build error if it detects canAny in a server action or route handler, treating it as a gate.

canAny(principal, pattern)

PrincipalRef
required
Identifies who is being checked. Either { userId: string } for a human user or { serviceId: string } for a machine principal.
P
required
A permission pattern declared in your catalog. Three shapes are valid:
  • A concrete key such as "docs.files.read" matches exactly that key.
  • A subtree wildcard such as "docs.*" or "admin.*" matches every key under the named group, at any depth.
  • The bare "*" matches every key in the catalog.
The TypeScript compiler validates patterns against the catalog’s derived PatternOf<typeof catalog> union at literal call sites. At runtime, an unknown pattern raises UnknownPermissionError.

Return value

Promise<boolean>
Resolves to true when the principal holds at least one unexpired, non-revoked grant whose effective permission set intersects the pattern. Resolves to false for inactive principals, or when no matching grant exists at any scope. This method never throws for a denial.

requireAny(principal, pattern)

The throwing form of canAny. Throws AccessDeniedError with reason: "forbidden" when no matching permission is held, always that reason, including for an inactive principal, whose check simply evaluates to false before any row is read. Use this for project-root visibility checks, for example to guard the project settings page against users with no access to the project at all, without requiring a specific leaf key.
requireAny is still a visibility affordance, not a gate. Use it to block navigation to an entirely foreign project; never use it as the sole check protecting a write operation or a privileged read. The page you land on after requireAny passes must gate its own actions with can.

Visibility affordance, never a gate

canAny and can answer complementary questions: When you use canAny as a gate, you admit any user who holds any permission in the subtree, including, for example, a user who can only delete documents but cannot read them. A grant of docs.files.delete at one specific document would pass canAny(p, "docs.*"), but that user must not be able to call a GET /docs/:id route for arbitrary documents.

Pattern syntax

Patterns are the typed string union PatternOf<typeof catalog>, which includes:
  • Every concrete permission key in the catalog ("docs.files.read", "docs.files.update_file", …)
  • Every group-level wildcard ("docs.*", "docs.files.*", "admin.*", …)
  • The global wildcard "*"
Group paths without the .* suffix are not valid patterns. "docs.files" is a group name and not a pattern. The catalog rejects unknown patterns with a suggestion: if you pass "docs.files", the error will recommend "docs.files.*".

The correct pattern: canAny for nav, can for the gate

Showing a project card

Showing a settings gear icon

requireAny for project-root access

canAny uses the same subject-closure cache as can. You do not pay an extra provider round-trip for canAny calls that follow a can call for the same principal within the same subjectCacheTtlMs window. Use client.snapshot(principal) when you need both in the same render to ensure they operate over one consistent data instant.