Skip to content
Merged
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
10 changes: 5 additions & 5 deletions ENVIRONMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This document lists all environment variables used in the Kilo Code cloud monore
- `PATH` - System executable search path; modified by tooling (OpenClaw, tsx, etc.) to locate CLIs. [SYSTEM]
- `TMUX` - Set when running inside a tmux session; used by `dev/local/tmux.ts` to detect tmux environment. [SYSTEM]
- `GITHUB_ACTIONS` - Set to `true` by GitHub Actions CI; detected by tooling (Rye log groups, Playwright, Vitest) to enable GitHub Actions-specific output/reporting. [SERVER]
- `NEXT_RUNTIME` - Set by Next.js to `'node'`, `'edge'`, or `'browser'`; used in `apps/web/src/instrumentation.ts` to select appropriate Sentry instrumentation. [SERVER]
- `NEXT_RUNTIME` - Set by Next.js to `'node'`, `'edge'`, or `'browser'`; used in `apps/web/src/instrumentation.ts` and `apps/ai-gateway/src/instrumentation.ts` to select appropriate Sentry instrumentation. [SERVER]
- `DOTENV_CONFIG_QUIET` - Set by dotenv to suppress load output; set to `'true'` in `dev/seed/lib/preflight.ts:9` during seeding. [SERVER]

## App (apps/web)
Expand Down Expand Up @@ -47,10 +47,10 @@ Manage shared web env var additions and rotations with `pnpm web:env set <VARIAB
- `SUBSTACK_PUBLICATION_URL` - Substack publication origin used by user-deletion subscriber cleanup; defaults to `https://blog.kilo.ai`. Must be `blog.kilo.ai` or a `*.substack.com` host. The Substack admin search URL is hardcoded to `https://kilocode.substack.com/publish/subscribers`, not this publication. [SERVER]
- `CSA_APP_BASE_URL` - CSA origin used by the Cloud deletion worker to call `POST /api/internal/cloud/users/gdpr-scrub`. Example: the production CSA app URL. [SERVER]
- `CSA_VERCEL_PROTECTION_BYPASS` - CSA Vercel Deployment Protection automation bypass. Cloud sends it as the `x-vercel-protection-bypass` header on Cloud → CSA `POST /api/internal/cloud/users/gdpr-scrub`, never as a query parameter. Required when CSA has Vercel Authentication enabled; without it Vercel returns 401 before the CSA route. Distinct from `SUPPORT_API_SECRET`. `[SECRET]`
- `SENTRY_ORG` - Sentry organization slug for source map uploads; used in `apps/web/next.config.mjs`. `[SECRET]`
- `SENTRY_PROJECT` - Sentry project slug for source map uploads; used in `apps/web/next.config.mjs`. `[SECRET]`
- `SENTRY_AUTH_TOKEN` - Sentry auth token for source map uploads; used in `apps/web/next.config.mjs`. `[SECRET]`
- `NEXT_PUBLIC_SENTRY_DSN` - Sentry DSN for server and Edge runtime error reporting; used in `apps/web/sentry.edge.config.ts` and `apps/web/sentry.server.config.ts`. `[PUBLIC]`
- `SENTRY_ORG` - Sentry organization slug for source map uploads; used in `apps/web/next.config.mjs` and `apps/ai-gateway/next.config.mjs`. `[SECRET]`
- `SENTRY_PROJECT` - Sentry project slug for source map uploads; used in `apps/web/next.config.mjs` and `apps/ai-gateway/next.config.mjs`. `[SECRET]`
- `SENTRY_AUTH_TOKEN` - Sentry auth token for source map uploads; used in `apps/web/next.config.mjs` and `apps/ai-gateway/next.config.mjs`. `[SECRET]`
- `NEXT_PUBLIC_SENTRY_DSN` - Sentry DSN for server and Edge runtime error reporting; used in `apps/web/sentry.edge.config.ts` and `packages/web-shared/src/lib/observability/sentry-server.ts`. `[PUBLIC]`

### Marketing Tags

Expand Down
17 changes: 16 additions & 1 deletion apps/ai-gateway/next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { withSentryConfig } from '@sentry/nextjs';
import { resolve } from 'path';

const monorepoRoot = resolve(import.meta.dirname, '../..');
Expand All @@ -17,4 +18,18 @@ const nextConfig = {
skipTrailingSlashRedirect: true,
};

