@autter/runtime-node 1.3.4 → 1.5.0

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/README.md CHANGED
@@ -1,3 +1,53 @@
1
+ # Operation logging
2
+
3
+ Use `withRuntimeOperation`, `runtimeLogger`, and `createRuntimeLogger` to attach
4
+ application context to logs and completed operations. See the full
5
+ [operation logging guide](../../docs/OPERATION-LOGGING.md) for initialization,
6
+ steps, outcomes, redaction, export lifecycle, and release requirements.
7
+
8
+ # Requests and coded errors (1.5.0)
9
+
10
+ Requires ingester 1.5.0. One summary per request (always kept), request ids
11
+ echoed to clients, one issue per error code:
12
+
13
+ ```ts
14
+ import {
15
+ autterRequests, autterErrorResponse, defineRuntimeErrors, runtimeContext,
16
+ } from "@autter/runtime-node";
17
+
18
+ export const billingErrors = defineRuntimeErrors("billing", {
19
+ declined: { status: 402, message: "Payment declined", expected: true,
20
+ why: "The card issuer rejected the charge", fix: "Ask for another card" },
21
+ limit: ({ plan }: { plan: string }) => ({ status: 429, message: `Plan ${plan} limit reached` }),
22
+ });
23
+
24
+ app.use(autterRequests({ ignore: ["/healthz", "/metrics"] }));
25
+ app.post("/checkout", async (req, res) => {
26
+ runtimeContext.set({ cart: { items: req.body.items.length } });
27
+ runtimeContext.info("Applied coupon", { coupon: "SPRING" }); // folded into the summary
28
+ if (overLimit) throw billingErrors.limit({ plan: "free" }); // Express 5 (next(err) on 4)
29
+ res.json({ ok: true, requestId: runtimeContext.requestId });
30
+ });
31
+ app.use(autterErrorResponse()); // { error: { message, code, why?, fix?, link?, requestId } }
32
+ ```
33
+
34
+ Also: `autterFastify`, `withRuntimeRequest` (fetch-style handlers),
35
+ `runtimeContext.fork` / `runInBackground` / `carrier()` for background work and
36
+ queues, the per-operation AI usage rollup, `waitUntil`, `initAutterLogging`
37
+ (no NodeSDK — for apps with their own OTel), enrichers (`enrichUserAgent`,
38
+ `enrichRequestSize`, `enrichEdgeGeo`, `enrichDeployment`), sinks (`otlpSink`,
39
+ `consoleSink`, `fileSink` → `.autter/runtime/*.jsonl` in development) and test
40
+ helpers:
41
+
42
+ ```ts
43
+ import { captureRuntime, expectOperation } from "@autter/runtime-node/testing";
44
+ const runtime = captureRuntime();
45
+ // … exercise the app …
46
+ expectOperation(runtime, "POST /checkout").toHaveOutcome("degraded").toHaveErrorCode("billing.declined");
47
+ ```
48
+
49
+ Full guide: [Requests, coded errors and background work](../../docs/REQUESTS-AND-ERRORS.md).
50
+
1
51
  # @autter/runtime-node
2
52
 
3
53
  The server tracker exports per-instance RSS, heap, memory limit, and GC
@@ -117,6 +167,35 @@ import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
117
167
  initAutterServer({ ..., instrumentations: [new ExpressInstrumentation()] });
