Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"expo-status-bar": "~57.0.1",
"expo-symbols": "~57.0.2",
"expo-web-browser": "~57.0.2",
"lucide-react-native": "^1.35.0",
"lucide-react-native": "^1.37.0",
"react": "19.2.3",
"react-native": "0.86.2",
"react-native-appwrite": "^0.34.0",
Expand Down
34 changes: 6 additions & 28 deletions apps/web/.env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ APPWRITE_MODERATOR_TEAM_ID=
# OPTIONAL: Server Configuration
# ==============================================================================
# Public URL of your application (for OAuth redirects, etc.)
#
# NOTE: Password reset and email verification links are built from this value,
# and Appwrite only sends them to hosts registered as "Platforms" for this
# project. If you develop on http://127.0.0.1:3000 but Appwrite knows
# http://localhost:3000, those links 400 and the flow silently breaks.
# Register EVERY host you use (localhost, 127.0.0.1, LAN IP, dev domain).
SERVER_URL=http://localhost:3000

# Instance name displayed in mobile app and about pages
Expand Down Expand Up @@ -182,34 +188,6 @@ TENOR_API_KEY=
TENOR_CLIENT_KEY=firepit-web
TENOR_LOCALE=en_US

# ==============================================================================
# OPTIONAL: New Relic APM (Application Performance Monitoring)
# ==============================================================================
# Configure New Relic for application monitoring and log ingestion.
# These are server-side only (no NEXT_PUBLIC prefix needed).
# Get these from your New Relic account: https://newrelic.com

# Your New Relic license key
# Find in: New Relic > Account Settings > API Keys > License Keys
NEW_RELIC_LICENSE_KEY=

# Application name as it appears in New Relic
# Example: firepit-production, firepit-staging, etc.
NEW_RELIC_APP_NAME=

# ==============================================================================
# OPTIONAL: Telemetry Provider Routing
# ==============================================================================
# Controls where server-side telemetry from src/lib/newrelic-utils.ts is sent.
# Allowed values: newrelic, posthog, both, none
# Default: newrelic
TELEMETRY_PROVIDER=newrelic

# Controls where client-side telemetry from src/lib/client-telemetry.ts is sent.
# Allowed values: newrelic, posthog, both, none
# Default: newrelic
NEXT_PUBLIC_TELEMETRY_PROVIDER=newrelic

# Enable PostHog forwarding during tests (normally disabled)
# ENABLE_POSTHOG_IN_TESTS=false

Expand Down
27 changes: 27 additions & 0 deletions apps/web/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.1.0] - 2026-08-29

### ✨ Features

- **Password reset** - Request a reset link from the sign-in page and set a new password from a secure email link
- **Change email** - Update your account email from Settings (with current-password confirmation and re-verification when enabled)
- **Session management** - View all signed-in devices in Settings and revoke any of them (or sign out everywhere except the current device)
- **Remember me** - Optional persistent sign-in; uncheck to use a session-only cookie that clears when the browser closes
- **Signup control** - Admins can set the instance policy to open, individual approval, or no signups, and approve/reject pending signups from the admin panel
- **Deactivate & delete account** - Temporarily deactivate your account (auto-reactivates on next sign-in) or permanently delete it from a new Danger Zone section
- **Deleted User tombstones** - Deleted accounts show as "Deleted User" and their user ID is permanently reserved so it can never be reused

### ⚙️ Improvements

- Sign-in now refuses accounts awaiting approval and reactivates deactivated accounts automatically on success
- Admin panel exposes the current signup policy and a pending-approvals queue
- **Telemetry simplified to PostHog only** - Removed all New Relic plumbing, dependencies, and config; server and client telemetry now route exclusively to PostHog (smaller installs, less startup overhead)
- Added `z.compile()` to one usage of zod in codebase to improve performance
- Updated `bun test` wiring to improve passing tests to 818 up from 717 previously. (Part of bun dep bump)
- **Account safety polish** - Deleting your account now opens a confirmation dialog with a full consequences summary and password confirmation; a persistent warning (with one-click resend) reminds you to confirm a new email before signing out; the remember-me checkbox preference and password-reset resend (with a 30s cooldown) are new conveniences on the sign-in page

### 🐛 Fixes

- **Session devices now identified** - The session list showed "Unknown device" and "signed in by unknown" because it read nested fields the Appwrite API doesn't return. It now maps the real device, browser, OS, and sign-in date
- Fix TDZ in `setup-appwrite.ts` (`[error] Cannot access 'now' before initalization`).
- Bumped zod to `4.5.4`, and bumped bun to `1.4.0` for performance improvements

