# How OpenWork Implements Analytics Key Management: A Deep Dive into PostHog Integration

> Discover how OpenWork manages PostHog analytics keys. Learn about environment variable overrides smart fallbacks for production development and disabling tracking.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: deep-dive
- Published: 2026-08-16

---

**OpenWork resolves its PostHog analytics key through environment variable overrides with smart fallbacks—production builds use a hard-coded default, development runs silent, and empty strings explicitly disable tracking.**

OpenWork is an open-source desktop application for asynchronous work management that uses **PostHog** for privacy-conscious, self-hosted product analytics. This article examines how the codebase handles analytics key security, environment-based configuration, and user-controlled opt-outs according to the different-ai/openwork repository.

## Analytics Key Resolution Strategy

The core logic lives in **[`analytics-key.ts`](https://github.com/different-ai/openwork/blob/main/analytics-key.ts)**, where the `resolvePosthogKey` function implements a three-tier resolution strategy:

```ts
// apps/app/src/app/lib/analytics-key.ts
export const DEFAULT_POSTHOG_KEY = "phc_4YnPTlDVYPjgwKvLuNxhbHjV5kadgvd7XLzVHWnCXAI";

export function resolvePosthogKey(raw: unknown, isDev: boolean): string {
  if (typeof raw === "string") return raw.trim(); // explicit value wins; "" disables
  return isDev ? "" : DEFAULT_POSTHOG_KEY;
}

```

The resolution order is strict:

- **Environment override wins**: If `VITE_OPENWORK_POSTHOG_KEY` is set, its trimmed value is used unconditionally—even an empty string
- **Production fallback**: In production builds (`import.meta.env.DEV === false`), the hard-coded `DEFAULT_POSTHOG_KEY` is used
- **Development silence**: In development, the key remains empty, disabling analytics by default

This design prevents accidental tracking during local development while ensuring production deployments never fail silently due to missing configuration.

## Configuration Integration in the Analytics Module

The resolved key feeds into **[`analytics.ts`](https://github.com/different-ai/openwork/blob/main/analytics.ts)**, which also handles host configuration:

```ts
// apps/app/src/app/lib/analytics.ts
const POSTHOG_KEY = resolvePosthogKey(
  import.meta.env.VITE_OPENWORK_POSTHOG_KEY,
  import.meta.env.DEV
);
const POSTHOG_HOST = (ENV_POSTHOG_HOST || DEFAULT_POSTHOG_HOST).replace(/\/+$/, "");

```

**Key characteristics:**

| Aspect | Implementation |
|--------|---------------|
| **Host override** | `VITE_OPENWORK_POSTHOG_HOST` env variable |
| **Default host** | `https://us.i.posthog.com` |
| **URL normalization** | Trailing slashes stripped with regex |

## User-Controlled Enable/Disable Mechanism

Analytics respect user autonomy through a stored preference. The `isAnalyticsEnabled()` helper reads from `localStorage` via the settings system:

```ts
import { isAnalyticsEnabled } from '@/app/lib/analytics';

if (isAnalyticsEnabled()) {
  captureAnalyticsEvent('feature_used', { feature: 'quick-search' });
}

```

The preference defaults to **enabled** when undefined or malformed—analytics remain active unless the user explicitly opts out through Settings → Preferences.

## Key Security and Deployment Considerations

### Production Key Protection

The `DEFAULT_POSTHOG_KEY` is embedded directly in source code rather than fetched remotely. This is intentional: PostHog project keys are **write-only public tokens** by design. They cannot read data or perform destructive operations, so client-side exposure carries minimal risk.

### Empty String as Kill Switch

Setting `VITE_OPENWORK_POSTHOG_KEY=''` intentionally disables analytics in **any** environment. This provides an emergency override for air-gapped deployments or compliance requirements.

## Testing Key Resolution Logic

The behavior is validated in **[`analytics-key.test.ts`](https://github.com/different-ai/openwork/blob/main/analytics-key.test.ts)** ([^12^]). Unit tests confirm:

- Explicit strings override all defaults
- Empty strings disable regardless of environment
- Production builds without overrides receive `DEFAULT_POSTHOG_KEY`
- Development builds without overrides receive empty string

## Distinct ID and Identity Management

Even with a valid key, analytics require stable user identification. The `getAnalyticsDistinctId()` function in **[`analytics.ts`](https://github.com/different-ai/openwork/blob/main/analytics.ts)** lazily generates a UUID:

```ts
import { getAnalyticsDistinctId, identify } from '@/app/lib/analytics';

const anonymousId = getAnalyticsDistinctId(); // Creates and stores UUID if missing

// After sign-in, link to real user without losing history
identify(denUser.id); // Emits $identify event preserving DAU continuity

```

This preserves accurate **daily active user** metrics when anonymous users later authenticate, without ever transmitting personal data.

## Event Pipeline Architecture

With key management resolved, the analytics system operates fire-and-forget:

1. **Queue**: Events buffered in memory
2. **Batch**: Flushed every 10 seconds or 50 items
3. **Endpoint**: `POST` to `${POSTHOG_HOST}/batch/`
4. **Failure handling**: Network errors swallowed silently—UI never blocks

```ts
import { captureAnalyticsEvent, flushAnalytics } from '@/app/lib/analytics';

// Automatic batching
captureAnalyticsEvent('document_created', { template: 'brief' });

// Manual flush for critical paths (rarely needed)
await flushAnalytics();

```

## Summary

- **`resolvePosthogKey`** in [`analytics-key.ts`](https://github.com/different-ai/openwork/blob/main/analytics-key.ts) implements environment-aware key resolution with explicit override support
- **Production deployments** use embedded `DEFAULT_POSTHOG_KEY` unless `VITE_OPENWORK_POSTHOG_KEY` overrides
- **Development builds** disable analytics by default (empty key) to prevent noise
- **User preferences** via `analyticsEnabled` provide runtime opt-out independent of key configuration
- **PostHog keys are public by design**—embedding them in client code is safe and standard practice
- **Identity bridging** through `identify()` maintains metric continuity across anonymous-to-authenticated transitions

## Frequently Asked Questions

### How do I disable analytics completely in OpenWork?

Set `VITE_OPENWORK_POSTHOG_KEY=''` (empty string) in your environment variables. This overrides all defaults and disables tracking in both development and production builds. Alternatively, users can toggle off the `analyticsEnabled` preference in Settings → Preferences at runtime.

### Is it safe that the PostHog key is visible in the source code?

Yes. PostHog project keys are **public write-only tokens** designed for client-side use. They can only submit events, not query data or modify configuration. The security model relies on project-level access controls in your PostHog instance, not key secrecy.

### How does OpenWork handle analytics during local development?

By default, development builds receive an empty key from `resolvePosthogKey`, making analytics silent. Developers can override this by setting `VITE_OPENWORK_POSTHOG_KEY` to a test project key when validating instrumentation locally.

### What happens to queued events if the user disables analytics mid-session?

Events already queued remain in memory until the next flush attempt, but subsequent `captureAnalyticsEvent` calls return immediately without buffering. The `isAnalyticsEnabled()` check gates both capture and flush operations, ensuring no new data transmits after opt-out.