Context
Today docs.eigenoid.services is protected by Cloudflare Access, but it will not be the only private surface in the org. The reasonable plan is for most of eigenoid's internal tools (static sites, dashboards, internal APIs, admin panels, future product instances) to sit behind the same Access layer with the same "members of the GitHub org" policy. Each of those surfaces will inherit the same problem when a visitor fails the policy.
The Free plan of Cloudflare Zero Trust does not allow uploading custom HTML/CSS for the block page. The only option it offers is to redirect the visitor to a public URL. That URL must exist on some hosting with SSL and, crucially, be publicly accessible without authentication -- if it is also behind Access, it enters a redirect loop.
If we create a different custom block page for every new service, we end up with N pages to maintain, inconsistent branding, and duplicated effort. It makes sense to define a default that applies to all services and reuse the same URL in every Access app.
Additionally, for future surfaces (custom login pages, transactional emails, public READMEs, etc.) we will need to reference the wordmark and other branding assets from a public URL. Today those assets live in internal-docs/static/img/, but that repo is behind Access and cannot be used as a public CDN.
Decision
Create a single generic block page served at https://block.eigenoid.services and configure it as the default block page redirect in every Access app in the org. The page:
- Does not mention any specific service. The copy is generic ("no access" + a short message about the failed policy), applicable to any blocked surface.
- Displays the eigenoid wordmark with a subtle glitch effect as the only visual identity element.
- Is independent of the service that triggers the redirect: the same URL serves
docs.eigenoid.services, future APIs, dashboards, etc.
The site lives in a dedicated repo eigenoid/access-block-page (private repo, public output) deployed on Cloudflare Pages. It is purely static (HTML + CSS + ES modules + three from CDN), with no build step.
The same origin doubles as a public CDN for branding assets: under https://block.eigenoid.services/assets/<file>, accessible without authentication. This solves the separate problem of needing public URLs for logos in places like the Access login page or transactional emails.
Consequences
- A single URL to configure across all Access apps: every time a new one is created, just point the block page redirect to
https://block.eigenoid.services. No extra design work. - Consistent branding across all friction points in the org. The visitor recognizes the page regardless of which service they tried to reach.
- Blocked visitors get an on-brand explanation instead of Cloudflare's generic page. Reduces the "WTF moment" and professionalizes the org's facade.
- Generic by design: the copy cannot be customized per service (there are no reliable parameters that travel with the redirect on Free), but that was the goal. If a particular service needs a different page in the future, it can point to another URL in its own Access app.
- Public CDN for branding from day one, without having to stop and set up an R2 bucket or a separate assets repo.
- Hosting within Cloudflare's free tier: 100 projects per account, 500 shared builds/month, 100 custom domains per project. This component uses <1% of any limit.
- No GitHub Actions: deployment is handled natively by Cloudflare when it detects a push to
main. One less piece of YAML to maintain and rotate SHAs for. - New component to maintain: although minimal, it adds surface area. The page must remain accessible and on-brand; the TronGrid in docs and the one here are independent ports that can diverge if not maintained together.
- The wordmark and logos become effectively public -- keeping them in a private repo was never a guarantee of confidentiality because they are already served on external surfaces. This decision makes that public nature explicit.
- Single point of failure risk: if
block.eigenoid.servicesgoes down, blocked visitors see Cloudflare's generic block page. Graceful degradation, not a critical path, but uptime should be monitored. - Single point of style drift: if eigenoid's visual branding evolves, this repo must be updated alongside
internal-docs. Mitigation: both live in the same local workspace and share a palette documented in the glossary.
Alternatives considered
- A custom block page per service: better granular control of the message, but N pages to maintain, N repos or N routes, branding that easily diverges. Does not scale with the expected number of private services.
- Accept Cloudflare's default block page on all services: free, zero code, but the experience is poor and leaves the visitor without context. Acceptable as a fallback, not as the default.
- Upload the logo to Cloudflare R2 and use the default page: solves the public asset problem but not the custom message. R2 also adds billing risk (free tier has egress limits that change).
- Pay for Cloudflare Access to get custom HTML: the paid plan allows uploading HTML directly to the edge, without an external site. Cost not justified for a single static page.
- Use GitHub Pages with a public repo: viable but adds an Actions workflow to maintain (configure-pages + upload-pages-artifact + deploy-pages, all pinned to SHA), a 5-30min wait for the Let's Encrypt cert when assigning a domain, and a public repo that breaks the "everything in
eigenoidis private" convention. - Serve assets from a human's personal account repo: couples the org's infrastructure to a person. Same anti-pattern that ADR-0004 aimed to solve for tokens.
- Two separate projects -- one for the block page, another for
assets.<domain>: cleaner URLs for assets (assets.eigenoid.services/logo.svg) but adds a repo and a Pages project to solve a cosmetic problem. Can be done in the future if assets grow significantly.
References
- Operational runbook: Cloudflare Pages + Access.
- Repo:
eigenoid/access-block-page. - Public domain:
https://block.eigenoid.services. - Cloudflare Pages limits — official docs.
- Customize Cloudflare Access app launcher and login page — what can be customized on each plan.