The policy package adds fine-grained authorization on top of sessions and API
tokens. Policies are JSON documents of allow and deny statements. Evaluation is
default-deny, and an explicit deny always wins.
Policy documents
- Globs.
* matches any run of characters, including /. A backslash
escapes the next character. Matching is case sensitive.
- Variables.
{name} in a resource or condition value is replaced from the
request attributes. Substituted values are escaped, so an attribute of *
cannot widen a statement. If a variable is missing, an allow statement does
not match and a deny statement still applies (fail closed).
- Conditions. All must hold. Operators:
equals, not_equals, in,
not_in, ip_cidr, not_ip_cidr (key ip), time_window (two RFC 3339
bounds, either may be empty) and daily_window (HH:MM UTC, may wrap
midnight). Positive operators are false for a missing key, negated ones are
true, so a “deny unless from the office network” rule blocks unknown callers.
- Parsing.
policy.Parse rejects unknown fields, unknown versions and
oversize documents. Use Policy.Marshal to write the canonical form.
Evaluating directly
HTTP middleware
RequirePolicy resolves the session, API token or agent token on every request,
loads the policies attached to the user, the groups and roles your Subjects
callback returns, and the token, then evaluates. A denial responds 403:
Every decision emits a policy.decision audit event with the action, resource,
policy, statement and reason (set AuditDenyOnly to skip allows). Built-in
attributes: principal.id, principal.kind, token.id, agent.name. Add more
with Options.Attributes.
Permission boundary for tokens
Policies attached to a token (SubjectToken) form a boundary. The request must
be allowed by the owner’s policies and by the boundary, and a boundary deny
blocks it, so a token can only be narrower than its owner, never wider. An
agent token is evaluated as its delegating human plus its own boundary.
Storage
policy.Storage (alias PolicyStorage) is an optional capability.
policy.NewMemory() is the reference implementation, and a backend proves
conformance with storagetest.RunPolicy. Suggested SQL schema:
PutPolicy upserts and keeps created_at. PoliciesFor orders by policy ID,
then subject kind and ID.
A project admin can do everything in their project, a developer cannot touch
production, and CI tokens may only deploy to staging.
Attach project-admin and developer to groups, and ci-staging to a CI
token. Even if the CI token’s owner is a project admin, the token can only run
deploy:create on staging. Removing a user from a group takes effect on the
next request because group membership is resolved per request. Last modified on October 7, 2026