## [2.0.3] - 2026-08-15

### 🐛 Fixes
Expand Down
2 changes: 1 addition & 1 deletion apps/web/bunfig.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ linker = "isolated"

[test]
root="src/__tests__/"
preload="./src/__tests__/setup.ts"
preload=["./src/__tests__/setup.ts", "./happydom.ts"]
pathIgnorePatterns="src/__tests__/__helpers__/**"
timeout=10000
90 changes: 26 additions & 64 deletions apps/web/docs/TELEMETRY.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,11 @@
# Telemetry Providers
# Telemetry

Firepit supports telemetry routing to New Relic, PostHog, both, or neither.
Firepit sends all telemetry to PostHog.

This is implemented in:
- Server helpers: `src/lib/posthog-utils.ts`
- Client helpers: `src/lib/client-logger.ts`

- Server telemetry helpers: `src/lib/newrelic-utils.ts`
- Client telemetry helpers: `src/lib/client-telemetry.ts`

## Provider Configuration

### Server-side provider

- Env var: `TELEMETRY_PROVIDER`
- Allowed values: `newrelic`, `posthog`, `both`, `none`
- Default: `newrelic`

### Client-side provider

- Env var: `NEXT_PUBLIC_TELEMETRY_PROVIDER`
- Allowed values: `newrelic`, `posthog`, `both`, `none`
- Default: `newrelic`

## Credentials

### New Relic

- `NEW_RELIC_LICENSE_KEY`
- `NEW_RELIC_APP_NAME`

### PostHog
## Server-side capture

Server-side capture (`posthog-node`) prefers:

Expand All @@ -40,11 +17,18 @@ Fallback compatibility keys:
- `NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN`
- `NEXT_PUBLIC_POSTHOG_HOST`

Client-side capture (`posthog-js`) uses existing browser initialization in `instrumentation-client.ts`.
Structured logs are also forwarded through the OTLP log pipeline to the
PostHog logs endpoint (`POSTHOG_LOGS_HOST`, defaults to the ingest host).

## PostHog Privacy And Bandwidth Controls
Process-level hooks (uncaught exceptions, unhandled rejections, flush on
shutdown) are registered in `instrumentation.ts`.

Client-side PostHog behavior is configured by these env vars:
## Client-side capture

Client capture (`posthog-js`) uses existing browser initialization in
`instrumentation-client.ts`.

## PostHog Privacy And Bandwidth Controls

- `NEXT_PUBLIC_POSTHOG_AUTOCAPTURE` (recommended `false`)
- `NEXT_PUBLIC_POSTHOG_SESSION_RECORDING` (recommended `false`)
Expand Down Expand Up @@ -98,45 +82,23 @@ If you use Next.js rewrites as a PostHog proxy path, configure:
If you set `NEXT_PUBLIC_POSTHOG_HOST` directly to your own reverse-proxy domain,
you can disable rewrites with `POSTHOG_REWRITE_ENABLED=false`.

## Telemetry Matrix
## Server Events

### Server helper mapping
Events emitted by `src/lib/posthog-utils.ts`:

| Helper | New Relic output | PostHog output |
| ------------------------------------ | ------------------------------------------ | ---------------------------------------------------------------------------------- |
| `recordEvent(eventType, attributes)` | `recordCustomEvent(eventType, attributes)` | capture event `eventType` with `attributes` |
| `recordMetric(name, value)` | `recordMetric(name, value)` | capture event `metric_recorded` with `{ metricName: name, value }` |
| `incrementMetric(name, value)` | `incrementMetric(name, value)` | capture event `metric_incremented` with `{ metricName: name, incrementBy: value }` |
| `recordError(error, attrs)` | `noticeError(error, attrs)` | capture event `error_recorded` with normalized error fields plus attrs |
| `logger.info/warn/error/debug` | `ApplicationLog` custom event | capture event `application_log` |
| Helper | PostHog output |
| ------------------------------------ | ---------------------------------------------------------------------------------- |
| `recordEvent(eventType, attributes)` | capture event `eventType` with `attributes` |
| `recordMetric(name, value)` | capture event `metric_recorded` with `{ metricName: name, value }` |
| `recordError(error, attrs)` | `captureException` with normalized error fields plus attrs |
| `logger.info/warn/error/debug` | capture event `application_log` plus OTLP log record |

### Client helper mapping

| Helper | New Relic output | PostHog output |
| ---------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------- |
| `recordClientAction(action, attributes)` | `addPageAction(action, attributes)` | `capture(action, attributes)` |
| `recordClientError(error, attributes)` | `noticeError(error, attributes)` | `captureException(error, attributes)` or fallback `capture("client_error", ...)` |
Client helpers emit `log_info`, `log_warn`, `log_error`, `client_error` events
and use `captureException` for rich client errors.

