Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Deploy Lighthouse to Cloudflare

on:
workflow_dispatch:
push:
branches:
- main

jobs:
gate:
if: github.event_name == 'workflow_dispatch' || contains(github.event.head_commit.message, '[deploy lighthouse]')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v6
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run typecheck
- run: npm test

deploy:
needs: gate
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v6
with:
node-version: 20
cache: npm
- run: npm ci
- name: Deploy Worker while preserving provisioned secrets
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [1.26.0] - 2026-07-18

- Added the protected `GET /report?view=tgc` commercial analytics view with today, 7-day, and 30-day acquisition, audience, engagement, funnel, performance, content, and health summaries.
- Enforced a TGC-specific site/event allowlist, production-origin matching, path/URL consistency, bounded context, and query/fragment stripping at ingestion.
- Added explicit support for consent-created TGC visitor/session IDs while keeping all identifiers out of operator reports and downstream-summary contracts.
- Replaced stored unsalted site-event IP/user-agent hashes with minute-scoped keyed abuse identifiers kept only in the two-day rate table; raw events no longer store IP hashes, user-agent hashes, or request IDs, and scheduled maintenance scrubs those legacy columns from existing rows.
- Added raw site-event retention: 90 days for `tgc_site`, 30 days for other site-event properties.
- Reused the existing D1 schema; no migration was added or applied.
- Added a gated Cloudflare deployment workflow that runs the full validation suite and deploys only on manual dispatch or an explicitly marked release merge, preserving provisioned Worker secrets.

## [1.25.0] - 2026-07-18

- Added `BUS_CORE_TRAFFIC_TRUTH.md` as the authoritative definition of artifact traffic, successful responses, HMAC client-network buckets, inferred download intent, confirmed product signals, lead separation, privacy, retention, rollout thresholds, rollback, evidence, and blind spots.
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# buscore-lighthouse

## TGC website analytics

Version 1.26.0 adds an explicitly consented commercial analytics lane for `site_key=tgc_site`. Lighthouse is the raw-event and aggregate-report source of truth; the protected operator view is `GET /report?view=tgc` using the existing `X-Admin-Token` contract.

The view reports today, 7-day, and 30-day page execution, new/returning and session counts, acquisition, content, engagement, service-funnel outcomes, performance, and health. It never returns visitor/session/rate identifiers or form contents. Raw TGC events are retained for 90 days; rotating keyed rate identifiers are retained for two days. See `TGC_SITE_ANALYTICS_POLICY.md` for the product-specific justification and prohibitions.

Production deploys use the gated `.github/workflows/deploy.yml`: full tests first, then Wrangler only on manual dispatch or an explicitly marked release merge. Migrations remain separate and are never implied by deployment.

BUS Core artifact delivery and demand semantics are defined in `BUS_CORE_TRAFFIC_TRUTH.md`. Version 1.25.0 keeps downloads public while separating raw Worker traffic, successful artifact responses, privacy-preserving daily client-network buckets, probable-human intent proxies, confirmed product telemetry, and leads. Migration `0014_add_artifact_traffic_truth.sql` was applied remotely before the 2026-07-18 v1.25.0 deployment.

## BUS Core transition direction
Expand Down
12 changes: 12 additions & 0 deletions SOT.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Lighthouse — Source of Truth

## TGC consented commercial analytics — v1.26.0 branch implementation

The protected `GET /report?view=tgc` view is the canonical on-demand source for True Good Craft website analytics. It reads existing `site_events_raw` storage and returns consented page-execution metrics for today, 7 days, and 30 days: page views, sessions, visitors, first/returning visits, commercial intent, form funnel outcomes, deep-scroll and engaged-time milestones, average page-load/LCP/CLS, top events, paths, sources, campaigns, and sections. It never returns visitor IDs, session IDs, rate identifiers, user-agent hashes, request IDs, or form contents.

TGC site ingestion is an explicitly approved exception to the company-wide aggregate-only default. Random first-party visitor and session IDs are justified because aggregate counters alone cannot measure new versus returning visits, sessions per visitor, multi-page journeys, attribution continuity, or funnel progression. The visitor ID is created only after explicit analytics consent and persists in the browser for at most 395 days; the session ID renews after 30 minutes of inactivity. They are used only for TGC website measurement, are not linked to intake identity or other properties, and are not exposed downstream.

The server enforces the TGC event allowlist, production-origin match, path/URL consistency, origin-and-path-only URL storage, bounded context, and test-mode exclusion. Form values, typed content, keystrokes, raw IP addresses, user-agent hashes, exact location, fingerprints, cross-site advertising identifiers, and session replay are prohibited. Minute-scoped abuse identifiers use keyed HMAC and are retained for two days; they are not copied into raw events. Scheduled maintenance also nulls legacy IP-hash, user-agent-hash, and request-ID columns in existing site-event rows. Raw TGC events are pruned after 90 days; other site-event raw rows are pruned after 30 days. No new D1 migration is required because the existing site-event schema is reused.

