Skip to content

Security policy

Please report security issues privately rather than opening a public issue.

Email security@wayscribe.dev, or open a confidential issue with the Confidential box ticked. Email reaches only the maintainer; a confidential issue is visible only to project members.

Include what you did, what happened, and what you expected. A proof of concept helps but is not required.

This is a small project without a dedicated security team. Expect an acknowledgement within a week. Please give a reasonable window for a fix before disclosing publicly.

Wayscribe is self-hosted. There is no hosted service, so there is no production environment to test against. Please test against your own installation.

In scope:

  • the API (apps/api), the web interface (apps/web), and the Node SDK
  • the @wayscribe/node package and the container images, once published (nothing is published yet, and releases will be 0.x)
  • the default Compose configuration

Out of scope, because they are known and documented rather than undiscovered:

  • The development defaults are published. ENCRYPTION_KEY and ADMIN_TOKEN ship with values committed to this repository so the demo runs with nothing configured. The API warns at every boot while they are in use. Running a real installation on them is a misconfiguration, not a vulnerability.
  • The admin token is a single shared secret. There are no user accounts, no per-user permissions, and no record of who used it. It can read every recorded payload of every project on the installation. See ADR-029.
  • Propagated journey context is not authenticated. A caller who can set headers on a request to an instrumented service, and who knows a valid journey ID, can attach events to that journey. Extraction validates shape only, which stops injection and garbage but not a well-formed forgery. This is stated in the Phase 4 design and is not an authorization control.
  • Anything requiring an attacker to already hold the admin token or a valid API key, unless it crosses a project boundary. Cross-project isolation is structural (composite foreign keys), and a break there is in scope.

Nothing is published yet. When a release is, its images at registry.gitlab.com/jojithedev/wayscribe/api and /web are signed with Sigstore keyless signing by this project’s GitLab release pipeline, and each platform’s image carries a signed CycloneDX software bill of materials. The signing certificate names the pipeline and the release tag, so a check like this one proves the image came from a tagged release of this repository. Replace vX.Y.Z with the release you run; releases will be 0.x, such as v0.1.0:

Terminal window
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.com

Extracting and checking the SBOM is described in docs/OPERATIONS.md §11. An image offered as Wayscribe that fails this check, or a signature from any other identity, is in scope: report it as above.

That claim rests on who can create a v* tag, because the certificate names the tag and nothing else about who pushed it. The v* tags must be protected in GitLab, creatable by Maintainers only, and this must be in place before the first release; until it is, anyone with Developer access could create a tag whose pipeline signs an image this check accepts. The release pipeline also runs only for tags of the exact form vMAJOR.MINOR.PATCH, so the repository’s phase-* and usable-v0 tags and pre-release tags never publish, but that is a guard against mistakes rather than a control.

Nothing captured is sent anywhere you did not configure: the only outbound requests the API makes are replays, to destinations an admin registered on a host REPLAY_ALLOWED_HOSTS lists. The running services send no telemetry and no analytics, and make no outbound connection other than the ones your own configuration creates. Building the images is not offline: it downloads base images, Alpine packages and npm packages. Next.js’s build telemetry is switched off (NEXT_TELEMETRY_DISABLED=1) in the web image and in the web package’s scripts.

Entity identifiers and alias values are encrypted at rest with keys derived from ENCRYPTION_KEY. Payloads are not: they are stored as jsonb, which is exactly why redaction matters: payloads are redacted in your process before they leave it, and again on the server before they are written, against a built-in list of secret names matched at any depth.

Wayscribe records the contents of your integration payloads. Treat the database as holding whatever your workflows carry. If that includes regulated data (health records, payment details, government identifiers), review captureMode before enabling it. metadata-only records the shape of a journey without storing payloads at all.

See docs/SECURITY.md for the threat model and docs/OPERATIONS.md for key handling.