@blamejs/core 0.8.43 → 0.8.50

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.
Files changed (222) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +10 -10
  3. package/index.js +52 -0
  4. package/lib/a2a.js +159 -34
  5. package/lib/acme.js +762 -0
  6. package/lib/ai-pref.js +166 -43
  7. package/lib/api-key.js +108 -47
  8. package/lib/api-snapshot.js +157 -40
  9. package/lib/app-shutdown.js +113 -77
  10. package/lib/archive.js +337 -40
  11. package/lib/arg-parser.js +697 -0
  12. package/lib/asyncapi.js +99 -55
  13. package/lib/atomic-file.js +465 -104
  14. package/lib/audit-chain.js +123 -34
  15. package/lib/audit-daily-review.js +389 -0
  16. package/lib/audit-sign.js +302 -56
  17. package/lib/audit-tools.js +412 -63
  18. package/lib/audit.js +656 -35
  19. package/lib/auth/jwt-external.js +17 -0
  20. package/lib/auth/oauth.js +7 -0
  21. package/lib/auth-bot-challenge.js +505 -0
  22. package/lib/auth-header.js +92 -25
  23. package/lib/backup/bundle.js +26 -0
  24. package/lib/backup/index.js +512 -89
  25. package/lib/backup/manifest.js +168 -7
  26. package/lib/break-glass.js +415 -39
  27. package/lib/budr.js +103 -30
  28. package/lib/bundler.js +86 -66
  29. package/lib/cache.js +192 -72
  30. package/lib/chain-writer.js +65 -40
  31. package/lib/circuit-breaker.js +56 -33
  32. package/lib/cli-helpers.js +106 -75
  33. package/lib/cli.js +6 -30
  34. package/lib/cloud-events.js +99 -32
  35. package/lib/cluster-storage.js +162 -37
  36. package/lib/cluster.js +340 -49
  37. package/lib/codepoint-class.js +66 -0
  38. package/lib/compliance.js +424 -24
  39. package/lib/config-drift.js +111 -46
  40. package/lib/config.js +94 -40
  41. package/lib/consent.js +165 -18
  42. package/lib/constants.js +1 -0
  43. package/lib/content-credentials.js +153 -48
  44. package/lib/cookies.js +154 -62
  45. package/lib/credential-hash.js +133 -61
  46. package/lib/crypto-field.js +702 -18
  47. package/lib/crypto-hpke.js +256 -0
  48. package/lib/crypto.js +744 -22
  49. package/lib/csv.js +178 -35
  50. package/lib/daemon.js +456 -0
  51. package/lib/dark-patterns.js +186 -55
  52. package/lib/db-query.js +79 -2
  53. package/lib/db.js +1431 -60
  54. package/lib/ddl-change-control.js +523 -0
  55. package/lib/deprecate.js +195 -40
  56. package/lib/dev.js +82 -39
  57. package/lib/dora.js +67 -48
  58. package/lib/dr-runbook.js +368 -0
  59. package/lib/dsr.js +142 -11
  60. package/lib/dual-control.js +91 -56
  61. package/lib/events.js +120 -41
  62. package/lib/external-db-migrate.js +192 -2
  63. package/lib/external-db.js +795 -50
  64. package/lib/fapi2.js +122 -1
  65. package/lib/fda-21cfr11.js +395 -0
  66. package/lib/fdx.js +132 -2
  67. package/lib/file-type.js +87 -0
  68. package/lib/file-upload.js +93 -0
  69. package/lib/flag.js +82 -20
  70. package/lib/forms.js +132 -29
  71. package/lib/framework-error.js +169 -0
  72. package/lib/framework-schema.js +163 -35
  73. package/lib/gate-contract.js +849 -175
  74. package/lib/graphql-federation.js +68 -7
  75. package/lib/guard-all.js +172 -55
  76. package/lib/guard-archive.js +286 -124
  77. package/lib/guard-auth.js +194 -21
  78. package/lib/guard-cidr.js +190 -28
  79. package/lib/guard-csv.js +397 -51
  80. package/lib/guard-domain.js +213 -91
  81. package/lib/guard-email.js +236 -29
  82. package/lib/guard-filename.js +307 -75
  83. package/lib/guard-graphql.js +263 -30
  84. package/lib/guard-html.js +310 -116
  85. package/lib/guard-image.js +243 -30
  86. package/lib/guard-json.js +260 -54
  87. package/lib/guard-jsonpath.js +235 -23
  88. package/lib/guard-jwt.js +284 -30
  89. package/lib/guard-markdown.js +204 -22
  90. package/lib/guard-mime.js +190 -26
  91. package/lib/guard-oauth.js +277 -28
  92. package/lib/guard-pdf.js +251 -27
  93. package/lib/guard-regex.js +226 -18
  94. package/lib/guard-shell.js +229 -26
  95. package/lib/guard-svg.js +177 -10
  96. package/lib/guard-template.js +232 -21
  97. package/lib/guard-time.js +195 -29
  98. package/lib/guard-uuid.js +189 -30
  99. package/lib/guard-xml.js +259 -36
  100. package/lib/guard-yaml.js +241 -44
  101. package/lib/honeytoken.js +63 -27
  102. package/lib/html-balance.js +83 -0
  103. package/lib/http-client.js +486 -59
  104. package/lib/http-message-signature.js +582 -0
  105. package/lib/i18n.js +102 -49
  106. package/lib/iab-mspa.js +112 -32
  107. package/lib/iab-tcf.js +107 -2
  108. package/lib/inbox.js +90 -52
  109. package/lib/keychain.js +865 -0
  110. package/lib/legal-hold.js +374 -0
  111. package/lib/local-db-thin.js +320 -0
  112. package/lib/log-stream.js +281 -51
  113. package/lib/log.js +184 -86
  114. package/lib/mail-bounce.js +107 -62
  115. package/lib/mail.js +295 -58
  116. package/lib/mcp.js +108 -27
  117. package/lib/metrics.js +98 -89
  118. package/lib/middleware/age-gate.js +36 -0
  119. package/lib/middleware/ai-act-disclosure.js +37 -0
  120. package/lib/middleware/api-encrypt.js +45 -0
  121. package/lib/middleware/assetlinks.js +40 -0
  122. package/lib/middleware/asyncapi-serve.js +35 -0
  123. package/lib/middleware/attach-user.js +40 -0
  124. package/lib/middleware/bearer-auth.js +40 -0
  125. package/lib/middleware/body-parser.js +230 -0
  126. package/lib/middleware/bot-disclose.js +34 -0
  127. package/lib/middleware/bot-guard.js +39 -0
  128. package/lib/middleware/compression.js +37 -0
  129. package/lib/middleware/cookies.js +32 -0
  130. package/lib/middleware/cors.js +40 -0
  131. package/lib/middleware/csp-nonce.js +40 -0
  132. package/lib/middleware/csp-report.js +34 -0
  133. package/lib/middleware/csrf-protect.js +43 -0
  134. package/lib/middleware/daily-byte-quota.js +53 -85
  135. package/lib/middleware/db-role-for.js +40 -0
  136. package/lib/middleware/dpop.js +40 -0
  137. package/lib/middleware/error-handler.js +37 -14
  138. package/lib/middleware/fetch-metadata.js +39 -0
  139. package/lib/middleware/flag-context.js +34 -0
  140. package/lib/middleware/gpc.js +33 -0
  141. package/lib/middleware/headers.js +35 -0
  142. package/lib/middleware/health.js +46 -0
  143. package/lib/middleware/host-allowlist.js +30 -0
  144. package/lib/middleware/network-allowlist.js +38 -0
  145. package/lib/middleware/openapi-serve.js +34 -0
  146. package/lib/middleware/rate-limit.js +160 -18
  147. package/lib/middleware/request-id.js +36 -18
  148. package/lib/middleware/request-log.js +37 -0
  149. package/lib/middleware/require-aal.js +29 -0
  150. package/lib/middleware/require-auth.js +32 -0
  151. package/lib/middleware/require-bound-key.js +41 -0
  152. package/lib/middleware/require-content-type.js +32 -0
  153. package/lib/middleware/require-methods.js +27 -0
  154. package/lib/middleware/require-mtls.js +33 -0
  155. package/lib/middleware/require-step-up.js +37 -0
  156. package/lib/middleware/security-headers.js +44 -0
  157. package/lib/middleware/security-txt.js +38 -0
  158. package/lib/middleware/span-http-server.js +37 -0
  159. package/lib/middleware/sse.js +36 -0
  160. package/lib/middleware/trace-log-correlation.js +33 -0
  161. package/lib/middleware/trace-propagate.js +32 -0
  162. package/lib/middleware/tus-upload.js +90 -0
  163. package/lib/middleware/web-app-manifest.js +53 -0
  164. package/lib/mtls-ca.js +100 -70
  165. package/lib/network-byte-quota.js +308 -0
  166. package/lib/network-heartbeat.js +135 -0
  167. package/lib/network-tls.js +534 -4
  168. package/lib/network.js +103 -0
  169. package/lib/notify.js +114 -43
  170. package/lib/ntp-check.js +192 -51
  171. package/lib/observability.js +145 -47
  172. package/lib/openapi.js +90 -44
  173. package/lib/outbox.js +99 -1
  174. package/lib/pagination.js +168 -86
  175. package/lib/parsers/index.js +16 -5
  176. package/lib/permissions.js +93 -40
  177. package/lib/pqc-agent.js +84 -8
  178. package/lib/pqc-software.js +94 -60
  179. package/lib/process-spawn.js +95 -21
  180. package/lib/pubsub.js +96 -66
  181. package/lib/queue.js +375 -54
  182. package/lib/redact.js +793 -21
  183. package/lib/render.js +139 -47
  184. package/lib/request-helpers.js +485 -121
  185. package/lib/restore-bundle.js +142 -39
  186. package/lib/restore-rollback.js +136 -45
  187. package/lib/retention.js +178 -50
  188. package/lib/retry.js +116 -33
  189. package/lib/router.js +475 -23
  190. package/lib/safe-async.js +543 -94
  191. package/lib/safe-buffer.js +337 -41
  192. package/lib/safe-json.js +467 -62
  193. package/lib/safe-jsonpath.js +285 -0
  194. package/lib/safe-schema.js +631 -87
  195. package/lib/safe-sql.js +221 -59
  196. package/lib/safe-url.js +278 -46
  197. package/lib/sandbox-worker.js +135 -0
  198. package/lib/sandbox.js +358 -0
  199. package/lib/scheduler.js +135 -70
  200. package/lib/self-update.js +647 -0
  201. package/lib/session-device-binding.js +431 -0
  202. package/lib/session.js +259 -49
  203. package/lib/slug.js +138 -26
  204. package/lib/ssrf-guard.js +316 -56
  205. package/lib/storage.js +433 -70
  206. package/lib/subject.js +405 -23
  207. package/lib/template.js +148 -8
  208. package/lib/tenant-quota.js +545 -0
  209. package/lib/testing.js +440 -53
  210. package/lib/time.js +291 -23
  211. package/lib/tls-exporter.js +239 -0
  212. package/lib/tracing.js +90 -74
  213. package/lib/uuid.js +97 -22
  214. package/lib/vault/index.js +284 -22
  215. package/lib/vault/seal-pem-file.js +66 -0
  216. package/lib/watcher.js +368 -0
  217. package/lib/webhook.js +196 -63
  218. package/lib/websocket.js +393 -68
  219. package/lib/wiki-concepts.js +338 -0
  220. package/lib/worker-pool.js +464 -0
  221. package/package.json +3 -3
  222. package/sbom.cyclonedx.json +7 -7