## Digest Telemetry Example

Digest generation in `src/lib/inbox.ts` emits:

- Metric: `Custom/InboxDigest/DurationMs`
- Metric: `Custom/InboxDigest/ReturnedItems`
- Metric: `Custom/InboxDigest/TotalUnread`
- Event: `InboxDigestGenerated`

Under provider routing:

- `newrelic`: only New Relic receives these
- `posthog`: only PostHog receives mapped equivalents
- `both`: both providers receive telemetry
- `none`: no provider receives telemetry

## Client Parity Coverage

The following client paths now route through provider-aware helpers instead of direct New Relic-only calls:

- `src/lib/client-logger.ts`
- `src/app/error.tsx`
- `src/app/global-error.tsx`
- Metrics: `Custom/InboxDigest/DurationMs`, `Custom/InboxDigest/ReturnedItems`, `Custom/InboxDigest/TotalUnread`
3 changes: 3 additions & 0 deletions apps/web/happydom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { GlobalRegistrator } from "@happy-dom/global-registrator";

GlobalRegistrator.register();
97 changes: 18 additions & 79 deletions apps/web/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,91 +2,30 @@
* Next.js Instrumentation Hook
*
* This file is automatically loaded by Next.js on both server and edge runtimes.
* It initializes server telemetry hooks for Node.js runtime only.
* It registers PostHog telemetry hooks for the Node.js runtime only.
*
* Documentation: https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation
*/

const instrumentationLogger = {
error(message: string, attributes?: Record<string, unknown>) {
const payload = attributes
? `${message} ${JSON.stringify(attributes)}`
: `${message}`;
const error = new Error(payload);

if (typeof globalThis.reportError === "function") {
globalThis.reportError(error);
return;
}

console.error(error);
},
};

export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
const newrelicLicenseKey = process.env.NEW_RELIC_LICENSE_KEY;
const newrelicAppName = process.env.NEW_RELIC_APP_NAME;

try {
const {
initNewRelic,
registerPostHogLoggerProvider,
registerPostHogProcessHandlers,
} = await import("./src/lib/newrelic-utils");
registerPostHogLoggerProvider();
registerPostHogProcessHandlers();

// Kick off New Relic initialization once at startup so sync
// dispatch paths can use the agent without async triggers.
if (newrelicLicenseKey && newrelicAppName) {
await initNewRelic();
}
} catch (error) {
// PostHog runtime hooks are optional and should not block startup.
instrumentationLogger.error(
"[PostHog] Failed to register process handlers",
{
error:
error instanceof Error
? {
message: error.message,
name: error.name,
stack: error.stack,
}
: String(error),
},
);
}

// Only initialize if both license key and app name are provided
if (newrelicLicenseKey && newrelicAppName) {
try {
// Dynamic import to avoid loading New Relic on Edge runtime
// New Relic will automatically load the newrelic.cjs config file
const newrelic = await import("newrelic");
if (process.env.NEXT_RUNTIME !== "nodejs") {
return;
}

// Return the newrelic instance for potential use
return newrelic;
} catch (error) {
// If New Relic fails to initialize, log the error but don't crash the app
console.error(
"[New Relic] Failed to initialize:",
error instanceof Error ? error.message : String(error),
);
}
try {
const {
registerPostHogLoggerProvider,
registerPostHogProcessHandlers,
} = await import("./src/lib/posthog-utils");
registerPostHogLoggerProvider();
registerPostHogProcessHandlers();
} catch (error) {
// PostHog runtime hooks are optional and should not block startup.
const payload = error instanceof Error ? `${error.message} ${error.stack ?? ""}` : String(error);
if (typeof globalThis.reportError === "function") {
globalThis.reportError(new Error(payload));
} else {
// If credentials are missing, log a warning but don't fail
if (!newrelicLicenseKey) {
console.warn(
"[New Relic] NEW_RELIC_LICENSE_KEY not found - APM monitoring disabled",
);
}
if (!newrelicAppName) {
console.warn(
"[New Relic] NEW_RELIC_APP_NAME not found - APM monitoring disabled",
);
}
console.error(payload);
}
}
}
}
1 change: 0 additions & 1 deletion apps/web/knip.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"$schema": "https://unpkg.com/knip@6/schema.json",
"entry": [
"newrelic.cjs",
"public/sw.js",
"scripts/test-server-auth.ts"
],
Expand Down
Loading
Loading