@powerhousedao/switchboard 6.0.0-dev.236 → 6.0.0-dev.238

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## 6.0.0-dev.238 (2026-05-11)
2
+
3
+ ### 🚀 Features
4
+
5
+ - **switchboard:** bridge OpenTelemetry spans to Sentry ([c1f2fc28b](https://github.com/powerhouse-inc/powerhouse/commit/c1f2fc28b))
6
+ - **ph-cli,ph-cmd,shared:** use lightweight sentry sdk ([248c6b2f6](https://github.com/powerhouse-inc/powerhouse/commit/248c6b2f6))
7
+
8
+ ### 🩹 Fixes
9
+
10
+ - update dockerfiles for pnpm 11 bin path ([d33db03ce](https://github.com/powerhouse-inc/powerhouse/commit/d33db03ce))
11
+ - **switchboard:** only enable tracing if a destination is configured ([8abff8020](https://github.com/powerhouse-inc/powerhouse/commit/8abff8020))
12
+
13
+ ### ❤️ Thank You
14
+
15
+ - acaldas
16
+
17
+ ## 6.0.0-dev.237 (2026-05-10)
18
+
19
+ This was a version bump only for @powerhousedao/switchboard to align it with other projects, there were no code changes.
20
+
1
21
  ## 6.0.0-dev.236 (2026-05-09)
2
22
 
3
23
  ### 🚀 Features
package/README.md CHANGED
@@ -13,6 +13,7 @@ A powerful document-driven server that provides a unified API for managing and s
13
13
  - **HTTPS Support**: Built-in HTTPS server with custom certificates
14
14
  - **Profiling**: Integration with Pyroscope for performance monitoring
15
15
  - **Error Tracking**: Sentry integration for error monitoring and reporting
16
+ - **Observability**: Unified OpenTelemetry tracing + metrics bootstrap, with optional Sentry APM bridging (same trace IDs in Tempo and Sentry)
16
17
 
17
18
  ## 📦 Installation
18
19
 
@@ -106,11 +107,11 @@ pnpm add -g @powerhousedao/switchboard
106
107
  | `PH_REACTOR_DATABASE_URL` | PostgreSQL URL (takes precedence) | - |
107
108
  | `REDIS_URL` | Redis connection URL | - |
108
109
  | `REDIS_TLS_URL` | Redis TLS connection URL | - |
109
- | `SENTRY_DSN` | Sentry DSN for error tracking | - |
110
- | `SENTRY_ENV` | Sentry environment | - |
111
110
  | `PYROSCOPE_SERVER_ADDRESS` | Pyroscope server address | - |
112
111
  | `FEATURE_REACTORV2_ENABLED` | Enable Reactor v2 subgraph feature | `false` |
113
112
 
113
+ See [Observability](#observability) below for Sentry and OpenTelemetry variables.
114
+
114
115
  ### Authentication Configuration
115
116
 
116
117
  ```typescript
@@ -132,6 +133,47 @@ Switchboard supports multiple storage backends:
132
133
  - **PostgreSQL**: Persistent database storage
133
134
  - **Redis**: Caching layer (optional)
134
135
 
136
+ ### Observability
137
+
138
+ Switchboard bootstraps Sentry and OpenTelemetry from a single module (`src/observability.mts`) that is imported as the very first thing in `src/index.mts`. The OpenTelemetry instrumentations (`http`, `express`, `pg`, `graphql`) register require-time hooks at load, so the import order must not be changed.
139
+
140
+ What runs depends on which environment variables are set:
141
+
142
+ - `SENTRY_DSN` set → Sentry error reporting is initialized.
143
+ - `ENABLE_TRACING=true` or `NODE_ENV=production`, **and** at least one trace destination is configured (`TEMPO_ENDPOINT` or `SENTRY_DSN`) → OpenTelemetry tracing is initialized. Spans go to Tempo over OTLP HTTP if `TEMPO_ENDPOINT` is set, and/or to Sentry via `SentrySpanProcessor` if `SENTRY_DSN` is set. When both are set, the same trace IDs appear in Tempo and Sentry and cross-link in Grafana.
144
+ - Tracing requested (prod or `ENABLE_TRACING=true`) but no destination configured → a warning is logged and tracing is **not** started. This avoids the cost of patching `http`/`express`/`pg`/`graphql` and generating spans only to drop them. Set `TEMPO_ENDPOINT` and/or `SENTRY_DSN` to enable.
145
+ - `OTEL_EXPORTER_OTLP_ENDPOINT` set → metrics export to that OTLP HTTP endpoint via a periodic reader. Reactor metrics emitted by `@powerhousedao/opentelemetry-instrumentation-reactor` flow through the same global meter provider.
146
+
147
+ If neither Sentry nor a tracing destination is configured, the module is a no-op and no exporters or instrumentations are registered.
148
+
149
+ #### Environment Variables
150
+
151
+ | Variable | Description | Default |
152
+ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
153
+ | `SENTRY_DSN` | Sentry DSN. When unset, Sentry is disabled. | - |
154
+ | `SENTRY_ENV` | `environment` tag passed to `Sentry.init`. | - |
155
+ | `SENTRY_RELEASE` | Release tag (must match the version uploaded by CI for source maps to resolve). | `v${npm_package_version}` if available |
156
+ | `SENTRY_TRACES_SAMPLE_RATE` | APM sampling rate (0.0–1.0). | `0.1` |
157
+ | `ENABLE_TRACING` | Set to `true` to request tracing outside production. Tracing only starts when a destination is also set. | `false` (also requested when `NODE_ENV=production`) |
158
+ | `NODE_ENV` | When `production`, tracing is requested automatically. Also exported as `deployment.environment` attribute. | `development` |
159
+ | `OTEL_SERVICE_NAME` | `service.name` resource attribute. | `switchboard` |
160
+ | `TENANT_ID` | `tenant.id` resource attribute (used to slice traces by tenant in Grafana). | `default` |
161
+ | `TEMPO_ENDPOINT` | OTLP HTTP endpoint for trace export. When unset, OTLP trace export is disabled. In-cluster deploys typically set `http://tempo.monitoring.svc.cluster.local:4318/v1/traces`. | - |
162
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP HTTP endpoint for metrics export. Metrics export is disabled when unset. | - |
163
+ | `OTEL_METRIC_EXPORT_INTERVAL` | Metric export interval in milliseconds. | `60000` |
164
+
165
+ #### Local Development
166
+
167
+ Tracing is off by default outside production, so a bare `pnpm dev` does not need any of these set. To exercise the full pipeline locally, point `TEMPO_ENDPOINT` and `OTEL_EXPORTER_OTLP_ENDPOINT` at a local OTel collector (or Tempo + Prometheus) and run with `ENABLE_TRACING=true`:
168
+
169
+ ```bash
170
+ ENABLE_TRACING=true \
171
+ TEMPO_ENDPOINT=http://localhost:4318/v1/traces \
172
+ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318/v1/metrics \
173
+ SENTRY_DSN=... \
174
+ pnpm dev
175
+ ```
176
+
135
177
  ## 🐳 Docker Deployment
136
178
 
137
179
  ### Using Docker Compose
@@ -5435,7 +5435,7 @@ var require_node = /* @__PURE__ */ __commonJSMin(((exports, module) => {
5435
5435
  };
5436
5436
  }));
5437
5437
  //#endregion
5438
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/utils/pyroscope-profiler.js
5438
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/utils/pyroscope-profiler.js
5439
5439
  var import_src = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
5440
5440
  /**
5441
5441
  * Detect Electron renderer / nwjs process, which is node, but we should
@@ -5458,7 +5458,7 @@ function setProfiler(value) {
5458
5458
  pyroscopeProfiler = value;
5459
5459
  }
5460
5460
  //#endregion
5461
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/middleware/express.js
5461
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/middleware/express.js
5462
5462
  const log$5 = (0, import_src.default)("pyroscope");
5463
5463
  async function collectProfile$1(profiler) {
5464
5464
  const profile = profiler.profile().profile;
@@ -5510,7 +5510,7 @@ function expressMiddleware() {
5510
5510
  };
5511
5511
  }
5512
5512
  //#endregion
5513
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/middleware/fastify.js
5513
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/middleware/fastify.js
5514
5514
  const log$4 = (0, import_src.default)("pyroscope");
5515
5515
  async function collectProfile(profiler) {
5516
5516
  const profile = profiler.profile().profile;
@@ -5567,7 +5567,7 @@ const fastifyMiddleware = () => {
5567
5567
  return plugin;
5568
5568
  };
5569
5569
  //#endregion
5570
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/logger.js
5570
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/logger.js
5571
5571
  /**
5572
5572
  * Copyright 2024 Datadog Inc. All Rights Reserved.
5573
5573
  *
@@ -5596,13 +5596,13 @@ function setLogger$1(newLogger) {
5596
5596
  logger = newLogger;
5597
5597
  }
5598
5598
  //#endregion
5599
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/utils/date-to-unix-timestamp.js
5599
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/utils/date-to-unix-timestamp.js
5600
5600
  const MS_PER_SECOND$1 = 1e3;
5601
5601
  function dateToUnixTimestamp(date) {
5602
5602
  return Math.floor(date.getTime() / MS_PER_SECOND$1);
5603
5603
  }
5604
5604
  //#endregion
5605
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/utils/process-profile.js
5605
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/utils/process-profile.js
5606
5606
  const V8_NAME_TO_GOLANG_NAME_MAP = {
5607
5607
  objects: "inuse_objects",
5608
5608
  sample: "samples",
@@ -5633,7 +5633,7 @@ function adjustSampleNames(profile) {
5633
5633
  }
5634
5634
  }
5635
5635
  //#endregion
5636
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/pyroscope-api-exporter.js
5636
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/pyroscope-api-exporter.js
5637
5637
  const log$3 = (0, import_src.default)("pyroscope");
5638
5638
  var PyroscopeApiExporter = class {
5639
5639
  applicationName;
@@ -5698,7 +5698,7 @@ var PyroscopeApiExporter = class {
5698
5698
  }
5699
5699
  };
5700
5700
  //#endregion
5701
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/profilers/continuous-profiler.js
5701
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/profilers/continuous-profiler.js
5702
5702
  const log$2 = (0, import_src.default)("pyroscope::profiler");
5703
5703
  var ContinuousProfiler = class {
5704
5704
  profiler;
@@ -5759,7 +5759,7 @@ var ContinuousProfiler = class {
5759
5759
  }
5760
5760
  };
5761
5761
  //#endregion
5762
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/profilers/heap-profiler.js
5762
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/profilers/heap-profiler.js
5763
5763
  const log$1 = (0, import_src.default)("pyroscope::profiler::heap");
5764
5764
  var HeapProfiler = class {
5765
5765
  lastProfiledAt;
@@ -5800,7 +5800,7 @@ var HeapProfiler = class {
5800
5800
  }
5801
5801
  };
5802
5802
  //#endregion
5803
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/profilers/wall-profiler.js
5803
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/profilers/wall-profiler.js
5804
5804
  const MICROS_PER_SECOND$1 = 1e6;
5805
5805
  const log = (0, import_src.default)("pyroscope::profiler::wall");
5806
5806
  var WallProfiler = class {
@@ -5871,7 +5871,7 @@ var WallProfiler = class {
5871
5871
  }
5872
5872
  };
5873
5873
  //#endregion
5874
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/profilers/pyroscope-profiler.js
5874
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/profilers/pyroscope-profiler.js
5875
5875
  const MICROS_PER_SECOND = 1e6;
5876
5876
  const MS_PER_SECOND = 1e3;
5877
5877
  const B_PER_MB = 1024;
@@ -6072,7 +6072,7 @@ function validateConcurrency(concurrency) {
6072
6072
  if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) throw new TypeError("Expected `concurrency` to be a number from 1 and up");
6073
6073
  }
6074
6074
  //#endregion
6075
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/sourcemapper.js
6075
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/sourcemapper.js
6076
6076
  /**
6077
6077
  * Copyright 2024 Datadog Inc. All Rights Reserved.
6078
6078
  * Copyright 2016 Google Inc. All Rights Reserved.
@@ -6308,7 +6308,7 @@ async function getSourceCodeFiles(baseDir) {
6308
6308
  return mapFiles;
6309
6309
  }
6310
6310
  //#endregion
6311
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/utils/check-pyroscope-config.js
6311
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/utils/check-pyroscope-config.js
6312
6312
  function checkPyroscopeConfig(config) {
6313
6313
  if (!isObject(config)) throw new Error("Expecting an object config");
6314
6314
  const errors = [];
@@ -6360,7 +6360,7 @@ function isObject(object) {
6360
6360
  return object !== null && typeof object === "object";
6361
6361
  }
6362
6362
  //#endregion
6363
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/utils/get-env.js
6363
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/utils/get-env.js
6364
6364
  function getEnv() {
6365
6365
  return {
6366
6366
  adhocServerAddress: process.env["PYROSCOPE_ADHOC_SERVER_ADDRESS"],
@@ -6382,7 +6382,7 @@ function parseBooleanEnv(envVal) {
6382
6382
  return envVal === void 0 ? envVal : envVal === "true" || envVal === "1";
6383
6383
  }
6384
6384
  //#endregion
6385
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/utils/process-config.js
6385
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/utils/process-config.js
6386
6386
  function processConfig(config, env) {
6387
6387
  return {
6388
6388
  appName: config.appName ?? env.appName,
@@ -6406,7 +6406,7 @@ function processConfig(config, env) {
6406
6406
  };
6407
6407
  }
6408
6408
  //#endregion
6409
- //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.4/node_modules/@pyroscope/nodejs/dist/esm/index.js
6409
+ //#region ../../node_modules/.pnpm/@pyroscope+nodejs@0.4.10_express@4.22.1_fastify@5.8.5/node_modules/@pyroscope/nodejs/dist/esm/index.js
6410
6410
  function init(config = {}) {
6411
6411
  checkPyroscopeConfig(config);
6412
6412
  setProfiler(new PyroscopeProfiler(processConfig(config, getEnv())));
@@ -6478,4 +6478,4 @@ var esm_default = {
6478
6478
  //#endregion
6479
6479
  export { esm_default as default };
6480
6480
 
6481
- //# sourceMappingURL=esm-aE6sDAbA.mjs.map
6481
+ //# sourceMappingURL=esm-CigE675X.mjs.map