Skip to content

feat(open-knowledge): PostHog analytics for subscribe events#436

Merged
inkeep-oss-sync[bot] merged 1 commit into
mainfrom
copybara/sync
Jul 2, 2026
Merged

feat(open-knowledge): PostHog analytics for subscribe events#436
inkeep-oss-sync[bot] merged 1 commit into
mainfrom
copybara/sync

Conversation

@inkeep-oss-sync

Copy link
Copy Markdown
Contributor

No description provided.

* feat(open-knowledge): PostHog analytics for subscribe events

Capture newsletter signups in PostHog, server-side in the subscribe route, tagged
by originating surface. Lands on the privatized ok-marketing route (#2357).

- Emit newsletter_subscribed on success and newsletter_subscribe_failed (with a
  bounded reason) on the 400/502/503 paths, from the ok-marketing route.
- Bounded `source` allowlist (marketing_site, resources_menu, post_update_card;
  default unknown), validated with z.enum.
- Cookie-only identity via resolveDistinctId; no email/PII sent to PostHog
  (Resend stays the system of record). Port track.ts into ok-marketing with an
  import 'server-only' guard; reuses NEXT_PUBLIC_POSTHOG_KEY, no new dep.
- Thread `source` from all three surfaces: the marketing form and the app's
  resources menu + post-update card.
- Tests: route capture (success + every failure path, source validation, and the
  no-email-on-any-path guarantee) plus client body/arg assertions.

* refactor(open-knowledge): use posthog-node SDK for marketing capture

Switch the marketing app's server-side PostHog capture from a hand-rolled fetch
to the official posthog-node SDK (the documented server-side approach). Uses the
SDK's serverless pattern (flushAt:1, flushInterval:0, await shutdown()) inside
Next after() so it stays non-blocking. Same exported track API, so the route and
its tests are unchanged.

- Pin posthog-node to ^5.17.3 to match public/agents/agents-docs (no cross-tree
  drift, no allowlist).
- Keep the server-only guard, cookie distinct_id reuse, and $ip/$geoip privacy
  guards; the email is still never passed to PostHog.
- Add track.test.ts (mocks posthog-node + after) covering the capture shape,
  privacy guards, shutdown, and no-key no-op.

Docs keeps its existing hand-rolled track.ts; standardizing both apps on
posthog-node is a separate refactor.

* refactor(open-knowledge): drop unbounded referrer from subscribe capture

Address reviewer feedback: the `referrer` property (raw Referer hostname) is
attacker-controllable and unbounded on this public cross-origin endpoint, which
contradicts the bounded-cardinality discipline deliberately applied to `source`.
Drop it from the subscribe events (and remove the now-unused referrerHostname
helper). source remains the intended attribution dimension.

Also add resolveDistinctId unit tests (cookie stitching + UUID fallbacks) that
were previously only covered in the docs app — the cookie-name format is a
silent-drift risk worth pinning.

* chore(open-knowledge): drop analytics SPEC from the public tree

The subscribe analytics is primarily about the private ok-marketing app; keep
its design decisions out of public/open-knowledge/ (belt-and-suspenders — specs/
is not in the public-mirror allowlist, so it was never exposed, but it does not
belong in the public tree).

* refactor(open-knowledge): harden subscribe capture (reviewer follow-ups)

Address non-blocking reviewer recommendations on the analytics PR:
- Cap posthog-node shutdown at 3s so a hung flush can't keep the serverless
  function alive (matches the bound the docs helper used).
- Handle a thrown Resend SDK error the same as a returned { error } — a network
  throw previously escaped to a 500 with no CORS headers (unreadable by the
  cross-origin caller) and no failure capture; now it's a 502 + CORS +
  resend_error event like any other Resend failure.
- Add capture assertions for the 503 (unavailable), unparseable-JSON, and
  Resend-throw failure paths.

GitOrigin-RevId: 5e23b7817eb328a550ff8840b3e24b8d5c175a04

@inkeep-internal-ci inkeep-internal-ci Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated approval from agents-private public-mirror-sync (run: https://github.com/inkeep/agents-private/actions/runs/28566321842). Source of truth is the monorepo; direct edits on inkeep/open-knowledge are overwritten on next sync.

@inkeep-oss-sync inkeep-oss-sync Bot merged commit e2b54b2 into main Jul 2, 2026
2 checks passed
@inkeep-oss-sync inkeep-oss-sync Bot deleted the copybara/sync branch July 2, 2026 04:55
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