Security review packet
For a security engineer deciding whether a Wayscribe pilot may run in their environment. Every statement links to its source; the threat model is SECURITY.md.
Data location
Section titled “Data location”- Self-hosted, with no hosted service and no account (../SECURITY.md). All state is in your own PostgreSQL, with an ordinary role and no extensions (OPERATIONS §1, ADR-037).
- The running services send no telemetry. Building the images downloads packages; Next.js build telemetry is disabled (../SECURITY.md).
- The API’s only outbound requests are replays to admin-configured
destinations on
REPLAY_ALLOWED_HOSTS(send.ts, SECURITY §9).
What is stored
Section titled “What is stored”Capture mode is per environment, and new environments get redacted-payload
(migration 002).
metadata-only stores no input or output payloads; full-payload also needs
ALLOW_FULL_PAYLOAD_CAPTURE, off by default
(INGESTION_CONTRACT §7).
| Stored as | What |
|---|---|
plain jsonb, after redaction |
payloads; error, runtime, deployment and custom metadata |
| plain text | event names, timings, trace ids, entity type |
| plain text, by the host’s choice | journey labels; copies of aliases marked displayable |
| AES-256-GCM | entity identifiers and alias values (searched by HMAC tokens), replay destination headers |
Sources: ingest-event.ts, SECURITY §6, SECURITY §7, ADR-053, ADR-054. Error messages are masked by shape, and stacks are dropped unless full capture is on (ADR-046).
Redaction
Section titled “Redaction”- By field name at any depth, including header shapes, in the SDK and again on
the server; a match becomes
[REDACTED](ADR-035, SECURITY §4). The built-in list includes eight webhook signature headers (default-secrets.ts). - Not caught: a credential under a name no rule knows. The SDK and
doctorwarn about secret-looking names but do not redact them (SECURITY §4, ADR-055).
Authentication and authorisation
Section titled “Authentication and authorisation”- One admin token (at least 32 characters) reads any project and can delete and replay. There are no user accounts, no roles, and no record of who used it (ADR-029, schema.ts).
- API keys carry 192 random bits, are stored as a prefix and an HMAC, and are scoped to one project and one environment. They cannot delete, or write into another environment’s journey (api-key.ts, SECURITY §2, SECURITY §14).
- Throttle: five failed credentials a minute from one address locks it for five minutes, per process. Ingestion is not throttled (OPERATIONS §9).
- Web session: a cookie signed with a key derived from the admin token;
HttpOnly,SameSite=Strict,Securein the image, 12 hours (login route, web Dockerfile). A nonce-based CSP and anti-framing headers are set on every page (SECURITY §2). - Reader token (optional,
READ_TOKEN): reads one project, payloads included, and may call only an allowlist of read routes. Anonymous read-only web mode signs every visitor in as that reader and refuses to start with an admin token set; it exists for a public demo of generated data only (ADR-070, ADR-069, OPERATIONS). The hosted demo runs IPv4 only with 22, 80 and 443 open, a deploy user limited by sudoers,noindex, and HSTS and CSP checked from outside (DEMO_HOSTING, DEMO_HOSTING).
Deletion, retention, key rotation
Section titled “Deletion, retention, key rotation”- Retention is per environment, swept hourly (OPERATIONS §7). Deletion is hard and audited without the value, and erasure does not search payload contents (OPERATIONS §8).
- Deleted or masked data stays in dead row versions until vacuum, and in WAL, replicas and backups (OPERATIONS §8).
- Key rotation has a grace period with the old key read alongside the new one (OPERATIONS §6, ADR-044).
Supply chain
Section titled “Supply chain”CI blocks on pnpm audit, gitleaks and Trivy. The current release is v0.2.3
(2026-10-08, annotated tag on b12397b). Its api, web and demo images were
signed by the tag pipeline and verified with cosign 3.1.3 in the release
session; cosign 2.x cannot read the v3 signature bundles and reports no
signatures. @wayscribe/node, the PyPI wayscribe wheel and sdist, and the Go
module wayscribe.dev/go all resolve to 0.2.3 on their public registries
(rechecked 2026-10-09). The release raises dependency overrides for new
advisories (sharp 0.35.5, source-map-js, uuid 11); a moderate sprintf-js
advisory has no fixed version yet
(CHANGELOG). The full artifact checks, SBOM
attestations and npm and PyPI provenance are recorded for v0.2.2
(release record,
v0.1.0 record,
OPERATIONS §11). To verify, with your tag
(full steps):
cosign verify registry.gitlab.com/jojithedev/wayscribe/api:vX.Y.Z \ --certificate-identity 'https://gitlab.com/jojithedev/wayscribe//.gitlab-ci.yml@refs/tags/vX.Y.Z' \ --certificate-oidc-issuer https://gitlab.comNetwork
Section titled “Network”- API on 8080, web on 3000, published on
127.0.0.1only; terminate TLS at a proxy (OPERATIONS §9). - Metrics are off unless
METRICS_PORTis set (OPERATIONS §13).X-Forwarded-Foris ignored unlessTRUSTED_PROXY_COUNTis set (OPERATIONS §9).
Known gaps
Section titled “Known gaps”- Not there: SSO, user accounts, roles, per-user audit, and encryption of payloads beyond the database’s own (ROADMAP, ROADMAP).
audit_eventsis never swept, and the login limiter is per process (ROADMAP). The development secrets are published, and propagated context is not authenticated (../SECURITY.md).- The pre-release security review of 2026-09-16, done by an AI review agent at the maintainer’s direction and not an independent audit, found no Critical, High or Medium issues. No outside party has reviewed the code. Among its low notes: a dry run holds row locks for the whole batch (INGESTION_CONTRACT §8), and a text search that matches nothing scans its whole window, bounded only by the statement timeout (OPERATIONS §10).
Reporting a vulnerability
Section titled “Reporting a vulnerability”Email security@wayscribe.dev, or open a confidential issue at https://gitlab.com/jojithedev/wayscribe/-/issues/new. Expect an acknowledgement within a week (../SECURITY.md).