Lighthouse remains the source of truth. Agent Smith may present this protected aggregate view through `/tgc`. Airtable may receive curated periodic KPI/campaign/content/experiment summaries later, but must not receive raw events or stable identifiers.

Production deployment is governed by `.github/workflows/deploy.yml`. It runs the complete typecheck/test gate and deploys only on manual dispatch or a main-branch commit explicitly marked `[deploy lighthouse]`; ordinary pushes do not deploy. Wrangler deployment preserves separately provisioned Worker secrets. Schema migrations remain a separate, explicit operation and this release requires none.

## BUS Core traffic truth and bounded delivery work — v1.25.0 deployed

`BUS_CORE_TRAFFIC_TRUTH.md` is the authoritative metric/privacy/retention/rollout contract for the new additive fields. Lighthouse now distinguishes Worker-visible artifact requests, successful 200/206 handoffs, full and partial responses, HEAD and Range traffic, declared response bytes, cache outcomes, daily HMAC/IP/version client-network buckets, repeats excluded from that proxy, inferred download intent, confirmed product events, and voluntary leads. None of these fields may be renamed to people, users, installations, completed downloads, or revenue.
Expand Down
65 changes: 65 additions & 0 deletions TGC_SITE_ANALYTICS_POLICY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# True Good Craft Website Analytics Policy

## Status

This is the product-specific declaration for `site_key=tgc_site`. It records the user-approved commercial analytics exception to the company-wide aggregate-only and no-persistent-identifier defaults. It does not change BUS Core, Star Map, or service-intake data policy.

## Levels and purposes

- Page level: consented browser execution, acquisition, content interest, navigation, engagement, funnel outcomes, performance, and sanitized reliability.
- Host level: Cloudflare traffic as a separate broad traffic source that may include bots and must not be described as human engagement.
- Internal: protected Lighthouse and Agent Smith aggregate reporting.
- User level: no analytics-only collection. Information intentionally submitted through an intake remains a separate business relationship path.

The purpose is to improve TGC content, acquisition, commercial offers, inquiry flow, and site reliability. Each allowed event must answer one of those questions.

## Explicit identifier exception

After explicit analytics consent, the TGC site creates:

- `anon_user_id`: a random first-party value retained in the browser for at most 395 days.
- `session_id`: a random first-party value renewed after 30 minutes of inactivity.

Aggregate-only measurement is insufficient for new-versus-returning analysis, sessions per visitor, multi-page journeys, attribution continuity, and service-funnel progression. These identifiers are used only for those TGC website questions.

They are not derived from IP address, user agent, account data, form data, device characteristics, or another property. They are not linked to intake identity, BUS Core, Star Map, advertising networks, or external profiles. They are not exposed in operator reports, Airtable summaries, logs, or exports.

Essential-only choice, Global Privacy Control, and Do Not Track keep optional analytics disabled. Withdrawing consent deletes the browser-side analytics identity.

## Allowed event data

- production origin and path, with query and fragment removed
- origin-and-path-only referrer
- `src` and bounded UTM attribution
- allowlisted semantic event name and bounded event value
- random visitor/session IDs and new/returning state
- coarse device, viewport, language, timezone, and edge country
- scroll/engaged-time/section milestones
- form identifier, field identifier, validation state, and submit outcome
- bounded page-load, FCP, LCP, CLS, and sanitized error category
- test-mode marker

## Prohibited data

- form values, names, emails, phone numbers, messages, typed content, or keystrokes
- passwords, credentials, intake payloads, or business records
- raw IP retention, stored user-agent hashes, request IDs in raw TGC events, or exact geolocation
- fingerprinting, session replay, cross-site advertising IDs, account linking, or enrichment
- full URL query strings/fragments, arbitrary event names, or arbitrary context keys
- visitor/session/rate identifiers in operator reports or Airtable

## Retention

- raw accepted/dropped TGC site events: 90 days
- minute-scoped keyed abuse-control identifiers: 2 days
- browser visitor ID: at most 395 days
- browser session ID: 30 minutes of inactivity
- longer-lived analytics: aggregate summaries only, without visitor/session/rate identifiers

## Reporting and downstream use

Lighthouse is the source of truth. `GET /report?view=tgc` is the protected aggregate contract and Agent Smith `/tgc` is the on-demand presentation surface. Airtable may later receive curated daily/weekly KPI, campaign, content, and experiment rows. Airtable must not be a raw-event sink.

## Safety

Collection is consented, fail-soft, production-origin restricted, rate-limited, and server-allowlisted. Analytics failure must never block navigation, forms, intake delivery, or mailto fallback.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "buscore-lighthouse",
"version": "1.25.0",
"version": "1.26.0",
"description": "Standalone deterministic metrics worker: manifest proxy + fixed daily counters + protected on-demand reporting.",
"scripts": {
"dev": "wrangler dev",
Expand Down
Loading