shiply.now

Secrets

Encrypted-at-rest API keys and tokens for your site's worker — never reach the browser

Secrets are encrypted-at-rest values (API keys, webhook secrets, tokens) accessible inside your site's worker function as env.NAME. Values are decrypted only when the worker boots; they never appear in the published payload and never reach the browser.

Distinct from shiply Variables: Variables are encrypted at rest too, but are available to both proxy routes and worker functions. Secrets are scoped strictly to the worker.

Declare in .shiply/secrets.json

List the secret names your worker expects. Values are set out-of-band via the CLI / MCP / REST — they never appear in the publish payload:

{
  "secrets": ["STRIPE_WEBHOOK_SECRET", "STRIPE_SECRET_KEY", "RESEND_API_KEY"]
}

On shiply publish, the CLI prompts you for any declared secret that isn't set yet (and MCP returns a hint in the publish response). The file is optional — you can set secrets purely via CLI / MCP without declaring them — but it's the easiest way to document what your worker needs.

Set a value

Three equivalent ways:

shiply secret set <slug> STRIPE_SECRET_KEY sk_live_xxx

MCP:

set_secret(slug, "STRIPE_SECRET_KEY", "sk_live_xxx")

REST:

POST /api/v1/sites/{slug}/secrets
{ "name": "STRIPE_SECRET_KEY", "value": "sk_live_xxx" }

All three require Authorization: Bearer shp_….

Use in your worker

import Stripe from 'stripe'

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const stripe = new Stripe(env.STRIPE_SECRET_KEY)
    // ...
  },
}

Cloudflare auto-injects the binding when the worker boots. Values never reach the browser — even if you accidentally console.log them, the log lands in CF logs, not the response.

Lifecycle

  • Secrets persist across function redeploys — once set, they stay set until you remove them or delete the whole function.
  • shiply secret ls <slug> lists names (values are never returned).
  • shiply secret rm <slug> NAME removes one.
  • shiply function rm <slug> cascades — removing the function deletes all its secrets and crons.

Naming + limits

  • Names must match [A-Z_][A-Z0-9_]* (uppercase + digits + underscores; can't start with a digit).
  • Max 80 chars per name.
  • Max 50 secrets per site.

Values are unbounded in length, but Cloudflare rejects values above ~64 KB at the secret-set step.