Live Draw separates presenter access from viewer access. The relay stores sessions, device credentials and the latest shared image in memory.
HOST_ACCESS_KEY is the operator-managed presenter password. Non-loopback
binding requires an explicit HTTPS origin and a password of at least 32
characters. Remote HTTP origins are rejected at startup, including behind a
loopback proxy listener. Browser clients refuse to transmit passwords and
session capabilities over remote HTTP.
Private editor mode also requires a password and protects editor HTML, scripts
and Klecks bundles before they are downloaded.
Sign-in exchanges the password for a separate 256-bit random device credential.
The relay retains its hash. Cookies are host-only, HttpOnly and SameSite=Strict;
HTTPS adds Secure and the __Host- prefix. Remembered access expires after
30 days, and browser-session access after eight hours. Restart clears all
credentials. Forgetting a device revokes only that browser's access.
HTTP mutations and WebSocket upgrades require the configured origin. Cookie
security uses PUBLIC_ORIGIN, including behind TLS termination, rather than
arbitrary forwarded headers. Network sign-in requires HTTPS. The browser does
not save the presenter password in local storage.
EDITOR_ACCESS=public allows editor downloads and local drawing without
sign-in, while session creation remains password-protected. Code already loaded
in a browser continues to run locally after access is revoked.
Each session has separate presenter and viewer capabilities. Presenter control
stays in the presenter's sessionStorage; the viewer link carries only its
viewer capability in the URL fragment. Fragments are absent from HTTP request
URLs. WebSocket upgrades carry the capability in Sec-WebSocket-Protocol,
which must be excluded from proxy logs. The relay validates it before accepting
the connection and binds the first authentication message to the same session
and role. Viewer sockets accept at most 2 KiB per incoming message; only valid
presenter capabilities unlock the frame-sized receive budget.
A viewer capability grants read-only access to that session. Anyone holding the link can watch until stop or expiry; viewers do not have named accounts. Share links with the intended audience and exclude capabilities, cookies and passwords from logs.
The relay retains only the latest complete bounded image. It records no session and stores no project. Stop, expiry and restart discard retained pixels. Viewers can save or screenshot images they receive. Save drawings through the presenter's local export controls before reload, restart or deployment.
The relay bounds sessions, viewers, frame size, frame rate, unauthenticated connections, queued output and authentication attempts. Startup validates combined media budgets against declared and detected runtime memory limits. Retained frames and unique queued payloads are accounted for globally, with headroom for active reception and connection state. Each session reserves only one presenter receive slot, including before its first authentication message. Sequence numbers and connection epochs prevent stale frames replacing newer views. Presenter message and byte budgets, plus an aggregate ingress byte budget, apply to image frames and control commands before parsing or status fanout. Sustained excess ends that session and revokes its capabilities. Slow viewers are disconnected when their outgoing queue exceeds its budget.
Client limits use the socket peer address by default. Only exact configured
TRUSTED_PROXY_ADDRESSES may supply a single valid X-Real-IP; the proxy must
overwrite that header from its own peer address. Other forwarded headers are
ignored. Password failures, sign-ins, session creation and authenticated socket
upgrades have independent budgets. Anonymous failures cannot exhaust an
authorized client's session or reconnect budget. Keep the application port on
host loopback. Application budgets complement host and
network controls; they do not provide volumetric attack protection.
The supplied container runs as UID/GID 1000 without a shell, npm or build tools. Compose sets a read-only filesystem, drops Linux capabilities, prevents privilege escalation and limits CPU, memory, swap and process count. It mounts no host directories or Docker socket. See Self-hosting for configuration and proxy logging requirements.
The Docker build pins the Klecks revision, npm lockfiles and container base digests. CI audits runtime dependencies and scans the exact Linux AMD64 image with Trivy. HIGH and CRITICAL image findings fail the workflow, including findings without a vendor fix. Review the security artifact for lower-severity findings and update dependencies as vulnerability data changes.
The separate klecks-build-security.patch overrides brace-expansion with
2.1.7 and source-map-js with 1.2.2 in the editor's build dependency tree.
It stays independent of the viewport API patch.
The build audit permits one exact advisory,
GHSA-vfj7-8cjw-p6xm, only for
braces, micromatch and @parcel/watcher. These Parcel/Sass dependencies
process trusted build source and are absent from the browser import graph and
final runtime image. scripts/audit-klecks.mjs rejects other HIGH or CRITICAL
build findings. Keep builds isolated and lifecycle hooks disabled; review the
exception when updating upstream, dependencies or build inputs.
The runtime uses a digest-pinned Distroless Node.js 24 image and contains only the application and runtime dependency tree. Deployment uses the exact tested and scanned image by immutable digest. Build-stage OS packages can change, so source pins do not imply byte-identical images.
Manual dispatch on main is required for publication and deployment. Verification
jobs cannot access deployment secrets or write packages. The deployment job uses
a short-lived registry token and a dedicated SSH key with a pinned host key.
The confined receiver checks the package allowlist, image platform and Git revision label, then updates only Live Draw. It accepts deployment, status and rollback commands without providing general shell or Docker access. Operator secrets remain on the host; temporary registry credentials are removed after pulling. Failed readiness restores the previous image.
See GitHub Actions for provisioning and release operation, and Testing for access-control and deployment checks.
Use GitHub's private vulnerability reporting to report a security issue. You can also use the contact details on the maintainer's GitHub profile.
Include the affected revision, configuration and a minimal reproduction with synthetic data. Keep passwords, device credentials, viewer links and real drawings out of reports. Public issues are for reports that do not expose exploitable security details.