Skip to content

Audit fixes, licence revocation fix, gh-pages deployment, static generation with hydration, accessibility and performance - #1

Merged
qectorlab merged 5 commits into
mainfrom
fix/audit-2026-11
Nov 11, 2026
Merged

qectorlab merged 5 commits into
mainfrom
fix/audit-2026-11

Conversation

@qectorlab

@qectorlab qectorlab commented Nov 11, 2026 •

Copy link
Copy Markdown
Owner

What this is

The result of a full audit of the site, both Workers and the deployment, followed by a frontend quality pass. Everything in this branch is live.

  1. Licence Worker 2.2 and header Worker — refunds and chargebacks on subscription payments now revoke the licence (they were silently acknowledged before); tighter Content-Security-Policy; image caching; the production audit script passes against the live site.
  2. Built site removed from main — GitHub Pages serves the gh-pages branch, so the React source, Workers and tests are no longer public on qector.store.
  3. Site fixes — blog articles were invisible to visitors; contact form mail draft; footer version lookup; pricing-card ticks; the simulator's pass/fail test; documentation corrected against the generated API index; bundled fonts; unused UI kit and animation library removed.
  4. Frontend quality — pages are rendered to static HTML by React at build time and hydrated in the browser (no browser needed to build, nothing rendered twice); font preload; lighter blog pages; accessibility fixes; npm run deploy and npm run check.
  5. Cloudflare e-mail obfuscation opt-out — it rewrote addresses in served HTML and broke hydration on /contact/.

Live state (2026-11-11)

  • Site: published from gh-pages with npm run deploy -- --push.
  • qector-fulfilment 2.2 and qector-headers deployed with Wrangler; /stripe/health reports version 2.2 with the signing key matching.
  • Stripe: invoice_payment.paid added to the live webhook endpoint (events use API version 2026-06-24.dahlia).
  • Repository: Dependabot alerts on; a ruleset blocks force-pushes and deletion of main and gh-pages.

Verification

  • Worker tests: 26/26 and 4/4. The previously deployed licence Worker was byte-identical to a bundle of main before the deploy.
  • npm run check on the published build: static checks 0 problems; browser check clean on 57 routes at laptop and phone sizes (the build before this work failed 544 checks under the same test); axe accessibility audit (WCAG 2.1 A/AA and best practices) reports no violation.
  • Live: production audit passes; key pages load in a real browser with no Content-Security-Policy violation and the booking widget working.

Left to the owner

  • Pricing copy that promises more than the licence agreement: "Priority support with SLA", "($5,000 value)", "GPU (CUDA/OpenCL)". Untouched, as are the legal texts.
  • Sparse Blossom is labelled "experimental" on some pages and "stable" on others; the conservative label was kept.
  • Package, not site: decoder 1.0.1 accepts non-graphlike matrices in the Blossom decoders, and its import banner still advertises the withdrawn 14-day trial.
  • GitHub Actions jobs do not start while the account is locked for billing, so the CI and nightly audit workflows cannot run yet.
  • Cloudflare SSL mode is "Full", not "Full (strict)": the origin leg to GitHub Pages is encrypted but not certificate-verified.

🤖 Generated with Claude Code

qectorlab and others added 5 commits November 11, 2026 03:27
…er and audit fixes

qector-fulfilment 2.2
- A refund or chargeback of a subscription payment matched no licence and
  was acknowledged without revoking anything: subscription-mode Checkout
  Sessions carry no PaymentIntent, and since API 2025-03-31 a Charge no
  longer names its invoice. Licences are now found through payment-to-invoice
  links (invoice_payment.paid, or invoice.paid on older API versions) and,
  for refunds, through a per-customer index with an unambiguous-match rule.
- A refund or dispute that still matches nothing fails the event instead of
  answering 200, so Stripe retries it and it is visible.
- The amount-to-tier fallback only applies to USD amounts.
- Seven new tests use the event shapes Stripe sends for subscriptions.
- README: how matching works, the new invoice_payment.paid event to enable,
  and a note that RECONCILE_TOKEN can issue or revoke any licence.

qector-headers
- Content-Security-Policy no longer lists Stripe script/frame hosts or Google
  font hosts: checkout is a plain link and fonts are bundled with the site.
- Images under /images and /og are cached for a day instead of revalidated
  on every view.

