# Website security posture

## Current design

- Static HTML, CSS, JavaScript, SVG, and images only.
- Zero production dependencies and no package install required.
- The documentation wiki is generated at build time from trusted repository Markdown. The
  dependency-free generator escapes HTML, restricts link schemes, and emits static markup; no
  Markdown or HTML is interpreted in the browser.
- No authentication, contact form, user input, analytics, external font, cookie of our own,
  or storage of personal data.
- **One third-party script: Paddle's checkout** (`https://cdn.paddle.com/paddle/v2/paddle.js`),
  on the home page only, which carries the price and the buy button. Paddle require it to be
  served from their CDN so it always ships their current security fixes; a self-hosted copy is
  not a supported configuration. It renders the checkout in an overlay so the buyer stays on the
  site. Paddle is the merchant of record, so card details are entered into Paddle's frame and
  never touch this site or this repository. `tools/check.mjs` holds the single-entry allowlist,
  so any second external resource still fails the static check.
- **Paddle.js tries to load a second third party, and we deliberately block it.** Once
  initialised it requests `https://public.profitwell.com/js/profitwell.js` — ProfitWell, Paddle's
  Retain product. That domain is **not** in the CSP, so the browser refuses it. Verified on
  2026-08-07 that the checkout overlay still opens and still renders normally with it blocked;
  Paddle.js itself proceeds with `retain_enabled=false`. Retain recovers failed *subscription*
  payments and this product is a one-time perpetual licence, so it has nothing to do here, and
  allowing it would contradict the "no analytics, no trackers" promise the privacy policy makes.
  The cost is four blocked-resource console errors per checkout page load. **If Paddle ever ask
  for it during domain review, that is an owner decision and the privacy policy must change with
  it.**
- **No secret is embedded.** The client-side token Paddle.js is initialised with is public by
  design and can only open a checkout. The API key and the webhook signing secret are not in
  this repository and must never be served to a browser.
- Our own JavaScript only changes local presentation state: navigation, same-origin screenshot
  switching, reveal classes, filtering documentation links, and opening the checkout overlay.
  It stores nothing and sends no request.
- A strict Content Security Policy blocks inline code, objects, form submissions, and non-local
  runtime assets. Third-party network connections and frames are allowed only for
  `*.paddle.com`, and only the home page's own meta CSP permits even that — every other page
  keeps `script-src 'self'` and no `frame-src`, and the header and meta policies are enforced
  together. `style-src` carries `'unsafe-inline'` because Paddle's overlay styles its own
  container; this is the one hardening concession the checkout costs.
- The local server rejects methods other than GET/HEAD, decodes paths once, contains resolved
  paths inside the website root, emits explicit MIME types, and sends hardening headers.

## Required at deployment

The host must apply `headers.conf`, including `frame-ancestors 'none'`. A CSP delivered in a
`meta` tag cannot enforce that directive. HTTPS must be enabled before relying on HSTS.

Checkout is now present, so its two remaining gates are deployment blockers:

1. **A live client-side token must be pasted into `paddle-checkout.js`.** Until it is, the buy
   button is not rendered at all and the page keeps its "checkout is not open yet" wording.
2. **`cosmikstreamer.com` must pass Paddle's domain approval** (Paddle > Checkout > Website
   Approval). Paddle refuse to open a live checkout from an unapproved domain, so the button
   will fail in production until that is granted.

If a mailing list, analytics, or a contact form is added later, this document and the CSP must
be redesigned before deployment. Those features introduce new data, privacy, fraud, abuse,
provider, and compliance boundaries; they are not safe drop-in additions.

## Reporting

Do not publish a security-contact address until the owner chooses the monitored address and
response process. An unmonitored address would create a false promise.