export default nextConfig;
/** @type {import('@sentry/nextjs').SentryBuildOptions} */
const sentryConfig = {
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
silent: !process.env.CI,
bundleSizeOptimizations: {
excludeDebugStatements: true,
},
telemetry: false,
};

export default process.env.NODE_ENV === 'development'
? nextConfig
: withSentryConfig(nextConfig, sentryConfig);
1 change: 1 addition & 0 deletions apps/ai-gateway/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"dependencies": {
"@kilocode/web-shared": "workspace:*",
"@opentelemetry/api": "1.9.1",
"@sentry/nextjs": "10.69.0",
"next": "16.3.6",
"react": "19.2.6",
"react-dom": "19.2.6"
Expand Down
11 changes: 11 additions & 0 deletions apps/ai-gateway/src/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { captureRequestError } from '@sentry/nextjs';

export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { registerNodeInstrumentation } =
await import('@/lib/observability/node-instrumentation');
registerNodeInstrumentation('kilocode-ai-gateway');
}
}

export const onRequestError = captureRequestError;
42 changes: 3 additions & 39 deletions apps/web/src/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,9 @@ import { captureRequestError } from '@sentry/nextjs';

export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('../sentry.server.config');

const { getClient, SentryContextManager, validateOpenTelemetrySetup } =
await import('@sentry/nextjs');
const { SentryPropagator, SentrySampler, SentrySpanProcessor } =
await import('@sentry/opentelemetry');

const sentryClient = getClient();
if (!sentryClient) {
console.warn('Sentry client not found in instrumentation.ts');
}

// Use Vercel's OpenTelemetry SDK (Node runtime) but supply the Sentry-required components
// (sampler/propagator/context manager/span processor) ourselves.
//
// Notes on runtime split:
// - `@vercel/otel` ships distinct entrypoints for Edge vs Node via conditional exports
// (edge -> dist/edge, node -> dist/node):
// https://github.com/vercel/otel/blob/v2.1.0/package.json#L14-L42
// - The Node build wires Node-specific instrumentations such as node:http / node:https
// and async context managers (async_hooks), which are not available in Edge runtime.
// See bundled code in dist/node/index.js (search for "async_hooks" and "node:http").
// - While `@vercel/otel` technically supports Edge runtime via its dist/edge build,
// it provides only lightweight trace propagation, not full auto-instrumentation.
// See: https://www.npmjs.com/package/@vercel/otel (README describes Edge as "lightweight mode")
//
// We intentionally only enable the Sentry+OTel custom wiring in Node runtime for now.
// Edge tracing should rely on Sentry's Edge SDK behavior configured in sentry.edge.config.ts.
// ref: https://docs.sentry.io/platforms/javascript/guides/nextjs/opentelemetry/custom-setup/
const { registerOTel } = await import('@vercel/otel');
registerOTel({
serviceName: 'kilocode-app',
traceSampler: sentryClient ? new SentrySampler(sentryClient) : undefined,
spanProcessors: ['auto', new SentrySpanProcessor()],
propagators: ['auto', new SentryPropagator()],
contextManager: new SentryContextManager(),
});

validateOpenTelemetrySetup();
const { registerNodeInstrumentation } =
await import('@/lib/observability/node-instrumentation');
registerNodeInstrumentation('kilocode-app');
}

