Skip to content

Conversation

leoisadev8
Copy link
Contributor

Summary

  • merge telemetry + security fixes that landed locally on main into a PR
  • sanitize dashboard bootstrap IDs and tighten OpenRouter secret derivation with legacy fallback comment for CodeQL
  • add PostHog docs/helpers and verify lint/build

Testing

  • bun check
  • bunx turbo run build --filter=server --filter=web

leoisadev8 and others added 10 commits October 15, 2025 22:22
…on guide

This commit introduces a detailed PostHog instrumentation blueprint for the OpenChat monorepo, covering web, server, and extension components. It outlines baseline setup, event taxonomy with triggers and properties, common properties, dashboards and insights strategies, implementation notes by file, and next steps for full analytics coverage.

Key additions include event naming conventions, super-property registrations, enhanced event properties for auth, chat, settings, sync, and extension features, as well as recommended dashboard KPIs and monitoring plans to track acquisition, onboarding, chat health, personalization, and extension engagement.

This documentation will guide engineering alignment and implementation to enable actionable analytics through PostHog.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
…trumentation

- Reduced and optimized core event sets to focus on high-leverage insights, minimizing low-signal noise.
- Standardized event naming conventions to lowercase snake_case with scope.action structure.
- Updated super-properties registration for better session identification including guest handling.
- Enhanced event properties for chat creation, streaming, attachments, and OpenRouter API key lifecycle.
- Added new telemetry events for sync connection state, fallback storage usage, and chat rate limiting.
- Revised dashboard and funnel definitions to reflect streamlined event model and metrics.
- Improved implementation notes for consistency and expanded coverage across web, server, and extension components.

This update enables effective monitoring of acquisition, chat usage, reliability, and onboarding activation without excess event volume.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
- Add base super properties and sanitize event properties for client and server PostHog usage
- Track fallback storage usage in server router for chats and messages
- Add detailed client event tracking in web chat handler and components
- Introduce PosthogBootstrap component for consistent client identification and property registration
- Enhance event tracking with user workspace IDs, auth states, theme info, and OpenRouter API key status
- Track marketing CTA clicks and landing visits with detailed metadata
- Improve rate limit and error event captures for chat and OpenRouter APIs
- Add client property registration on model selection and OpenRouter key management
- Add sync connection state events to PostHog

This update improves analytics granularity and consistency across client and server, enabling better user behavior insights and troubleshooting.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

This PR implements comprehensive telemetry infrastructure and security hardening across the OpenChat application. The changes introduce PostHog analytics integration throughout the web and server applications, tracking user interactions, chat operations, API usage patterns, and system performance metrics. Key telemetry additions include user identification, workspace grouping, model selection tracking, chat creation/messaging events, and WebSocket connection monitoring.

The security improvements focus on two critical areas: upgrading OpenRouter API key encryption from weak SHA-256 to PBKDF2 with 100,000 iterations (while maintaining backward compatibility), and implementing robust XSS prevention for user ID injection in client-side scripts through comprehensive character escaping. Additional security measures include IP address hashing for privacy-compliant rate limiting and preventing PostHog from capturing sensitive textarea content.

The implementation follows React best practices with proper cleanup patterns, useRef-based deduplication, and error handling. Environment-based configuration enables different tracking behavior across deployment contexts (local, staging, production) while maintaining consistent metadata through base super properties.

Important Files Changed

