Configure a production Energon instance so authenticated hub activity stays separate from published active content, token ownership stays inside the intended personal or organization boundary, mutations default to the creator, and content expires. These boundaries matter because a healthy Worker can still expose content or authority through a bad policy. This guide gives operators a conservative baseline plus checks for each boundary.
Set safe instance boundaries
1. Give the hub and content different origins
Set two custom hostnames. The hub origin (PUBLIC_ORIGIN) serves the human hub and /v1 API. The content origin (CONTENT_ORIGIN) serves published sites and files.
[vars]
PUBLIC_ORIGIN = "https://energon.company.example"
CONTENT_ORIGIN = "https://content.energon.company.example"
TOKEN_ENV = "COMPANY_ENERGON_TOKEN"
TOKEN_PREFIX = "ee_live_"Attach both hostnames to the Worker. In production, Energon rejects a missing, malformed, or same-as-hub content origin when it needs to publish or serve content. The same origin is permitted only for localhost development.
People browse the hub with an Access session, while teams may publish HTML, SVG, or Markdown supplied by an agent. A dedicated content hostname prevents that active content from sharing the hub’s authenticated origin. Energon also adds a sandbox CSP to active documents, with origin separation as the outer boundary.
Do not put Cloudflare Access on the content hostname. Recipients are meant to open content links without an operator session, and the Worker does not serve hub account routes there. Put confidential material behind an object share password, or use a system with reader-specific authorization when a shared password is insufficient.
2. Configure Cloudflare Access by path
On the hub hostname, configure Access to allow only the intended individual or organization members on:
/
/account*
/about
/stats
/setup
/tokensBypass Access for the routes that use their own protocol:
/v1*
/health
/llms.txt
/favicon.svg
/static*Agents do not carry an Access cookie; /v1 authenticates them with Authorization: Bearer. Public content paths on CONTENT_ORIGIN also bypass Access.
Energon application code rejects human hub requests on *.workers.dev, but disable or ignore that hostname for humans as well. Keep the custom hub hostname as the only place where people establish an Access session.
3. Add a second identity boundary
For an organization instance, restrict the Access identity provider to the organization, then set the same email-domain intent in Energon:
[vars]
ALLOWED_EMAIL_DOMAINS = "company.example"The Worker applies this allowlist to both verified Access identities and the owners of API tokens. This catches a mis-aimed Access policy before an outside email can mint a token or create a user row. An empty allowlist accepts any email that Access verifies.
For a personal instance, restrict Access to the operator’s exact identity. If that identity uses a shared consumer domain, leave ALLOWED_EMAIL_DOMAINS empty rather than allowing the entire domain, and treat the narrow Access rule as the identity boundary.
Use the stable IdP subject supplied by Cloudflare Access. Energon binds owner-only content to that subject-backed user id. If an email is reassigned to a new subject, the old account’s tokens are revoked and its owner-only objects do not transfer.
4. Choose the mutation boundary explicitly
For a conservative default, make new objects creator-only:
[vars]
WRITE_POLICY = "owner"Use instance only when every valid API token on this host should be able to overwrite and delete newly created work. The setting is copied at creation; it does not retroactively change existing sites or files. A creator can override the default on create and later PATCH the stored policy.
export ENERGON_ORIGIN="https://energon.company.example"
test -n "$COMPANY_ENERGON_TOKEN" || { echo "Set the human-minted token" >&2; exit 1; }
curl -sS "$ENERGON_ORIGIN/v1/sites" \
-H "Authorization: Bearer $COMPANY_ENERGON_TOKEN" \
-H "Content-Type: application/json" \
--data '{"slug":"release-notes","write_policy":"owner","ttl":"7d"}'Remember that this controls mutation only. Every valid API token on the instance can still read through /v1 and duplicate readable content into a new object.
5. Set content and token clocks separately
This explicit baseline requires content to expire within 30 days and removes never from future token-mint choices:
[vars]
ALLOW_UNLIMITED_RETENTION = "false"
DEFAULT_TTL = "7d"
MAX_TTL = "30d"
ALLOW_UNLIMITED_TOKENS = "false"Content and token settings do not affect one another:
ALLOW_UNLIMITED_TOKENS = "false" affects future mints only. Audit and revoke existing tokens whose expires_at is null if your policy no longer permits them. Do not roll the Worker back below the first version that enforces token expiry while finite, unrevoked tokens exist; an older build would ignore their expiry.
The scheduled expiry sweep should remain configured. Reads still return 410 immediately after content expiry, even if a cron run fails, but the sweep removes expired R2 bytes and catalog rows.
6. Verify from both sides of the boundary
First, confirm that public metadata is reachable without a credential and advertises the expected identity and policies:
export ENERGON_ORIGIN="https://energon.company.example"
curl -sS "$ENERGON_ORIGIN/v1/health"
curl -sS "$ENERGON_ORIGIN/v1/help" | jq '{hub, content_origin, env, token_prefix, retention, tokens}'Then verify a human-minted token without printing it:
test -n "$COMPANY_ENERGON_TOKEN" || { echo "Set COMPANY_ENERGON_TOKEN" >&2; exit 1; }
curl -sS "$ENERGON_ORIGIN/v1/whoami" \
-H "Authorization: Bearer $COMPANY_ENERGON_TOKEN"Finally, check the host split in a browser or with headers:
curl -sS -D - -o /dev/null "https://energon.company.example/example/s/security-check/"
curl -sS -D - -o /dev/null "https://content.energon.company.example/account"The first request should redirect to the content origin when the object path is valid. The content hostname must not serve the account route. A real published HTML/SVG response should include a sandbox Content-Security-Policy; a password-protected response should use private, no-store caching.
A successful health check proves only that the Worker is running. It does not prove Access paths, email domains, write policy, content-host routing, or cache behavior. Verify each boundary after deployment and whenever routing or policy variables change.
7. Handle credentials and reports safely
- Never invent, commit, or paste a live Energon token into documentation or an issue.
- Revoke a leaked token from
/tokens; it cannot be recovered or renewed. - Avoid logging the token-mint response because it contains the one-time secret.
- Treat share passwords and customer file contents as sensitive report material.
- Report code vulnerabilities through GitHub private vulnerability reporting, including the tested commit, reproduction steps, and attacker impact.
- Fix an instance-only misconfiguration, such as an open Access policy or exposed hub hostname, on that instance without publishing its credentials.
Continue reading
- Security model explains why Energon uses separate controls instead of one private/public setting.
- Sharing, access, and expiry shows the exact reader, writer, and lifetime matrix.
- Deploy an Energon host covers Cloudflare resources and the full deployment sequence.
- Configure an instance documents the remaining runtime variables and operational tradeoffs.