Only the latest tagged release and main receive security fixes. Release candidates (-rcN) are supported until the corresponding final release ships.
Report suspected vulnerabilities privately via GitHub Security Advisories:
https://github.com/srdjan/zigttp/security/advisories/new
Do not open public issues for security reports. After triage you will receive a remediation plan or a rationale for closing the report.
Include, where possible:
- Affected version (tag, commit, or binary hash)
- Minimal reproducer (handler source, CLI flags, request)
- Observed vs. expected behavior
- Impact assessment (panic, isolation bypass, data exposure, etc.)
In scope: the runtime (packages/runtime/), the zigts engine and virtual modules (packages/zigts/), the build-time tooling (packages/tools/), and the zigttp deploy client path.
Out of scope: the hosted control plane itself (api.zigttp.dev), which has its own reporting channel, and third-party extensions built with zigttp-sdk.
The high-level boundaries enforced by zigttp:
-
Handler isolation. Each request acquires a
Runtimeinstance from a pool. Recycling underreuse_unboundedis only enabled when the compile-time contract provespure+deterministic. Arena-escape and string-table audits run on every release; failures are logged via the runtime metrics (arena_audit_failures,persistent_escape_failures). -
Capability boundary. Virtual modules declare
required_capabilitiesin theirModuleBinding. The set is aggregated into the handler's contract and used by deployment policy to refuse mounts that exceed the granted surface. See docs/internals/capability-audit.md for the per-module review and known caveats (only.runtime_callbackis enforced at module call time; other capabilities are policy-level). -
HTTP boundary. Headers reject CRLF and NUL bytes before allocation (commit
6e65bb7,packages/runtime/src/http_types.zig). Body size defaults to 1 MiB and is rejected with 413 on overflow. Static-file paths reject traversal (..), absolute paths, drive letters, and NUL bytes (isPathSafe); a second-line canonical-realpath check (isCanonicalPathInsideRoot) catches symlink escapes. -
WebSocket boundary. RFC 6455 frame parsing is delegated to
std.http.Server.WebSocket; runtime maps stdlib errors to wire-correct close codes (1002 for protocol error, 1009 for oversize). See docs/internals/websocket-audit.md for the full checklist and the one documented gap (peer-sent close-code validation, deferred to W2). -
Attestation trust model. Slice 1 (current beta): the JWS protected header carries the full Ed25519 public key.
zigttp verify <url>validates the signature against that embedded key and prints the key fingerprint. This proves "the holder of this key signed these claims" - not "this key belongs to a specific publisher." For now, third-party verifiers should pin keys with--trust-key <hex>. Slice 2 adds identity binding via well-known endpoints. See docs/roadmap/attest-slice-1.md and docs/roadmap/attest-slice-2.md. -
Self-extracting binary integrity. The 32-byte trailer's CRC-32 is a corruption check, not a security check. The actual integrity boundary is the JWS attestation over
(bytecode_sha256, contract_sha256, policy_sha256). See docs/internals/architecture.md - Self-Extracting Binary Trailer.
- docs/threat-model.md - threat model for the rule review system and Claude Code integration
- docs/verification.md - compile-time verification invariants
- docs/internals/capability-audit.md - per-module capability declaration audit
- docs/internals/websocket-audit.md - RFC 6455 compliance audit
- packages/zigts/src/module_binding.zig - capability enforcement surface