package/lib/log-stream.js CHANGED
@@ -1,56 +1,52 @@
1
1
  "use strict";
2
2
  /**
3
- * Log streaming dispatcher — operational logs to one or many sinks with
4
- * redaction and bidirectional command channel.
5
- *
6
- * Same dispatcher pattern: backends configured per-name with a protocol
7
- * + protocol-specific options. Built-in protocols:
8
- *
9
- * local append-only file with rotation
10
- * webhook — generic HTTP POST; covers Splunk HEC, Datadog, Sumo
11
- * Logic, Loki, custom collectors that ingest JSON
12
- * otlp — OpenTelemetry Protocol over HTTP/JSON; ResourceLogs
13
- * envelope with severity mapping per OTel Logs Data
14
- * Model. Operators with an OTel collector running
15
- * (k8s, cloud) get standard log forwarding without a
16
- * vendor-specific adapter.
17
- * otlp-grpc same OTel Logs Data Model but over gRPC (HTTP/2 +
18
- * hand-encoded protobuf). Higher-throughput than the
19
- * JSON variant; preferred for production deployments
20
- * pushing >100K logs/s straight to a remote OTel
21
- * collector. No protobuf parser ships in the bundle —
22
- * the encoder is the framework's own (lib/protobuf-encoder.js).
23
- * cloudwatch AWS CloudWatch Logs (PutLogEvents) over HTTPS with
24
- * SigV4. Pass { autoCreate: true } to have the framework
25
- * issue CreateLogGroup + CreateLogStream on first emit
26
- * (idempotent ResourceAlreadyExistsException treated as
27
- * success). Honors IAM role + STS session tokens. Respects
28
- * the 10K-event / 1 MiB / 256 KiB-per-event AWS caps.
29
- * syslog — RFC 5424 with octet-counting framing over UDP / TCP /
30
- * TLS. UDP is best-effort; TCP/TLS buffer during socket
31
- * reconnect and replay on connect. Default ports 514
32
- * (UDP/TCP) and 6514 (TLS).
33
- *
34
- * Every emit goes through lib/redact.js BEFORE any sink sees it. PHI/PCI
35
- * never reaches operational logs even on a misconfigured field name —
36
- * pattern detectors catch credit-card-shaped values, JWTs, PEM blocks,
37
- * AWS access keys, vault-sealed strings, SSN-shaped values, etc.
38
- *
39
- * Bidirectional command channel:
40
- * logStream.onIncoming(handler) registers a handler for inbound events.
41
- * Operators wire their HTTP route (or other transport — webhook receiver,
42
- * SSE, message-queue subscriber) to call logStream.deliverIncoming(payload)
43
- * which invokes registered handlers. The framework doesn't prescribe the
44
- * transport it provides the dispatch.
45
- *
46
- * Public API:
47
- * logStream.init({ sinks: { name: { protocol, ... } }, classification? })
48
- * logStream.emit(level, message, meta?) (sync — non-blocking)
49
- * logStream.info(msg, meta?) / .warn / .error / .debug
50
- * logStream.onIncoming(handler) (handler returns Promise)
51
- * logStream.deliverIncoming(payload, opts?)
52
- * logStream.shutdown()
53
- * logStream.listSinks() → [{ name, protocol, stats }]
3
+ * @module b.logStream
4
+ * @nav Observability
5
+ * @title Log Stream
6
+ *
7
+ * @intro
8
+ * Pluggable structured-log dispatcher — fire-and-forget JSON records
9
+ * from request hot paths to one or many sinks (local file with
10
+ * rotation, generic webhook, OTLP HTTP/JSON, OTLP gRPC, AWS
11
+ * CloudWatch Logs, RFC 5424 syslog over UDP/TCP/TLS). Each sink
12
+ * keeps its own connection / fd / batch buffer, so a slow remote
13
+ * collector backpressures only its own queue never the request
14
+ * thread that called `emit()`.
15
+ *
16
+ * Every record passes through `b.redact` BEFORE any sink sees it.
17
+ * PHI / PCI / JWTs / PEM blocks / AWS access keys / vault-sealed
18
+ * strings / credit-card-shaped digits / SSN-shaped values are
19
+ * stripped on the framework side, not delegated to the operator's
20
+ * sink config a misnamed field cannot leak sensitive data into
21
+ * operational logs.
22
+ *
23
+ * Sink failures are drop-silent on the hot path: a captured Promise
24
+ * reroutes the error into `audit.system.log.sink_failure` so a
25
+ * downed collector never crashes the request that emitted the log
26
+ * line. Pending emits are tracked and drained on `shutdown()` so
27
+ * records queued just before close still reach disk / the wire.
28
+ *
29
+ * Bidirectional command channel: `onIncoming(handler)` registers a
30
+ * handler for inbound events; the operator wires their preferred
31
+ * transport (HTTP route, webhook receiver, SSE subscription,
32
+ * message-queue consumer) to call `deliverIncoming(payload)`, which
33
+ * redacts, audits, and dispatches to every registered handler. The
34
+ * framework provides the dispatch; the operator provides the wire.
35
+ *
36
+ * Built-in protocols:
37
+ * local — append-only file with size + age rotation
38
+ * webhook — generic HTTP POST (Splunk HEC, Datadog, Loki,
39
+ * Sumo Logic, custom JSON collectors)
40
+ * otlp — OpenTelemetry Protocol over HTTP/JSON
41
+ * otlp-grpc — same Logs Data Model over gRPC (higher throughput;
42
+ * hand-encoded protobuf, no parser dependency)
43
+ * cloudwatch PutLogEvents over HTTPS with SigV4; honours the
44
+ * 10K-event / 1 MiB / 256 KiB-per-event AWS caps
45
+ * syslog — RFC 5424 octet-counting framing over UDP / TCP /
46
+ * TLS (default ports 514 / 6514)
47
+ *
48
+ * @card
49
+ * Pluggable structured-log dispatcher — fire-and-forget JSON records from request hot paths to one or many sinks (local file with rotation, generic webhook, OTLP HTTP/JSON, OTLP gRPC, AWS CloudWatch Logs, RFC 5424 syslog over UDP/TCP/TLS).
54
50
  */
