Skip to content

Security: writerslogic/facet

Security

SECURITY.md

Security Policy

Supported Versions

Facet is pre-1.0 and under active development. Security fixes are applied to main; there is no long-term support branch yet.

Version Supported
main
< 0.3

Reporting a Vulnerability

Please do not report security vulnerabilities through public GitHub issues.

Preferred Method

Open a private advisory via GitHub Security Advisories, or email admin@writerslogic.com.

Please include:

  • A description of the vulnerability and its impact
  • Steps to reproduce (do not include real API keys, admin tokens, or .dev.vars)
  • The affected surface (Worker/API, dashboard, client, or CLI) and version/commit

You can expect an initial response within a few days. Coordinated disclosure is appreciated; please give us a reasonable window to ship a fix before publishing details.

Scope & privacy model

Facet is privacy-first by design: unique visitors are counted with a daily-rotating, salted SHA-256 hash, and raw IP addresses are never stored, logged, or returned. Reports that are especially valuable include:

  • Any path by which a raw IP, user-agent, or other PII could be persisted, logged, or exfiltrated
  • Any way to recover cross-day visitor identity from stored data (the model guarantees this is cryptographically prevented)
  • Authentication bypasses on the admin (ADMIN_TOKEN) or API-key (clk_...) surfaces
  • Cross-site data access (a key or token reading another site's data)
  • Injection (SQL/JSON) via the collect, event, stats, or admin endpoints

Handling of secrets

Worker secrets: ADMIN_TOKEN (admin auth) and CF_API_TOKEN (Analytics Engine reads) are required; WEBHOOK_SECRET (anomaly-webhook HMAC), FACET_SIGNING_JWK (attestation/provenance signing key), and SCITT_TOKEN (external transparency service) are optional and only used when their features are enabled. API keys are stored only as SHA-256 hashes; plaintext keys are shown once at issuance and are never retrievable. FACET_SIGNING_JWK holds a private key — its public half is published at /.well-known/jwks.json, but the private JWK must live only as a Worker secret. Never commit .dev.vars, tokens, private keys, or database ids in a report or PR.

Supply-chain provenance

The public npm packages (@writerslogic/facet, @writerslogic/facet-cli) are published from a tag-triggered GitHub Actions workflow (.github/workflows/release.yml) with two independent provenance signals, both signed via the workflow's OIDC identity through Sigstore (recorded in the public Rekor transparency log):

  • npm provenance (npm publish --provenance) — links each published version to the exact source commit and build workflow. Visible on the npm package page and verifiable with npm audit signatures.
  • GitHub build-provenance attestations (actions/attest-build-provenance) over the packed tarballs — verifiable with gh attestation verify <tarball> --repo writerslogic/facet.
  • Attested SPDX SBOM (actions/attest-sbom) — a Software Bill of Materials for each release, signed against the same tarballs and uploaded as a build artifact.

All GitHub Actions are pinned to full commit SHAs, and CodeQL, OpenSSF Scorecard, and Dependency Review run in CI.

SLSA level: this reaches SLSA v1.0 Build Level 2 — provenance is generated by a hosted build platform (GitHub-hosted runners) and is signed/non-forgeable, but the build is not yet run on a hardened, isolated reusable workflow. Reaching Build Level 3 would require moving the publish job into an isolated reusable workflow with provenance generated outside the job's control; that is a planned hardening step, not a current guarantee.

There aren't any published security advisories