if (process.env.NEXT_RUNTIME === 'edge') {
Expand Down
1 change: 0 additions & 1 deletion apps/web/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
"jest.config.ts",
"knip.ts",
"sentry.edge.config.ts",
"sentry.server.config.ts",
".next/dev/types/**/*.ts",
"dev/discord-gateway-cron.ts"
],
Expand Down
7 changes: 6 additions & 1 deletion packages/web-shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@
"@kilocode/notifications": "workspace:*",
"@kilocode/organization-entitlement": "workspace:*",
"@kilocode/worker-utils": "workspace:*",
"@opentelemetry/api": "1.9.1",
"@opentelemetry/api-logs": "0.208.0",
"@opentelemetry/sdk-logs": "0.212.0",
"@opentelemetry/sdk-metrics": "2.6.1",
"@sentry/nextjs": "10.69.0",
"@sentry/opentelemetry": "10.69.0",
"@slack/types": "2.21.1",
"@trpc/server": "catalog:",
"@typesafe-ai/sdk": "0.6.0",
"@upstash/redis": "^1.38.0",
"@vercel/firewall": "1.2.1",
"@vercel/functions": "3.4.6",
"@vercel/otel": "2.1.2",
"date-fns": "4.1.0",
"dayjs": "1.11.20",
"drizzle-orm": "catalog:",
Expand All @@ -49,7 +55,6 @@
},
"devDependencies": {
"@jest/globals": "30.3.0",
"@opentelemetry/api": "1.9.1",
"@playwright/test": "1.58.2",
"@swc/core": "1.15.18",
"@types/jest": "30.0.0",
Expand Down
42 changes: 42 additions & 0 deletions packages/web-shared/src/lib/observability/node-instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { getClient, SentryContextManager, validateOpenTelemetrySetup } from '@sentry/nextjs';
import { SentryPropagator, SentrySampler, SentrySpanProcessor } from '@sentry/opentelemetry';
import { registerOTel } from '@vercel/otel';
import { initServerSentry } from '@/lib/observability/sentry-server';

// Import this module only when NEXT_RUNTIME is 'nodejs'.
//
// Use Vercel's OpenTelemetry SDK (Node runtime) but supply the Sentry-required components
// (sampler/propagator/context manager/span processor) ourselves.
//
// Notes on runtime split:
// - `@vercel/otel` ships distinct entrypoints for Edge vs Node via conditional exports
// (edge -> dist/edge, node -> dist/node):
// https://github.com/vercel/otel/blob/v2.1.0/package.json#L14-L42
// - The Node build wires Node-specific instrumentations such as node:http / node:https
// and async context managers (async_hooks), which are not available in Edge runtime.
// See bundled code in dist/node/index.js (search for "async_hooks" and "node:http").
// - While `@vercel/otel` technically supports Edge runtime via its dist/edge build,
// it provides only lightweight trace propagation, not full auto-instrumentation.
// See: https://www.npmjs.com/package/@vercel/otel (README describes Edge as "lightweight mode")
//
// We intentionally only enable the Sentry+OTel custom wiring in Node runtime for now.
// Edge tracing should rely on Sentry's Edge SDK behavior configured in apps/web/sentry.edge.config.ts.
// ref: https://docs.sentry.io/platforms/javascript/guides/nextjs/opentelemetry/custom-setup/
export function registerNodeInstrumentation(serviceName: string): void {
initServerSentry();

const sentryClient = getClient();
if (!sentryClient) {
console.warn('Sentry client not found in instrumentation.ts');
}

registerOTel({
serviceName,
traceSampler: sentryClient ? new SentrySampler(sentryClient) : undefined,
spanProcessors: ['auto', new SentrySpanProcessor()],
propagators: ['auto', new SentryPropagator()],
contextManager: new SentryContextManager(),
});

validateOpenTelemetrySetup();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, test, expect } from '@jest/globals';
import type { Event } from '@sentry/nextjs';
import { sanitizeSentryRequestData } from '../sentry.server.config';
import { sanitizeSentryRequestData } from '@/lib/observability/sentry-server';

describe('sanitizeSentryRequestData', () => {
test('removes the GitHub OAuth state token from request URL and query string', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// This file configures the initialization of Sentry on the server.
// The config you add here will be used whenever the server handles a request.
// Sentry initialization for the Node.js server runtime of apps/web and apps/ai-gateway.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
// But note tricky corner cases using vercel otel with sentry:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/opentelemetry/custom-setup/

import type { Event } from '@sentry/nextjs';
import { consoleLoggingIntegration, httpIntegration, init } from '@sentry/nextjs';
import { sanitizeAnalyticsPathname } from './src/lib/sanitize-analytics-url';
import { sanitizeAnalyticsPathname } from '@/lib/sanitize-analytics-url';

type DrizzleQueryError = Error & {
query: string;
Expand Down Expand Up @@ -175,7 +174,11 @@ export function sanitizeSentryRequestData(event: Event): Event {
return event;
}

if (process.env.NODE_ENV !== 'development') {
export function initServerSentry(): void {
if (process.env.NODE_ENV === 'development') {
return;
}

init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,

Expand Down
24 changes: 21 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading