Skip to Content
Operate EnergonConfigure Energon policy

Set an explicit operating policy for an existing Energon with this guide. You will choose who can mint agent keys, which write_policy governs new objects, how long content and tokens live, and how much storage one request may consume, then verify the resolved result through /v1/help. This matters because a missing Wrangler variable can quietly replace the operator’s intended policy with Energon’s stricter code default.

Configure Energon policy

Treat configuration as policy

Most Energon variables change who may mint, who may modify, how long content remains, or how much storage one upload can consume. Omission is meaningful: code defaults are deliberately stricter than the trusted-workspace values committed in wrangler.toml. Treat a configuration diff as a security and lifecycle change, even when no TypeScript changed.

1. Start from the correct operating mode

Use the configuration presets in docs/DEPLOY.md  as the starting point. During installation, follow the copy in your deployment checkout so its values match the deployed code. Use the sections below to understand and adjust those settings.

Choose content retention, write ownership, and token lifetime independently. Personal use does not inherently require finite retention, and owner-only writes do not make published links private. For a new installation, start with Deploy an Energon host.

WRITE_POLICY is copied to each new site or loose file. Changing this Energon value does not rewrite existing rows. Stored NULL and legacy instance rows are treated as org (migration 0017 and ensureSchema rewrite stored instance to org). An unset or unrecognized environment value resolves to owner for new objects.

2. Restrict identity and align plugin metadata

Access authenticates human hub users, while ALLOWED_EMAIL_DOMAINS prevents a misconfigured Access application from creating keys for an unrelated email domain. Set both the domain restriction and the identity advertised to agents:

PUBLIC_ORIGIN = "https://energon.your.co" CONTENT_ORIGIN = "https://share.your.co" ALLOWED_EMAIL_DOMAINS = "your.co,subsidiary.your.co" TOKEN_ENV = "EXAMPLE_COMPANY_ENERGON_TOKEN" SKILL_NAME = "example-company-energon" MARKETPLACE_NAME = "example-company-energon" MARKETPLACE_REPO = "example-org/energon"

Keep Access on the signed-in hub paths only. /v1* must bypass Access so agents can send agent keys with Authorization: Bearer, and the entire content hostname must remain outside Access. The distinct origins prevent published active content from sharing the hub’s authenticated context.

For an organization, restrict Access to the intended identity provider and use ALLOWED_EMAIL_DOMAINS as a second check. For a personal Energon, restrict the Access policy to the operator’s exact identity. Do not use a shared consumer domain such as gmail.com as though it identified one person; an empty ALLOWED_EMAIL_DOMAINS accepts any identity that Access verifies, so the Access policy must remain narrow.

For hostname-based Access applications, also configure the signed-token issuer and hub audience:

ACCESS_TEAM_DOMAIN = "your-team.cloudflareaccess.com" ACCESS_AUD = "REPLACE_WITH_HUB_APPLICATION_AUD"

These values are not secrets. Use the human hub application’s audience, not the bypass application’s ID or audience. Without matching values, a successful provider sign-in can still leave the hub showing Not signed in. See Deploy an Energon host for the application setup.

Serve the well-known skill

From v1.12.0, the content origin serves this Energon’s rendered publish skill at /.well-known/agent-skills/ (an index.json plus SKILL.md, references/api.md, and scripts/energon_publish.py). It is on by default, because tool gateways and npx skills add <content origin> should work without operator action, and the skill holds no credentials. It is rendered at request time from the identity variables above, so keep TOKEN_ENV, TOKEN_PREFIX, and SKILL_NAME matched to the rendered plugin. To stop serving it:

AGENT_SKILLS_DISCOVERY = "false"

Unset means on. Once the variable is set, only 1, true, or yes keeps it on, so a typo such as on turns it off. When off, the paths return 404, /v1/help reports agent_skills_url: null, and hub /llms.txt drops the install line. Responses are cached for five minutes (max-age=300), so edge caches can keep serving the skill for up to five minutes after you turn it off. No Access change is needed either way: the paths live only on the content hostname, which stays outside Access, and the hub host returns 404 for them. See Use Energon through a tool gateway for what gateways do with it.

3. Set retention choices

The code-owned TTL catalog is 30m, 1h, 1d, 7d, 14d, 30d, 60d, 90d, 180d, and 365d. MAX_TTL caps usable choices. TTL_PRESETS hides choices from the picker; it does not invent durations outside the catalog.

ALLOW_UNLIMITED_RETENTION = "false" DEFAULT_TTL = "7d" MAX_TTL = "90d" TTL_PRESETS = "1d,7d,30d,90d"

PUT replaces bytes without extending expiry. A content PATCH with a TTL recalculates expiry from the time of that patch. The five-minute cron removes expired storage in batches, while reads and writes reject expired objects with 410 even if a cron run was missed.

4. Configure token lifetime separately

Token lifetime is not inherited from content retention. Humans choose 1d, 7d, 30d, 60d, 90d, 180d, or 365d when creating a key; the default is 90d. ALLOW_UNLIMITED_TOKENS=true adds Never.

ALLOW_UNLIMITED_TOKENS = "false"

Turning this flag off affects future mints only. Existing never-expiring keys keep working until their owners revoke them on /keys. Expired keys cannot be renewed; the human must approve a new connection or create a replacement.

ADMIN_EMAILS is a comma list of operator addresses. Only those people can create an admin key from /keys (scope: admin). Connect never grants that scope. Admin keys last at most 7 days (default 1 day) and cannot be never. Admin routes also check that the owner is still on the list, so removing an email strips admin from every key at once. Ordinary /v1 calls with an admin key still act as that account. Leave the list empty when nobody should be admin.

5. Set capacity and presentation limits

MAX_FILE_BYTES = "100mb" MAX_ZIP_IMPORT_BYTES = "100mb" MAX_ZIP_EXTRACTED_BYTES = "500mb" MAX_ZIP_EXPORT_BYTES = "25mb" MAX_PLATFORM_BYTES = "20gb" FOOTER_TEXT = "Internal publishing service"

The three MAX_ZIP_* settings and the storage accounting below arrived with atomic site deployments in Energon v1.10.0 . Earlier releases use MAX_ZIP_BYTES alone, with a 25 MB default for import, expanded content, and export.

MAX_FILE_BYTES caps one file upload: a loose-file create or replace, a site file PUT, or a guest write. The default is 100 MB. Raw bodies over 25 MB are staged in R2 under tmp/ instead of held in Worker memory, and so are rollback copies of objects over 25 MB. Staged objects are deleted when the request ends, and the cron sweep removes any left there for over an hour. Bodies staged for a site write or deployment are reserved against MAX_PLATFORM_BYTES until cleanup confirms they are gone. Multipart uploads stay capped at 25 MB whatever this value is, because the Worker buffers them; send larger files as a raw body with X-Filename. Raw bodies without Content-Length are also capped at 25 MB.

Cloudflare rejects request bodies above the zone plan’s limit before the Worker runs: 100 MB on Free and Pro, 200 MB on Business, and up to 5 GB on Enterprise. The effective single-file limit is the smaller of MAX_FILE_BYTES and that plan limit, so a value above 100 MB helps only on Business or Enterprise. That body limit is separate from the Worker CPU limit described in Deploy an Energon host. Energon does not support chunked uploads or resuming a partly sent file. A deployment session resumes between files, so a large site can span many requests, but each file or ZIP must arrive in one request.

Three ZIP limits are set independently. MAX_ZIP_IMPORT_BYTES caps the compressed ZIP sent to an import or deployment (default 100 MiB). MAX_ZIP_EXTRACTED_BYTES caps the total expanded content (default 500 MiB); MAX_FILE_BYTES still applies to each file inside. MAX_ZIP_EXPORT_BYTES caps one site export and the owned-account ZIP (default 25 MiB). Imports stage the archive and prepare it in bounded steps rather than unpacking it in memory. A new deployment also accepts at most 200 files.

MAX_ZIP_BYTES is the older single ZIP setting. If your wrangler.toml sets it, its value (capped at MAX_FILE_BYTES) becomes the fallback for each of the three new settings you have not set explicitly. An installation that copied MAX_ZIP_BYTES = "25mb" from older examples therefore keeps 25 MB import, expanded, and export caps after upgrading. Set the new variables, or remove the old one, to use the larger defaults. Leave MAX_ZIP_BYTES unset on a new installation. /v1/help reports the effective values as limits.zip_import_bytes, limits.zip_extracted_bytes, and limits.zip_export_bytes, with limits.zip_bytes kept as a compatibility field equal to the import cap.

These byte settings accept 25mb, 5mb, or a raw byte count. MAX_PLATFORM_BYTES is the instance-wide storage quota (default 20 GiB, shown as 20 GB), shared by every account; there is no per-user quota. /stats shows used versus that cap for any signed-in user.

Leave storage headroom for site updates

The quota counts more than published content. It includes files staged in an open deployment, uploaded ZIP inputs, the old version of a site retained after a commit, and objects waiting for cleanup. A superseded version stays retained for a five-minute grace period and while any in-flight read still uses it. Charges are released only after storage deletion is confirmed.

For example, replacing a 500 MiB site from a 100 MiB ZIP can briefly hold about 1.1 GiB: the old site, the new site, and the archive together. Keep at least 2 GiB free when people publish large sites, and more when several deployments, retries, or a cleanup backlog run at once. This is planning guidance, not a second enforced limit. A publication that would exceed the quota fails with 413 storage_cap and leaves the live site unchanged. Administer Energon shows pending cleanup bytes. FOOTER_TEXT is escaped as a single line on signed-in pages; it is not HTML.

6. Validate the resolved policy

Check render consistency locally, then inspect the live unauthenticated help document after the normal migrate-before-deploy workflow completes:

npm run skill:render -- --check curl -fsS https://energon.your.co/v1/help

Confirm that retention, tokens, identity, limits, origin, env, agent_skills_url, and the install line match the intended Energon configuration. The hub’s menus are derived from the same resolved policies.

Configuration failures

Do not rely on committed Energon defaults surviving an incomplete merge. If ALLOW_UNLIMITED_RETENTION, TTL, or WRITE_POLICY disappears, the Worker falls back to required expiry and owner-only writes. Compare new keys in wrangler.example.toml, restore explicit values, and verify /v1/help.

Do not put Access on /v1 or the content hostname. If agents receive an Access challenge instead of Energon JSON, update the Access application so only human hub paths require sign-in and the agent/public paths bypass it.

Disabling Never for tokens does not revoke existing Never tokens. Review them on /keys and have their owners revoke any that violate the new policy.

Verify the deployed contract

Read /v1/help after every policy deployment. It shows the catalog after caps and defaults are applied, along with the identity agents will follow; the text in wrangler.toml alone does not prove the live Worker received those values. For provisioning context, see Deploy an Energon host. For migration-sensitive policy changes, use Upgrade and recover and the security model.

Last updated on