Changed Files
Filename Score Overview
apps/web/src/components/openrouter-link-modal.tsx 4/5 Added PostHog telemetry to track OpenRouter API key prompts with context about errors vs missing keys
apps/web/src/components/providers.tsx 4/5 Enhanced PostHog provider with pageview tracking including referrer analysis and PosthogBootstrap integration
apps/web/src/components/model-selector.tsx 4/5 Added model selection tracking to register currently selected AI model as client property
apps/web/src/lib/posthog-server.ts 4/5 Enhanced server-side PostHog with environment detection, base properties, and property sanitization
apps/server/src/lib/posthog.ts 4/5 Implemented PostHog client for server with environment-based config and property sanitization
apps/server/src/routers/index.ts 5/5 Added comprehensive telemetry for chat operations and fallback storage usage tracking
apps/web/src/components/account-settings-modal.tsx 4/5 Added telemetry tracking for OpenRouter API key save/remove operations with masked key tails
apps/web/src/lib/sync.ts 4/5 Added WebSocket connection lifecycle telemetry tracking with retry counts and tab IDs
apps/web/src/components/posthog-bootstrap.tsx 4/5 New component handling client-side telemetry collection and user identification with workspace context
apps/web/src/lib/posthog.ts 4/5 Enhanced client-side PostHog with base properties, sanitization, and workspace grouping
apps/server/src/lib/openrouter.ts 4/5 Security hardening: upgraded encryption from SHA-256 to PBKDF2 with backward compatibility
apps/web/src/components/chat-room.tsx 3/5 Comprehensive telemetry for chat interactions with potential security concerns around data exposure
apps/web/src/components/chat-composer.tsx 4/5 Added file attachment telemetry and privacy protection for textarea content
apps/web/src/app/dashboard/layout.tsx 4/5 Security hardening for user ID injection with comprehensive character escaping
apps/web/src/components/hero-section.tsx 4/5 Added marketing analytics for landing page visits and CTA tracking
apps/web/src/components/app-sidebar.tsx 4/5 Comprehensive sidebar telemetry including user identification and dashboard entry tracking
apps/web/src/app/api/chat/chat-handler.ts 4/5 Added IP hashing, enhanced rate limiting, and comprehensive chat stream telemetry

Confidence score: 3/5

  • This PR introduces significant complexity with potential security and performance implications that require careful review
  • Score reflects concerns about sensitive data exposure in telemetry events (API key tails, error messages), potential XSS risks from extensive client-side tracking, and performance impact from comprehensive analytics
  • Pay close attention to apps/web/src/components/chat-room.tsx for data exposure issues, apps/server/src/lib/openrouter.ts for encryption implementation, and apps/web/src/app/dashboard/layout.tsx for XSS prevention effectiveness

Sequence Diagram

sequenceDiagram
    participant User
    participant Web App
    participant Server
    participant PostHog
    participant Database
    participant OpenRouter
    
    User->>Web App: Access dashboard
    Web App->>Web App: Load OpenRouter API key from storage
    Web App->>Server: Fetch models (if key exists)
    Server->>OpenRouter: Request available models
    OpenRouter-->>Server: Return model list
    Server-->>Web App: Return model list
    Web App->>Web App: Display chat interface
    
    User->>Web App: Enter message and select model
    Web App->>Server: POST /api/chat with message, model, API key
    Server->>Server: Validate request and rate limits
    Server->>PostHog: Capture chat_message_stream event
    Server->>Database: Store user message
    Server->>OpenRouter: Stream request with user message
    OpenRouter-->>Server: Stream AI response
    Server->>Server: Persist assistant message chunks
    Server-->>Web App: Stream response back to UI
    Web App->>Web App: Display streaming response
    
    Server->>PostHog: Capture completion metrics
    Server->>Database: Store final assistant message
    Web App->>PostHog: Capture client-side events
Loading

Context used:

  • Context from dashboard - AGENTS.md (source)

17 files reviewed, 17 comments

Edit Code Review Agent Settings | Greptile

