Files
o11y/pkg/web/settings.go
c308df9e6d fix(privacy): remove third-party trackers, native support chat (#37)
o11y is a self-hosted observability tool that was phoning home to three
third-party SaaS vendors with our users' data — inherited from the upstream
fork and, worse, enabled by DEFAULT: vite.config.ts gated each on
`env.VITE_X_ENABLED !== 'false'` (opt-OUT), so any operator who never set the
vars shipped all three. index.html injected two vendor <script> tags on every
page load, and AppRoutes HMAC-hashed the logged-in user's email to identify
them to the support-chat vendor.

Removed end to end:

- Go: web.Settings/SettingsConfig drop the three trackers, their configs,
  defaults, and the O11Y_WEB_SETTINGS_* env surface.
- Schema: docs/config/web-settings.json declares only sentry;
  types/generated/webSettings.ts regenerated from it (not hand-edited).
- Frontend: both vendor <script> loaders deleted from index.html; vite
  defines, env types, example.env, window typings and widget styles gone.
  The identify + theme/bubble effects in AppRoutes go with them, along with
  the now-dead crypto-js Hex/HmacSHA256 and useIsDarkMode imports, and a
  useEffect that existed only to compute the widget's feature-flag gates.

Support chat is now Hanzo Chat, native and one way: utils/supportChat.ts
exposes a single openSupportChat(); the three inline widget call sites
(SideNav, Support, LaunchChatSupport) all route through it. It delegates to
the existing openInNewTab helper rather than calling window.open itself —
the repo's own o11y/no-raw-absolute-path rule mandates that helper.

openInNewTab now passes `noopener`. window.open, unlike <a target="_blank">,
does not imply it, so all 57 call sites were leaving the opened tab able to
navigate us via window.opener (reverse tabnabbing). No caller can be affected:
the function returns void.

Sentry stays — it is our own fork (hanzoai/sentry), and it is now opt-IN
(`=== 'true'`) rather than opt-out.

Also drops two pre-existing dead references that the lint gate surfaced in
the touched files: an unused brand-logo import in SideNav and an empty
`declare interface Window {}` in global.d.ts (the real Window augmentation
lives in typings/window.ts — now the only one).

Verified: go test ./pkg/web/... ok (web + routerweb); tsgo --noEmit clean;
oxlint 0 errors; zero vendor references and zero outbound vendor script
loads remain.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-16 12:04:44 -07:00

32 lines
1.0 KiB
Go

package web
// Settings is what the SPA reads at boot.
//
// Hanzo o11y does NOT ship third-party trackers. The upstream fork wired in
// product analytics, onboarding tours and a support-chat widget (all enabled by
// default in the frontend build), which meant a self-hosted observability tool
// phoned home to third parties with our users' data. They are removed: analytics
// is Hanzo Insights, support chat is Hanzo Chat. Do not reintroduce them.
//
// Sentry stays because we run our own fork (hanzoai/sentry) and it is opt-in —
// it only activates when an operator sets a DSN pointing at our instance.
type Settings struct {
Sentry Sentry `json:"sentry" required:"true"`
}
type Sentry struct {
Enabled bool `json:"enabled" required:"true"`
DSN string `json:"dsn"`
Tunnel string `json:"tunnel"`
}
func NewSettings(config Config) Settings {
return Settings{
Sentry: Sentry{
Enabled: config.Settings.Sentry.Enabled,
DSN: config.Settings.Sentry.DSN,
Tunnel: config.Settings.Sentry.Tunnel,
},
}
}