Skip to content

Search docs

Jump between documentation pages.

Browse docs

Runtime protections that travel with your app

Think of it like… the seatbelts, airbags, and crumple zones built into the car itself. They protect you in every country you drive in, regardless of which dealership sold you the car, which gas station you fill up at, or whether your country requires roadworthiness inspections. CI hardening is the inspection sticker; these runtime guards are the car.

These protections live inside @daloyjs/core and run at request time in your app process. They apply regardless of where you host the repo or which CI you use: private GitHub, GitLab, Bitbucket, Azure DevOps, Gitea, self-managed Jenkins, or on-prem runners. They are also unaffected by whether you keep or delete the optional GitHub Actions bundle from create-daloy.

Think of DaloyJS supply-chain and security posture as three independent layers. This page documents the first one.

Three independent layers
Runtime guardrails (this page)inside @daloyjs/core, every request, no CI host required
safe CORSsafeJsonParsebodyLimitBytesrequestTimeoutMssecureHeaders()
Install-time hardeningpnpm scaffold .npmrc + verify:lockfile
ignore-scripts=trueminimum-release-age=1440frozen lockfile
CI / CD hardeningGitHub Actions bundle only; translate elsewhere
SHA-pinned actionsharden-runnerpermissions: {}
Only the top layer travels everywhere. It is baked into the framework and runs on every request no matter where you host the repo. The lower two layers depend on keeping the pnpm scaffold and the generated GitHub workflows.

The three layers, side by side

