Documentation
Security

Roles and access control

Forge uses a role-based access model. Roles are plain strings stored in tokens and sessions. Their permission level is derived at runtime from a registry — not from the stored string — which means you can add custom roles without touching your content or tokens.

Built-in roles

Four roles are available out of the box, in ascending order:

RoleLevelIntended for
guest1Unauthenticated requests
author2Creating and managing own content
editor3Managing all content
admin4Full access including app configuration

The level is never stored anywhere. Only the role name ("author", "editor", etc.) travels in the token. The level is looked up when a permission check runs.

Hierarchical vs. exact checks

Most checks in Forge are hierarchical: an Admin satisfies an Editor requirement, an Editor satisfies an Author requirement, and so on.

// Hierarchical — Admin passes an Editor check
forge.HasRole(userRoles, forge.Editor) // true for admin, editor

// Exact — Admin does NOT pass an Editor check
forge.IsRole(userRoles, forge.Editor) // true only for editor

Use HasRole for access control. Use IsRole only when you need to distinguish between specific roles explicitly.

Module access control

Each module accepts Read, Write, and Delete options that restrict access by role:

app.Content(
    forge.NewModule[Post]().
        At("/posts").
        With(
            forge.Read(forge.Guest),    // anyone can read
            forge.Write(forge.Author),  // authors can create and update
            forge.Delete(forge.Editor), // only editors and above can delete
        ),
)

If no option is set, the default is Guest — meaning unrestricted access. Set Read(forge.Author) to make a module private.

Unknown roles

A role that has not been registered has a numeric level of zero. A zero-level role never satisfies any permission check, including Guest. This prevents tokens with fabricated role strings from accidentally gaining access.

Custom roles

Use NewRole to define a role and position it relative to the built-in hierarchy:

publisher, err := forge.NewRole("publisher").
    Above(forge.Author).
    Below(forge.Editor).
    Register()

This inserts a publisher role between Author and Editor. A publisher satisfies any check requiring Author or Guest, but not Editor or Admin. The built-in levels are spaced by 10, so there is room for multiple custom roles between each pair.

Registration is idempotent: calling Register again with the same name and level is a no-op. Registering the same name with a different level returns an error.

Token-based assignment

Roles are assigned when a token is issued and cannot be changed without issuing a new token. There is no session mutation. If a user's role changes, revoke the existing token and issue a new one.

Tokens are managed via forge-cli or the create_token MCP tool. See Token management for details.