118
168
  ```
119
169
 
170
+ ### Version compatibility: checked for you
171
+
172
+ Some features need a minimum ingester version: operation logging needs
173
+ 1.4.0, memory metrics 1.3.3, endpoint latency 1.3.1. After
174
+ `initAutterServer`, the SDK checks the ingester once in the background
175
+ (`GET /v1/compat`, unref'd, 3 s timeout). For each feature in use that the
176
+ ingester can't store, it prints one warning:
177
+
178
+ ```text
179
+ [autter-runtime] Operation logging needs ingester >= 1.4.0; yours is 1.3.4. Upgrade the ingester: docker pull ghcr.io/autter-dev/otlp-ingester:latest and restart it (ClickHouse migrations run at boot). See …/docs/COMPATIBILITY.md
180
+ ```
181
+
182
+ The check never throws, never delays startup or exit, and is silent when
183
+ versions match or are unknown. `debug: true` logs the result. Turn it off
184
+ with `compatCheck: false` or `AUTTER_COMPAT_CHECK=0`. The browser relay does
185
+ the same for browser features such as CSP violation capture. The SDK reports
186
+ itself through the OTLP resource attributes `telemetry.distro.name` and
187
+ `telemetry.distro.version`, so the Autter dashboard shows the SDK version
188
+ each service runs.
189
+
190
+ For a full report (for example in CI or after a deploy):
191
+
192
+ ```bash
193
+ npx @autter/runtime-node doctor --endpoint https://ingest.example.com [--key "$AUTTER_RUNTIME_KEY"] [--json]
194
+ ```
195
+
196
+ Exit codes: 0 compatible, 1 mismatch or rejected key, 2 ingester unreachable.
197
+ See [docs/COMPATIBILITY.md](../../docs/COMPATIBILITY.md).
198
+
120
199
  ### Lifecycle: never lose telemetry to a forgotten shutdown()
121
200
 
122
201
  Telemetry is batched (errors every ~2 s, healthy traces every ~5 s, metrics
@@ -148,23 +227,39 @@ default 1% sampling the span contribution is negligible; if you set
148
227
  Tail-retained error traces don't distort this: their spans carry
149
228
  `autter.tail_retained` and the ingester keeps them out of span-fed rollups.
150
229
 
151
- ### Privacy: custom attributes are redacted before they leave the process
152
-
153
- The browser relay whitelist-sanitises everything a client posts — but the
154
- server tracker accepts free-form attributes from *your* code, where a stray
155
- `captureException(err, { "user.email": … })` would otherwise go out
156
- verbatim. Custom attributes are therefore scrubbed at capture time by
157
- default:
158
-
159
- - values that look like emails, JWTs, `sk-…`/`ghp_…`/AWS/Slack tokens,
160
- `Bearer …` headers, or `scheme://user:pass@host` URLs are masked;
230
+ ### Privacy: secrets and PII are scrubbed before they leave the process
231
+
232
+ Error context is where secrets leak: `connect ECONNREFUSED
233
+ postgres://admin:hunter2@db…` in an exception message, a provider error
234
+ echoing an API key, a `?token=` in a request URL, a stray
235
+ `captureException(err, { "user.email": … })`. Everything the SDK exports is
236
+ therefore scrubbed by default — custom attributes, exception messages and
237
+ stack traces, span status messages, LLM call attributes and errors,
238
+ structured logs, and the browser events the relay forwards. A final pass at
239
+ export time scrubs every span regardless of who created it (HTTP
240
+ instrumentation URLs, third-party instrumentations, `recordException` in
241
+ your own code).
242
+
243
+ - secrets inside any string are masked in place, keeping the rest of the
244
+ message/stack readable: JWTs, `Bearer`/`Basic` credentials,
245
+ `Authorization:`/`Cookie:`/`Set-Cookie:` header text,
246
+ `scheme://user:pass@host` connection strings (postgres, mysql,
247
+ mongodb+srv, redis, …), vendor keys (`sk-…`, `sk_live_…`, `ghp_…`,
248
+ `github_pat_…`, `xox?-…`, `AIza…`, `AKIA…`, …), PEM private keys,
249
+ `password=`/`?token=`/`?api_key=`-style assignments, emails, and
250
+ Luhn-valid card numbers;
161
251
  - attributes whose **key** looks sensitive (`password`, `token`, `secret`,
162
- `api_key`, `authorization`, `cookie`, `ssn`, `card_number`, …) are masked
163
- wholesale;
164
- - non-sensitive keys and primitives pass through untouched, so grouping and
165
- dashboards keep working.
252
+ `api_key`, `authorization`, `cookie`, `session`, `ssn`, `card_number`, …)
253
+ are masked wholesale, at any nesting depth;
254
+ - non-sensitive keys, numbers (including LLM token counts) and booleans pass
255
+ through untouched, so grouping, dashboards and cost tracking keep working.
256
+
257
+ The same patterns run in the browser SDK, the relay, the Python adapter,
258
+ and again in the ingester before storage (shared test vectors in
259
+ `test-vectors/redaction.json`).
166
260
 
167
- Disable or extend it per service:
261
+ Extend it (applies to messages, stacks and URLs too) or disable it per
262
+ service:
168
263
 
169
264
  ```ts
170
265
  initAutterServer({
@@ -186,7 +281,9 @@ const safe = makeSafeCapture();
186
281
  safe.captureException(err, { "user.email": email }); // masked before export
187
282
  ```
188
283
 
189
- The raw primitive is exported too (`redactAttributes(attrs, options)`).
284
+ The raw primitives are exported too (`redactAttributes(attrs, options)`,
285
+ `redactText(text, options)`). The browser relay takes the same options as
286
+ `createBrowserRelayHandler({ ..., redact })`.
190
287
  This closes the server-side gap to match the browser relay's payload
191
288
  whitelist; it is best-effort scrubbing of obvious PII shapes, not a DLP
192
289
  engine — keep secrets out of attributes in the first place.
@@ -287,4 +384,4 @@ Note on the slow-process monitor: Autter flags HTTP routes from the
287
384
  unsampled request metrics, so route detection works out of the box. Non-HTTP
288
385
  work is only visible where a span exists — relying on 1%-sampled regular
289
386
  traces there would undercount ~100×, which is why these spans skip head
290
- sampling.
387
+ sampling.