Production audit
- Drop the check for a hero video that was removed from the site, and stop
  requiring a page-rendering header on the JSON licence endpoints. The audit
  now passes against the live site.
- New CI workflow (type-check, lint, build, Worker tests) and Dependabot
  configuration.

README: repository layout and the gh-pages deployment.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The repository root held a copy of the production build next to its source,
and Pages served the whole root, so the React source, the Workers and their
tests were public on qector.store, and every old build's hashed files piled
up (586 of the 659 files in assets/ were unreachable).

The built site now lives only on the gh-pages branch, written by
`npm run deploy` from source/. Nothing here changes what is served.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…performance and tooling

Fixes (in the build deployed to gh-pages on 2026-11-11)
- Blog articles were invisible to every visitor with JavaScript: the
  reveal-on-scroll code hid each block until it was 10% on screen and never
  saw blocks added after its first scan. Content is now visible by default
  and only hidden while the observer is actively waiting for it.
- Contact form: the mail draft used "+" for spaces. Footer: the "latest
  version" lookup never matched PyPI's feed and now reads its JSON API,
  once per session.
- Pricing cards showed a tick for excluded features. Blog pages cancelled
  every in-page link, including the skip link.
- Simulator: two crossing error chains were reported as a logical failure;
  the result is now the parity of the residual on the boundary.
- Fonts are bundled (no Google Fonts request, no inline handler for the
  Content-Security-Policy to block). The unused Stripe script is gone.
- Documentation now matches the generated API index (signatures, method
  names, defaults); versions, Python range and install pin come from
  lib/releases.ts; licence and tax wording corrected; blog figures cite the
  decoder changelog; discovery files (.well-known) corrected.
- Removed the unused UI kit (52 files) and its dependencies, and the
  animation library: 12 runtime dependencies instead of 47. No high or
  moderate advisories remain in production dependencies.
- The build type-checks first and fails if prerendering cannot run.

Accessibility and performance
- Scrollable code and table blocks are keyboard-reachable; link names
  contain their visible text; the open mobile menu holds focus; a fresh
  page load no longer moves focus past the navigation; print styles.
- The simulator and the booking widget load on demand; images are cached.

Tooling
- `npm run deploy` publishes dist/ to the gh-pages branch and keeps the
  previous build's hashed files for one more deploy.
- `npm run check` runs static checks, a browser check of every route at two
  screen sizes behind the real security headers, and an axe accessibility
  audit.

Not yet verified end to end
- Static generation with React (scripts/prerender.mjs, src/entry-server.tsx)
  and hydration in src/main.tsx replace the Playwright capture and the
  render-twice-and-swap startup. This part was written after the deployed
  build and its full check run is still pending.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…and page-weight budget

- Preload the text font with the HTML instead of after the stylesheet.
- Blog renderer no longer bundles an HTML-in-Markdown parser that no post
  uses: about 170 kB less script on every blog page. Raw HTML in a post is
  now shown as text rather than interpreted.
- Accessibility: blog tables are keyboard-reachable scroll regions named
  after their column headings; the home page's command blocks have distinct
  names. The axe audit (WCAG 2.1 A/AA and best practices) reports no
  violation on any of the 57 routes at laptop or phone size.
- Static check: a page-weight budget for the script each page asks for up
  front (450 kB, 1100 kB for blog posts).
- Browser check: a block still waiting off screen is no longer reported;
  each hidden block is brought into view and must then appear.
- CI builds with the new static-generation step and runs all three checks.
- README: the browser is needed for `npm run check`, not for the build.

The static-generation and hydration rework from the previous commit is now
verified: build, lint, static check and accessibility audit pass on it.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…hydration

Cloudflare rewrites every e-mail address in the HTML it serves. That changed
the text React attaches to and the live /contact/ page reported a hydration
text mismatch (React error 418) and re-rendered. The prerender step now wraps
the page content in Cloudflare's <!--email_off--> markers, outside #root.

The static check fails if a hydrated page lacks the marker, and the browser
check accepts CHECK_ROUTES to run on a few routes.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@qectorlab qectorlab changed the title Audit fixes, licence revocation fix, gh-pages deployment, accessibility and performance Audit fixes, licence revocation fix, gh-pages deployment, static generation with hydration, accessibility and performance Nov 11, 2026
@qectorlab
qectorlab marked this pull request as ready for review November 11, 2026 10:36
@qectorlab
qectorlab merged commit 46b1346 into main Nov 11, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant