Role gating
Vaults supports access tiers. Multi-role builds emit one variant per role, with a Cloudflare Pages Function in front that rewrites requests to the right variant based on a session cookie. This vault has three tiers: public (anyone), patron (paying readers), and dm (the campaign GM).
| Role | Password |
|---|---|
patron | patron-pass |
dm | dm-pass |
Use the auth box in the sidebar. Higher tiers see everything below them
too. dm sees patron + public content as well.
Two flavours of gating
Page-level (whole-page invisibility)
Add role: <name> to a page's frontmatter and the entire page is only
included in that tier's build (and higher tiers). Lower tiers don't get
the HTML, the body, the search hit, the sitemap entry, or the manifest
entry. The page structurally doesn't exist for them.
---
title: The Witchwood Cult
role: patron
---
Examples in this vault:
- Witchwood Cult:
role: patron - Hidden Caves:
role: dm
Try clicking these as a public visitor: the link itself renders as "unresolved" (muted text, no anchor), and a direct URL hit returns the 404 page.
Callout-level (paragraph-scoped redaction)
Add a callout whose type matches a configured role name and the callout is stripped from every variant lower than that role:
> [!patron] For supporters
> Backers see this paragraph.
> [!dm] DM-only
> Only the GM sees this paragraph.
Aelar has both a [!patron] and a [!dm] callout. Toggle tiers and watch the page change! Surrounding paragraphs stay; only the role-tagged blockquote disappears.
Setup
vaults role add patron # offers a password; press Enter to skip
vaults role add dm # same
vaults push # multi-role build + auth middleware
A password is one way to reach a role, not the only one. Press Enter at the
prompt (or pass --no-password) for a role granted by Patreon
or OIDC instead:
vaults role add staff --no-password
vaults oidc configure # grant 'staff' by email domain
The login page renders only the methods the deploy actually has. With no
password on any role there is no password form and no role picker, just the
provider button. With exactly one password role the picker disappears too,
since there is nothing to choose. vaults role list shows what each role
accepts, and the build warns about any role that nothing can reach.
Roles are ordered by add time, lowest → highest. To reorder:
vaults role promote dm # move up
vaults role demote patron # move down
To see what's configured:
vaults role list
Production hardening
The auth Function does several things by default:
- Signed, partitioned, HttpOnly session cookies (HMAC-SHA256).
- CSRF state cookies on the Patreon round-trip with a 10-minute TTL.
What it does not do, and what you should consider configuring on Cloudflare:
- Rate limiting on
/loginand/connect/approve. PBKDF2 is slow (~100 ms per guess) but a determined attacker can still brute-force common passwords from a botnet. Cloudflare's Rate Limiting is a paid feature (but there is a free tier). Turn it on for those routes if your vault is high-value. Cloudflare's built-in DDoS protection covers volumetric attacks but not slow-trickle credential spray. - WAF rules. The free tier includes a managed ruleset. Consider enabling it on production deploys.
Rotate the cookie-signing key any time you suspect a leak:
vaults push --rotate-secret
Generates a fresh SESSION_SECRET, uploads it to Cloudflare, invalidates
every issued cookie + bearer token immediately, and writes the new value
to your local .env.