Context
Both #24 (frontend events) and #25 (server-side events) need a PostHog project to send to. It was originally bundled into #24's scope simply because #24 was the first issue to need it — which made #25 depend on a frontend ticket for something that is not frontend work.
Pulling it out here so either side can proceed independently. This is a signup and some environment config, not a feature.
Scope
In
- Create the PostHog project (one project, both clients).
- Project API key available to
web/ and to server/ — same key, separate env vars, since Vite inlines its own at build time and must never read the server's environment.
- Region choice recorded in
DECISIONS.md (EU vs US changes the ingest host and is annoying to move later).
- Confirm the ingest host is set explicitly rather than left to the SDK default, so the two clients cannot end up pointed at different regions.
Out
Notes
The key that ships to web/ is public by design — posthog-js exposes it in the bundle, and that is expected. It is not a secret and does not need secret handling; it just needs to not be confused with a personal API key, which is one and is only needed if #26 later does local flag evaluation.
Acceptance criteria
Context
Both #24 (frontend events) and #25 (server-side events) need a PostHog project to send to. It was originally bundled into #24's scope simply because #24 was the first issue to need it — which made #25 depend on a frontend ticket for something that is not frontend work.
Pulling it out here so either side can proceed independently. This is a signup and some environment config, not a feature.
Scope
In
web/and toserver/— same key, separate env vars, since Vite inlines its own at build time and must never read the server's environment.DECISIONS.md(EU vs US changes the ingest host and is annoying to move later).Out
capture()calls. Frontend events are Product analytics: PostHog on the frontend #24, server events are Product analytics: server-side events #25.Notes
The key that ships to
web/is public by design —posthog-jsexposes it in the bundle, and that is expected. It is not a secret and does not need secret handling; it just needs to not be confused with a personal API key, which is one and is only needed if #26 later does local flag evaluation.Acceptance criteria
DECISIONS.md.web/and fromserver/via separate env vars, documented in.env.example.