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 | ❌ |
This is the policy for Facet the software, maintained in this repository. It is not the policy of
any particular deployment. If you found an issue in someone's running Facet instance (a
misconfiguration, an exposed admin token, exposed data), report it to that instance's operator —
a deployment publishes its own operator's contact at /.well-known/security.txt, which Facet serves
only from a contact that operator explicitly configured. Report it here as well if the underlying
cause is a bug in Facet itself.
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) is required; CF_API_TOKEN (Analytics Engine reads — the
AE mirror writes through its binding and needs no token), 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.