LayerWhere it runsTravels to GitLab / Bitbucket / Azure / on-prem?
Runtime guardrails (this page)Inside your app, every request. Lives in @daloyjs/core.Yes. Always on. No CI host required.
Install-time hardening.npmrc in pnpm scaffolds, plus pnpm verify:lockfile.Yes when you use pnpm. The hardened .npmrc ships in the project itself.
CI / CD hardening.github/workflows/*.yml from create-daloy --with-ci.GitHub only (public or private repo / org). On other CIs you have to translate the rules yourself.

Backend footguns the framework handles by default

Every row below describes behavior that is on by default in a fresh DaloyJS app. You do not need to install a plugin, deploy on a specific CI, or open a particular file to get these.

FootgunWhat DaloyJS does
Unsafe CORS defaultsCross-origin state-changing requests are refused unless cors() explicitly allows the origin. No reflective Access-Control-Allow-Origin: * with credentials.
Missing CSRF on stateful routesBooting session() with mutating routes but without csrf() is refused at startup, not silently allowed.
Weak session secretsShort / low-entropy session secrets are refused at boot. Production requires a real secret.
Missing secure response headerssecureHeaders() is auto-applied: HSTS, frame deny, no-sniff, strict referrer policy, baseline CSP.
Prototype pollution via JSON bodiessafeJsonParse strips __proto__, constructor, and prototype keys before the value reaches your handler.
Path traversalDot-segments (. / ..) are resolved to a canonical path before route matching, and empty // segments are refused. Routes match exact strings, so there is no directory to escape into.
Body-size abuseStreamed reads with a hard cap (default 1 MiB); oversize requests return 413 before they hit your handler.
Hung handlers / slow-lorisrequestTimeoutMs aborts handlers (default 30s); the Node adapter sets socket-level timeouts.
Header-count flood / HTTP/2 BombmaxHeaderCount (default 100) is a portable application-tier guard that returns 431 Request Header Fields Too Large when a request exceeds the cap (e.g. via app.fetch() or runtimes without a native cap). The Node adapter also sets the native server.maxHeadersCount to the same value, so on Node a header-count flood is dropped at the HTTP parser as a 400-class client error before it ever becomes a request. Either way it is defence-in-depth, apply the vendor HTTP/2 fix at any proxy that terminates HTTP/2.
Bad reverse-proxy assumptionsX-Forwarded-* headers are never trusted by default. In production, a request carrying one while trustProxy / behindProxy is unconfigured is refused with a 500(and a clear log line) so a spoofed source IP can't reach the rate limiter or audit log. Dev and CI relax this so you can test forwarded headers locally. Opt in via behindProxy (or trustProxy: false to ignore the headers).
Auth response caching401, 403, and 429 automatically set Cache-Control: no-store so proxies and CDNs cannot reuse them.
Duplicate dangerous headersDuplicate Host and Content-Length are rejected at parse time to block request-smuggling shapes.
Weak JWT secretscreateJwtSigner() refuses HS* secrets shorter than the algorithm requires.
Missing JWT expirySigning without an exp claim is refused, not defaulted to a forever token.
Unsafe compression cases (BREACH)compression() skips Set-Cookie, Authorization, session / CSRF cookie responses, and already-encoded content; downgrades strong ETags per RFC 9110.
Unsafe file-upload assumptionsmultipartObject + fileField enforce per-field size caps, MIME allowlists, and magic-byte checks.
Leaky production errorsProduction mode strips detail from 5xx problem+json automatically; stack traces never leak through the default error path.
Unsupported content typesRoutes with body schemas reject non-allowed content-types with 415.
Method confusionReal 405 with Allow header instead of a misleading 404.
Header / response splittingsanitizeHeaderName / sanitizeHeaderValue reject CRLF and NUL in header values.

What this page does not cover

The following protections only apply if you keep using the matching scaffolded bits:

  • Install-time hardening (blocked install scripts, 24h release-age cooldown, source-verified lockfile, zero-runtime-dep gate) applies when you use the pnpm scaffold and keep its .npmrc + pnpm verify:lockfile script.
  • CI / CD hardening (pinned actions, harden-runner, top-level permissions: {}, CODEOWNERS, Dependabot, CodeQL / Scorecard / zizmor) applies when you use the create-daloy --with-ciGitHub Actions bundle. On GitLab, Bitbucket, Azure DevOps, Jenkins, or on-prem runners you have to translate those rules into your CI's own configuration.
  • Branch protection, environment approvals, secret hygiene, runner isolation, and org policy are decisions of the host (GitHub / GitLab / Azure / Bitbucket / your own infra). DaloyJS cannot enforce them from inside your code.

What the generated GitHub Actions bundle actually does

If you scaffold with create-daloy --with-ci and keep the generated workflows, the YAML itself encodes these protections. They apply equally to public repos, private repos, and private organizations, being private is not a substitute for any of them:

  • Top-level permissions: {} with least-privilege per-job permissions.
  • Third-party Actions pinned to a commit SHA (not a moving tag).
  • actions/checkout with persist-credentials: false.
  • step-security/harden-runner with egress policy on every job.
  • Lifecycle scripts disabled during CI installs (--ignore-scripts for npm/yarn, ignore-scripts=true in the scaffolded .npmrc for pnpm).
  • No shared Actions cache by default.
  • Dependabot config for npm + Actions ecosystems.
  • CODEOWNERS for security-sensitive files.
  • CodeQL, OpenSSF Scorecard, zizmor, and vulnerability-scan workflows where included.
  • Manual-only deploy.yml starter instead of automatic publish or deploy on push.

This is generated GitHub CI hardening, not “default supply-chain protection everywhere”. If you delete the workflows, rewrite them, or use a different CI host, DaloyJS cannot give you these guarantees automatically.

The honest matrix

Use this table to figure out which protections you actually get for a given setup.

User setupWhat DaloyJS can protect
create-daloy --with-ci on GitHub (private or public repo / org)Runtime guardrails + pnpm install-time hardening (if pnpm) + full generated GitHub Actions starter protections.
create-daloy with pnpm, no CI bundleRuntime guardrails + hardened install defaults via .npmrc and pnpm verify:lockfile.
npm / yarn / bun users (no pnpm scaffold)Runtime guardrails. CI install commands still benefit if you keep the generated workflows on GitHub.
GitLab / Bitbucket / Azure DevOps / Jenkins / on-premRuntime guardrails and portable docs / patterns. No GitHub Actions protections.Translate the YAML rules into your CI's own configuration.
User deletes or rewrites the generated workflowsRuntime guardrails only. DaloyJS cannot guarantee CI supply-chain posture once the workflows are gone.
Branch protection, environment approvals, secret hygiene, runner isolation, egress policy, org settings, deploy-platform configOut of scope. These are decisions of your repo host and deploy platform; DaloyJS cannot enforce them from inside your code.

See Secure-by-default, Boot guards, and Supply-chain security for the full surface of each layer.