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 | ❌ |
Please do not report security vulnerabilities through public GitHub issues.
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.
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
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.
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 withnpm audit signatures. - GitHub build-provenance attestations (
actions/attest-build-provenance) over the packed tarballs — verifiable withgh 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.