secureDefaults enforcement
Daloy ships a focused slice of cross-cutting bake-ins from the secure-by-default initiative. Three items are implemented now; the remaining cross-cutting bullets (single-source helpers for cookie / client IP / time-claim / secret comparison, the __Secure- cookie without TLS refuse-to-boot, the daloy doctor --audit-secrets subcommand, and the zero-runtime-dependency governance CI grep gate) remain tracked on the roadmap and will land in subsequent additive 0.26.x releases.
1. secureDefaults: false master-flag enforcement
The wholesale escape hatch for the entire secure-by-default surface now refuses-to-construct in production unless you also pass acknowledgeInsecureDefaults: true. This closes the well-documented "developer flipped the flag off while debugging and shipped to production" footgun by forcing an explicit two-step opt-in:
Any time the flag is off, a once-per-process error log is emitted with event: "secure_defaults.disabled" enumerating every default it disabled — so the blast radius is loud at boot even when the option was set deep in shared configuration:
- auto
secureHeadersinstall - cross-origin guard for state-changing requests
- crash-on-unhandled-rejection (production)
- first-request
X-Forwarded-*/trustProxyguard session()+ state-changing route requirescsrf()boot guard- weak session secret refuse-to-boot
cors({ origin: '*' })refuse-to-boot- anonymous stateful plugin refuse-to-boot
Per-feature opt-outs (secureHeaders: false, corsCrossOriginGuard: false, crashOnUnhandledRejection: false, trustProxy: false, csrf: "off") remain available without the production refusal — prefer those when you only need to disable one default rather than the whole surface. Tests can reset the audit-log latch via the exported _resetInsecureDefaultsLogForTests() helper, mirroring the existing _resetCrashHandlersForTests pattern.
2. JWT HS-secret length refuse-to-construct (RFC 7518 §3.2)
createJwtSigner() and createJwtVerifier() now refuse Uint8Array HS-shaped secrets shorter than 32 bytes at construction time. RFC 7518 §3.2 sets the floor at the hash output size (32 bytes for HS256) — and Daloy applies the same floor to HS384 and HS512 because a shorter key does not buy a stronger HMAC, it only reduces the effective entropy.
3. secureHeaders() refuses dual framing-defense disable
secureHeaders() ships two layered defenses against clickjacking: the X-Frame-Options header (legacy browsers) and a CSP frame-ancestors directive (modern spec). The helper now refuses to construct when both are disabled simultaneously — that combination silently re-opens the clickjacking surface the helper exists to close:
If you only want to disable one of the two defenses, keep the other one on — the helper's defaults already wire both layers, so the common case (no options passed) needs no changes.
4. Mandatory hardware-backed 2FA for publish access
Daloy's supply-chain posture now mandates hardware-backed 2FA for every contributor with publish access, documented in SECURITY.md as a release-checklist item:
- GitHub organization level:
Settings → Authentication security → Require two-factor authenticationis enforced on the@daloyjsorg; every account with write access must have a hardware-backed factor (passkey or security key — TOTP-only accounts are off-boarded). - npm registry level:
npm access 2fa-requiredis set on@daloyjs/coreandcreate-daloy; OIDC trusted publishing from the protectednpm-publishenvironment means publishes themselves carry no long-lived token, but every maintainer who can approve the environment still needs hardware-backed 2FA on the registry account. - Off-boarding: when a maintainer leaves rotation, their org membership, publish grants, and granular tokens are revoked in the same change.
- Release-checklist audit gate: before tagging a release the maintainer running the release verifies that every contributor who approved the
npm-publishEnvironment for that release has 2FA enabled at both levels (the mandatory-2FA audit gate).
What's next
The remaining cross-cutting bullets stay tracked on the roadmap and will land in subsequent 0.26.x additive patches: single source of truth for cookie writes / client IP / time-claim validation / secret comparison; the __Secure- cookie without TLS refuse-to-boot guard; the daloy doctor --audit-secretssubcommand; the zero-external-runtime-dependency governance CI grep gate; and the timing-safe-comparison CI grep gate. Together these items remove the last "developer remembered to do X but not Y" failure modes by making the framework's security surface internally self-consistent.