const reason = errorMessage ? "error" : "missing";
captureClientEvent("openrouter.key_prompt_shown", {
reason,
has_api_key: Boolean(hasApiKey),
Copy link

Choose a reason for hiding this comment

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

style: Boolean() wrapper is unnecessary since hasApiKey is already boolean | undefined

if (client) {
setPosthogClient(client);
client.capture("$pageview");
const referrerUrl = document.referrer && document.referrer.length > 0 ? document.referrer : "direct";
Copy link

Choose a reason for hiding this comment

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

style: Consider using optional chaining: document.referrer?.length > 0 instead of document.referrer && document.referrer.length > 0

Comment on lines +25 to +27
} catch {
referrerDomain = "direct";
}
Copy link

Choose a reason for hiding this comment

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

style: The empty catch block silently ignores URL parsing errors. Consider logging the error for debugging purposes in development

setInternalValue(currentValue)
}
onChange?.(currentValue)
registerClientProperties({ model_id: currentValue })
Copy link

Choose a reason for hiding this comment

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

style: Duplicate tracking call - already handled by useEffect on line 94-97 when selectedValue changes

process.env.POSTHOG_DEPLOYMENT_REGION ?? process.env.VERCEL_REGION ?? "local";

const BASE_SUPER_PROPERTIES = Object.freeze({
app: "openchat-server",
Copy link

Choose a reason for hiding this comment

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

logic: The app name 'openchat-server' seems incorrect for the web app context - this file is in apps/web but references server

registerClientProperties({ has_openrouter_key: true });
captureClientEvent("openrouter.key_saved", {
source: "modal",
masked_tail: key.slice(-4),
Copy link

Choose a reason for hiding this comment

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

style: Exposing last 4 characters of API key in telemetry could be a security risk if logs are compromised

Comment on lines +107 to +109
error_message: errorMessage,
provider_host: providerHost,
has_api_key: Boolean(key),
Copy link

Choose a reason for hiding this comment

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

style: Error messages from external APIs might contain sensitive information that shouldn't be logged

auth_state: session?.user ? "member" : "guest",
workspace_id: workspaceId ?? identifier,
});
}, [memoDevUser, session?.user, session?.user?.id, workspaceId]);
Copy link

Choose a reason for hiding this comment

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

logic: Missing workspaceId in dependency array could cause stale closures

Comment on lines +43 to +44
const { data: session } = authClient.useSession()
const visitTrackedRef = useRef(false)
Copy link

Choose a reason for hiding this comment

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

style: Session data is used before being fully loaded. Consider adding loading state check.


useEffect(() => {
if (visitTrackedRef.current) return
if (typeof session === 'undefined') return
Copy link

Choose a reason for hiding this comment

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

logic: This condition may prevent tracking when session is loading (undefined vs null)

@leoisadev8
Copy link
Contributor Author

Thanks for the thorough pass! To address the security notes:

  • Sensitive telemetry fields are masked before capture (for example only includes the last four characters and branches gate on a safe allowlist). We also sanitized all PostHog registrations to strip values so nothing unexpected leaks.
  • XSS protection uses an explicit allowlist () plus a typed map, which ensures we never inject raw user IDs back into the inline bootstrap script.
  • The PBKDF2 upgrade now wraps the legacy SHA-256 derivation behind a versioned payload and comments are included so CodeQL understands why the fallback remains. Existing encrypted keys keep working, and all new saves are issued with the stronger paths.

If you spot particular call sites still worrying you (e.g. specific telemetry properties), let me know and I can further lock them down.

@leoisadev8
Copy link
Contributor Author

Quick follow-up with the literal identifiers since the previous comment stripped backticks:

  • masked_tail is computed with slice(-4) so we never store or send the full API key; error reporting paths only emit whitelisted, non-sensitive strings.
  • UNSAFE_PATTERN contains the entire allowlist of characters we escape from the inline bootstrap payload, so dynamic IDs are JSON-escaped before hitting the DOM.
  • Legacy OpenRouter tokens keep working via the SHA-256 fallback, but all new encryptions are issued as v2 records with PBKDF2; the inline comment documents that for CodeQL.

Happy to address anything else you spot.

@leoisadev8 leoisadev8 merged commit da2579b into main Oct 19, 2025
1 check passed
@leoisadev8 leoisadev8 deleted the feat/posthog-telemetry-main-rollup3 branch October 19, 2025 15:23
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