55
51
  var localProto = require("./log-stream-local");
56
52
  var webhookProto = require("./log-stream-webhook");
@@ -100,6 +96,39 @@ var _inflight = new Set();
100
96
  var minLevel = "info";
101
97
  var incomingHandlers = [];
102
98
 
99
+ /**
100
+ * @primitive b.logStream.init
101
+ * @signature b.logStream.init(opts)
102
+ * @since 0.0.13
103
+ * @related b.logStream.bootFromEnv, b.logStream.shutdown, b.logStream.listSinks
104
+ *
105
+ * Configure the dispatcher. Call once at boot; subsequent calls are
106
+ * no-ops while the dispatcher is initialized (call `shutdown()` first
107
+ * to reconfigure). Every named sink resolves a built-in protocol
108
+ * (`local` / `webhook` / `otlp` / `otlp-grpc` / `cloudwatch` /
109
+ * `syslog`) and constructs a per-sink instance from its own typed
110
+ * config block.
111
+ *
112
+ * Records below `minLevel` (or a sink's per-sink `minLevel` override)
113
+ * are dropped before redaction — debug / info chatter on a
114
+ * production deployment costs nothing past the dispatcher.
115
+ *
116
+ * @opts
117
+ * sinks: { [name]: { protocol, minLevel?, ...protocolOpts } },
118
+ * minLevel: "debug" | "info" | "warn" | "error", // default "info"
119
+ *
120
+ * @example
121
+ * b.logStream.init({
122
+ * minLevel: "info",
123
+ * sinks: {
124
+ * file: { protocol: "local", path: "/var/log/app.log" },
125
+ * remote: { protocol: "otlp",
126
+ * url: "https://collector.internal:4318/v1/logs",
127
+ * serviceName: "checkout",
128
+ * minLevel: "warn" },
129
+ * },
130
+ * });
131
+ */
103
132
  function init(opts) {
104
133
  if (initialized) return;
105
134
  if (!opts || !opts.sinks) throw new Error("logStream.init({ sinks }) is required");
@@ -125,6 +154,31 @@ function _shouldEmit(level, sinkLevelFilter) {
125
154
  return LEVEL_PRIORITY[level] >= threshold;
126
155
  }
127
156
 
157
+ /**
158
+ * @primitive b.logStream.emit
159
+ * @signature b.logStream.emit(level, message, meta?)
160
+ * @since 0.0.13
161
+ * @related b.logStream.info, b.logStream.warn, b.logStream.error, b.logStream.debug, b.redact.redact
162
+ *
163
+ * Synchronous, fire-and-forget emit to every registered sink whose
164
+ * level filter accepts `level`. The record is `{ ts, level, message,
165
+ * meta }`; `meta` is run through `b.redact.redact` BEFORE distribution
166
+ * so PHI / credentials / vault-sealed values never reach a sink even
167
+ * on a misnamed field. Sink errors are captured, audited
168
+ * (`system.log.sink_failure`), and discarded — a downed collector
169
+ * cannot crash the caller. Throws only on an unknown level (config
170
+ * typo at the call site).
171
+ *
172
+ * @example
173
+ * // Structured event with sensitive metadata — `apiKey` and
174
+ * // `cardNumber` are redacted by pattern before any sink sees them.
175
+ * b.logStream.emit("warn", "checkout retry", {
176
+ * orderId: "ord_01HXYZ",
177
+ * attempt: 3,
178
+ * apiKey: "<sk-live-placeholder>",
179
+ * cardNumber: "<pan-placeholder>",
180
+ * });
181
+ */
128
182
  function emit(level, message, meta) {
129
183
  if (!initialized) return;
130
184
  if (LEVELS.indexOf(level) === -1) {
@@ -164,13 +218,93 @@ function emit(level, message, meta) {
164
218
  });
165
219
  }
166
220
 
221
+ /**
222
+ * @primitive b.logStream.debug
223
+ * @signature b.logStream.debug(message, meta?)
224
+ * @since 0.0.13
225
+ * @related b.logStream.emit, b.logStream.info, b.logStream.warn, b.logStream.error
226
+ *
227
+ * Convenience wrapper for `emit("debug", ...)`. Records drop below
228
+ * `minLevel` (default `"info"`) without serialization cost, so leaving
229
+ * `debug()` calls in production code is cheap.
230
+ *
231
+ * @example
232
+ * b.logStream.debug("cache lookup", { key: "user:42", hit: false });
233
+ */
167
234
  function debug(message, meta) { emit("debug", message, meta); }
235
+
236
+ /**
237
+ * @primitive b.logStream.info
238
+ * @signature b.logStream.info(message, meta?)
239
+ * @since 0.0.13
240
+ * @related b.logStream.emit, b.logStream.debug, b.logStream.warn, b.logStream.error
241
+ *
242
+ * Convenience wrapper for `emit("info", ...)`. Use for routine
243
+ * lifecycle events worth keeping in the operational log under default
244
+ * filtering.
245
+ *
246
+ * @example
247
+ * b.logStream.info("worker ready", { pid: process.pid, queue: "checkout" });
248
+ */
168
249
  function info(message, meta) { emit("info", message, meta); }
250
+
251
+ /**
252
+ * @primitive b.logStream.warn
253
+ * @signature b.logStream.warn(message, meta?)
254
+ * @since 0.0.13
255
+ * @related b.logStream.emit, b.logStream.debug, b.logStream.info, b.logStream.error
256
+ *
257
+ * Convenience wrapper for `emit("warn", ...)`. Use for recoverable
258
+ * anomalies the operator should notice but that don't fail the
259
+ * request — retry exhaustion below the cap, degraded-mode entry,
260
+ * cache misses on a hot key.
261
+ *
262
+ * @example
263
+ * b.logStream.warn("retry succeeded after backoff", {
264
+ * route: "POST /checkout", attempts: 4, totalMs: 1820,
265
+ * });
266
+ */
169
267
  function warn(message, meta) { emit("warn", message, meta); }
268
+
269
+ /**
270
+ * @primitive b.logStream.error
271
+ * @signature b.logStream.error(message, meta?)
272
+ * @since 0.0.13
273
+ * @related b.logStream.emit, b.logStream.debug, b.logStream.info, b.logStream.warn
274
+ *
275
+ * Convenience wrapper for `emit("error", ...)`. Use for the failed-
276
+ * request / unhandled-exception class. `b.audit` remains the
277
+ * authoritative tamper-evident record for privileged actions; the
278
+ * log stream is operational telemetry.
279
+ *
280
+ * @example
281
+ * b.logStream.error("dispatcher failure", {
282
+ * route: "POST /checkout", err: "ECONNRESET", upstream: "payments",
283
+ * });
284
+ */
170
285
  function error(message, meta) { emit("error", message, meta); }
171
286
 
172
287
  // ---- Bidirectional incoming command channel ----
173
288
 
289
+ /**
290
+ * @primitive b.logStream.onIncoming
291
+ * @signature b.logStream.onIncoming(handler)
292
+ * @since 0.0.13
293
+ * @related b.logStream.deliverIncoming
294
+ *
295
+ * Register a handler for inbound command-channel events. Returns an
296
+ * unsubscribe function. Handlers may be `async` and may return a
297
+ * value; `deliverIncoming` collects every handler's result and reports
298
+ * per-handler success / failure. Throws on a non-function argument.
299
+ *
300
+ * @example
301
+ * var off = b.logStream.onIncoming(async function (payload) {
302
+ * if (payload.command === "raise-log-level") return { applied: true };
303
+ * return { applied: false };
304
+ * });
305
+ * // Later, when teardown is needed:
306
+ * off();
307
+ */
174
308
  function onIncoming(handler) {
175
309
  if (typeof handler !== "function") {
176
310
  throw _err("INVALID_HANDLER", "onIncoming requires a function handler", true);
@@ -182,6 +316,31 @@ function onIncoming(handler) {
182
316
  };
183
317
  }
184
318
 
319
+ /**
320
+ * @primitive b.logStream.deliverIncoming
321
+ * @signature b.logStream.deliverIncoming(payload, opts?)
322
+ * @since 0.0.13
323
+ * @related b.logStream.onIncoming, b.audit.safeEmit
324
+ *
325
+ * Dispatch an inbound command-channel payload to every registered
326
+ * handler. The payload is redacted before audit and before handlers
327
+ * run, so even a noisy webhook receiver cannot smuggle secrets into
328
+ * the audit chain. Audit-logs the receipt under
329
+ * `system.log.incoming` BEFORE invoking handlers — handler exceptions
330
+ * never erase the receipt. Returns a per-handler `[{ ok, value? |
331
+ * error? }]` array; one handler throwing does not abort the rest.
332
+ *
333
+ * @opts
334
+ * actor: { userId?, sessionId?, ip?, userAgent? }, // audit context
335
+ * source: string, // transport name
336
+ *
337
+ * @example
338
+ * var results = await b.logStream.deliverIncoming(
339
+ * { command: "rotate-sink", sink: "file" },
340
+ * { actor: { userId: "ops-42" }, source: "webhook" }
341
+ * );
342
+ * // → [{ ok: true, value: { applied: false } }]
343
+ */
185
344
  async function deliverIncoming(payload, opts) {
186
345
  opts = opts || {};
187
346
  var redacted = redactor.redact(payload);
@@ -204,6 +363,24 @@ async function deliverIncoming(payload, opts) {
204
363
  return results;
205
364
  }
206
365
 
366
+ /**
367
+ * @primitive b.logStream.shutdown
368
+ * @signature b.logStream.shutdown()
369
+ * @since 0.0.13
370
+ * @related b.logStream.init, b.appShutdown.create
371
+ *
372
+ * Drain pending fire-and-forget emits, close every sink (file fds,
373
+ * webhook keep-alive sockets, syslog connections, OTLP gRPC streams),
374
+ * and clear registered incoming handlers. Idempotent — safe to call
375
+ * twice. Records queued just before shutdown reach disk / the wire
376
+ * because in-flight Promises are tracked and awaited before close.
377
+ *
378
+ * @example
379
+ * process.on("SIGTERM", async function () {
380
+ * await b.logStream.shutdown();
381
+ * process.exit(0);
382
+ * });
383
+ */
207
384
  async function shutdown() {
208
385
  if (!initialized) return;
209
386
  // Drain any in-flight emits before closing sink fds so records
@@ -222,6 +399,23 @@ async function shutdown() {
222
399
  initialized = false;
223
400
  }
224
401
 
402
+ /**
403
+ * @primitive b.logStream.listSinks
404
+ * @signature b.logStream.listSinks()
405
+ * @since 0.0.13
406
+ * @related b.logStream.init
407
+ *
408
+ * Return one descriptor per configured sink: `{ name, protocol, stats
409
+ * }`. Sinks that expose a `stats()` method (file rotation counters,
410
+ * webhook batch metrics, OTLP queue depth) report through it; those
411
+ * that don't return `null`. Returns `[]` before `init()` runs, so
412
+ * health endpoints can call it unconditionally.
413
+ *
414
+ * @example
415
+ * var snapshot = b.logStream.listSinks();
416
+ * // → [{ name: "file", protocol: "local",
417
+ * // stats: { rotations: 2, bytesWritten: 4194304 } }]
418
+ */
225
419
  function listSinks() {
226
420
  if (!initialized) return [];
227
421
  return Object.keys(sinks).map(function (name) {
@@ -255,6 +449,42 @@ function listSinks() {
255
449
  // BLAMEJS_LOG_STREAM_CLOUDWATCH_LOG_STREAM
256
450
  // local-only:
257
451
  // BLAMEJS_LOG_STREAM_PATH
452
+ /**
453
+ * @primitive b.logStream.bootFromEnv
454
+ * @signature b.logStream.bootFromEnv(opts?)
455
+ * @since 0.6.25
456
+ * @related b.logStream.init, b.network.bootFromEnv
457
+ *
458
+ * Operator-friendly env-driven init. Reads `BLAMEJS_LOG_STREAM_*` (and
459
+ * standard `AWS_*`) variables and constructs a single-sink
460
+ * configuration. Returns `false` and skips silently when
461
+ * `BLAMEJS_LOG_STREAM_PROTOCOL` is unset, so deployments that wire
462
+ * sinks through `init()` keep their existing config. Throws on an
463
+ * unknown protocol value.
464
+ *
465
+ * Recognised variables: `BLAMEJS_LOG_STREAM_PROTOCOL` (`local` |
466
+ * `webhook` | `otlp` | `cloudwatch`), `BLAMEJS_LOG_STREAM_MIN_LEVEL`,
467
+ * `BLAMEJS_LOG_STREAM_URL`, `BLAMEJS_LOG_STREAM_TOKEN`,
468
+ * `BLAMEJS_LOG_STREAM_SERVICE_NAME`, `BLAMEJS_LOG_STREAM_PATH`,
469
+ * `BLAMEJS_LOG_STREAM_CLOUDWATCH_LOG_GROUP`, plus `AWS_REGION` /
470
+ * `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_SESSION_TOKEN`.
471
+ *
472
+ * @opts
473
+ * env: object, // override process.env (testing / fixtures)
474
+ *
475
+ * @example
476
+ * // Operator sets BLAMEJS_LOG_STREAM_PROTOCOL=otlp and the URL in
477
+ * // the deployment manifest; the framework wires the sink at boot.
478
+ * var wired = b.logStream.bootFromEnv({
479
+ * env: {
480
+ * BLAMEJS_LOG_STREAM_PROTOCOL: "otlp",
481
+ * BLAMEJS_LOG_STREAM_URL: "https://collector.internal:4318/v1/logs",
482
+ * BLAMEJS_LOG_STREAM_SERVICE_NAME: "checkout",
483
+ * BLAMEJS_LOG_STREAM_MIN_LEVEL: "info",
484
+ * },
485
+ * });
486
+ * // → true (false when BLAMEJS_LOG_STREAM_PROTOCOL is unset)
487
+ */
258
488
  function bootFromEnv(opts) {
259
489
  opts = opts || {};
260
490
  var env = opts.env || process.env;