@camstack/addon-mqtt-broker 1.2.77 → 1.2.79

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.
@@ -7644,111 +7644,6 @@ var CameraSwitchGroupSchema = object({
7644
7644
  fetchedAt: number()
7645
7645
  });
7646
7646
  /**
7647
- * Per-component log CHANNELS — the gate a hot path consults, and the registry
7648
- * an addon declares its channels in.
7649
- *
7650
- * ## Two axes, deliberately separated
7651
- *
7652
- * - **DECLARATION** — which channels exist. Only the addon knows:
7653
- * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7654
- * baichuan/handshake. A hand-wired central list rots at the first addition,
7655
- * and rots silently. So a channel is declared where it is consulted, and the
7656
- * `log-channels` capability enumerates the declarations.
7657
- * - **VALUE** — at which level, for which scope, until when. That stays ONE
7658
- * thing: the logging settings document on the `system` cap. Two authorities
7659
- * over the values is the exact defect
7660
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7661
- * remove; re-introducing it from the cure side would be grotesque.
7662
- *
7663
- * Nothing in this file reads a clock, an env var or a store. The registry is
7664
- * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7665
- * the hot path with a value somebody actually read, and by
7666
- * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7667
- * never reaches here, so it can neither disarm an armed channel nor arm a
7668
- * disarmed one (D49).
7669
- *
7670
- * ## The canonical call shape
7671
- *
7672
- * ```ts
7673
- * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7674
- * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7675
- * }
7676
- * ```
7677
- *
7678
- * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7679
- * read. Disarmed, a call site costs one load and one branch, and the `extras`
7680
- * object literal is never constructed because it lives inside the branch. It
7681
- * is the same shape already proven in production at `stream-broker.ts:1650`,
7682
- * and the same discipline `LoggingGate.allowsDestination` uses for the
7683
- * destination floor (measured at 1.93 ns/call when off).
7684
- *
7685
- * ## Why a channel emits at `info`
7686
- *
7687
- * `loki-logging.addon.ts` pins the destination default at `info` and
7688
- * `loki-destination.ts` drops everything below it, so a line emitted at
7689
- * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7690
- * minutes. A diagnostic that cannot be read an hour later is worse than no
7691
- * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7692
- * emits at the channel's declared level, whose schema floor is `info`.
7693
- */
7694
- /**
7695
- * The level a channel writes at once armed.
7696
- *
7697
- * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7698
- * not leave the process for Loki, and the whole point of arming a channel is
7699
- * to read it later.
7700
- */
7701
- var LogChannelLevelSchema = _enum([
7702
- "info",
7703
- "warn",
7704
- "error"
7705
- ]);
7706
- /**
7707
- * What an addon declares about one channel. No value, no state — a
7708
- * declaration is inert.
7709
- */
7710
- var LogChannelDescriptorSchema = object({
7711
- /**
7712
- * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7713
- * the addon's short name so an operator reading a channel list can tell who
7714
- * owns it without a second lookup.
7715
- */
7716
- name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7717
- /** One sentence: what the operator will SEE after arming it. */
7718
- description: string().min(1),
7719
- /** The level its lines are emitted at. Never below `info`. */
7720
- defaultLevel: LogChannelLevelSchema,
7721
- /**
7722
- * Whether this channel can be narrowed to a camera.
7723
- *
7724
- * `true` is a PROMISE with two halves, and both must hold: the gate is
7725
- * consulted with the numeric device id, AND every line the channel admits
7726
- * carries `tags: { deviceId }` with that same numeric id. The second half is
7727
- * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7728
- * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7729
- * the body is the only way to filter.
7730
- *
7731
- * A channel whose lines carry the device only in `meta` (or not at all) is
7732
- * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7733
- * the operator narrows to one camera, sees nothing, and concludes the code
7734
- * path was never taken.
7735
- */
7736
- perDevice: boolean()
7737
- });
7738
- /**
7739
- * An armed window over one channel, as the document hands it to a mirror.
7740
- *
7741
- * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7742
- * expires by itself, which is the one failure a boolean cannot avoid.
7743
- */
7744
- var LogChannelWindowSchema = object({
7745
- channel: string().min(1),
7746
- /** Epoch ms the window closes at. */
7747
- armedUntilMs: number(),
7748
- /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7749
- deviceIds: array(number().int()).readonly().nullable()
7750
- });
7751
- /**
7752
7647
  * Ops-log — the durable, append-only operations audit shared by the
7753
7648
  * recordings and events management surfaces.
7754
7649
  *
@@ -8694,8 +8589,11 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8694
8589
  * `STORAGE_LOCATION_CARDINALITY` map has been removed.
8695
8590
  *
8696
8591
  * `id` is a stable namespaced string of the form `<type>:<slug>`.
8697
- * The default location for a type uses `id === <type>:default` by
8698
- * convention (the bare type ref like `'backups'` resolves to it).
8592
+ * The seed names its first instance `<type>:default` — a NAME, not a flag.
8593
+ * There is no default location any more (D383): `enabled` is the whole write
8594
+ * model, and a bare type ref resolves to the sole location of the type, or —
8595
+ * transitionally, only while legacy NULL-stamped rows exist — to the row whose
8596
+ * slug is `default`.
8699
8597
  *
8700
8598
  * `isSystem` is a legacy persisted flag. Seed still creates the initial
8701
8599
  * `<type>:default` locations; the flag is no longer a lock, a badge, or a
@@ -8716,21 +8614,20 @@ var StorageLocationSchema = object({
8716
8614
  * flag at upsert time, not here (the schema is provider-agnostic).
8717
8615
  */
8718
8616
  nodeId: string().optional(),
8719
- isDefault: boolean().default(false),
8720
8617
  isSystem: boolean().default(false),
8721
8618
  /**
8722
- * Operator opt-in: whether consumers that BALANCE across several locations
8723
- * of a type may write here. Recordings reads it today; event media and
8724
- * backups are the next consumers, which is why the flag lives on the
8725
- * location rather than in any one addon's store — nothing has to be
8726
- * extended to add the next consumer.
8619
+ * THE write switch, and the only one (D383). `enabled: true` means every
8620
+ * consumer that chooses a write target for this type may write here, and all
8621
+ * enabled locations of a type are used TOGETHER; `false` means read-only —
8622
+ * still read, still played back, still age-swept, still drained, never
8623
+ * written.
8727
8624
  *
8728
- * OPTIONAL, and ABSENT MEANS ACTIVE. Every location persisted before the
8729
- * flag existed reads back with no flag and keeps working exactly as before;
8730
- * that is the whole compat story, and it is why no migration ships with it.
8731
- * A newly CREATED sibling is stamped `false` by the orchestrator (creating a
8732
- * disk must not silently start writing to it); the default of a type is
8733
- * always stamped `true`.
8625
+ * OPTIONAL only for the wire: an upsert that omits it means "leave what is
8626
+ * stored" on an update and "born inert unless it is the first location of its
8627
+ * type" on a create. On a PERSISTED row absence is legacy and it means
8628
+ * enabled — {@link isLocationEnabled} is the one place that says so, and the
8629
+ * orchestrator stamps every flagless row `true` once at hydrate so absence
8630
+ * stops existing rather than being re-derived on every read.
8734
8631
  */
8735
8632
  enabled: boolean().optional(),
8736
8633
  /** COMPUTED at read time by the orchestrator (statfs of the backing volume
@@ -8743,10 +8640,12 @@ var StorageLocationSchema = object({
8743
8640
  createdAt: number(),
8744
8641
  updatedAt: number()
8745
8642
  });
8643
+ object({ isDefault: boolean().optional() });
8746
8644
  /**
8747
8645
  * Reference accepted by consumer-facing `api.storage.*` calls.
8748
8646
  * Either:
8749
- * - a `StorageLocationType` (e.g. `'backups'`) → orchestrator resolves to the default of that type
8647
+ * - a `StorageLocationType` (e.g. `'backups'`) → the sole location of that type
8648
+ * (transitionally, the `<type>:default`-slugged row when several exist)
8750
8649
  * - a fully-qualified id (e.g. `'backups:nas-01'`) → addresses a specific instance
8751
8650
  *
8752
8651
  * The orchestrator's `resolveRef(ref)` handles both cases.
@@ -8922,6 +8821,111 @@ var DecoderSessionConfigSchema = object({
8922
8821
  */
8923
8822
  debug: boolean().optional()
8924
8823
  });
8824
+ /**
8825
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
8826
+ * an addon declares its channels in.
8827
+ *
8828
+ * ## Two axes, deliberately separated
8829
+ *
8830
+ * - **DECLARATION** — which channels exist. Only the addon knows:
8831
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
8832
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
8833
+ * and rots silently. So a channel is declared where it is consulted, and the
8834
+ * `log-channels` capability enumerates the declarations.
8835
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
8836
+ * thing: the logging settings document on the `system` cap. Two authorities
8837
+ * over the values is the exact defect
8838
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
8839
+ * remove; re-introducing it from the cure side would be grotesque.
8840
+ *
8841
+ * Nothing in this file reads a clock, an env var or a store. The registry is
8842
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
8843
+ * the hot path with a value somebody actually read, and by
8844
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
8845
+ * never reaches here, so it can neither disarm an armed channel nor arm a
8846
+ * disarmed one (D49).
8847
+ *
8848
+ * ## The canonical call shape
8849
+ *
8850
+ * ```ts
8851
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
8852
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
8853
+ * }
8854
+ * ```
8855
+ *
8856
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
8857
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
8858
+ * object literal is never constructed because it lives inside the branch. It
8859
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
8860
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
8861
+ * destination floor (measured at 1.93 ns/call when off).
8862
+ *
8863
+ * ## Why a channel emits at `info`
8864
+ *
8865
+ * `loki-logging.addon.ts` pins the destination default at `info` and
8866
+ * `loki-destination.ts` drops everything below it, so a line emitted at
8867
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
8868
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
8869
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
8870
+ * emits at the channel's declared level, whose schema floor is `info`.
8871
+ */
8872
+ /**
8873
+ * The level a channel writes at once armed.
8874
+ *
8875
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
8876
+ * not leave the process for Loki, and the whole point of arming a channel is
8877
+ * to read it later.
8878
+ */
8879
+ var LogChannelLevelSchema = _enum([
8880
+ "info",
8881
+ "warn",
8882
+ "error"
8883
+ ]);
8884
+ /**
8885
+ * What an addon declares about one channel. No value, no state — a
8886
+ * declaration is inert.
8887
+ */
8888
+ var LogChannelDescriptorSchema = object({
8889
+ /**
8890
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
8891
+ * the addon's short name so an operator reading a channel list can tell who
8892
+ * owns it without a second lookup.
8893
+ */
8894
+ name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
8895
+ /** One sentence: what the operator will SEE after arming it. */
8896
+ description: string().min(1),
8897
+ /** The level its lines are emitted at. Never below `info`. */
8898
+ defaultLevel: LogChannelLevelSchema,
8899
+ /**
8900
+ * Whether this channel can be narrowed to a camera.
8901
+ *
8902
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
8903
+ * consulted with the numeric device id, AND every line the channel admits
8904
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
8905
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
8906
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
8907
+ * the body is the only way to filter.
8908
+ *
8909
+ * A channel whose lines carry the device only in `meta` (or not at all) is
8910
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
8911
+ * the operator narrows to one camera, sees nothing, and concludes the code
8912
+ * path was never taken.
8913
+ */
8914
+ perDevice: boolean()
8915
+ });
8916
+ /**
8917
+ * An armed window over one channel, as the document hands it to a mirror.
8918
+ *
8919
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
8920
+ * expires by itself, which is the one failure a boolean cannot avoid.
8921
+ */
8922
+ var LogChannelWindowSchema = object({
8923
+ channel: string().min(1),
8924
+ /** Epoch ms the window closes at. */
8925
+ armedUntilMs: number(),
8926
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
8927
+ deviceIds: array(number().int()).readonly().nullable()
8928
+ });
8925
8929
  var MODEL_FORMATS = [
8926
8930
  "onnx",
8927
8931
  "coreml",
@@ -10578,12 +10582,30 @@ var BackupDestinationInfoSchema = object({
10578
10582
  lastSuccessAt: number().optional(),
10579
10583
  /** Newest-archive size from `manifests.json`, or undefined. */
10580
10584
  lastSuccessSizeBytes: number().optional(),
10581
- /** Per-destination cron expression. Empty = manual-only (no schedule). */
10585
+ /**
10586
+ * Cron cadence(s) of the ENABLED schedules that fan out to this
10587
+ * destination, comma-joined. Absent when no enabled schedule targets it
10588
+ * — a destination nothing is scheduled to write to must not advertise a
10589
+ * cadence (D384). This is never the `backup_destination_policies.cron`
10590
+ * column: that per-location cron has scheduled nothing since 2026-07-28
10591
+ * and reading it made a destination with a DISABLED schedule claim a
10592
+ * nightly run.
10593
+ */
10582
10594
  cron: string().optional(),
10583
- /** ms-epoch of next computed firing for this destination's cron, if any. */
10595
+ /** ms-epoch of the next firing across those schedules (earliest), if any. */
10584
10596
  nextRunAt: number().optional(),
10585
- /** ms-epoch of last successful scheduled run (mirrors policy.lastRunAt). */
10586
- lastRunAt: number().optional()
10597
+ /**
10598
+ * ms-epoch of the last time a run ATTEMPTED to write here — success or
10599
+ * failure. Never a success stamp: pair it with `lastSuccessAt` (the
10600
+ * newest archive that actually landed) and `lastError`.
10601
+ */
10602
+ lastAttemptAt: number().optional(),
10603
+ /**
10604
+ * Why the last attempt failed, verbatim. Absent when the last attempt
10605
+ * landed the archive. A destination that has never been written to has
10606
+ * neither this nor `lastAttemptAt`.
10607
+ */
10608
+ lastError: string().optional()
10587
10609
  });
10588
10610
  /**
10589
10611
  * Per-archive entry returned by `backup.listArchives({ destinationId })`.
@@ -10746,8 +10768,16 @@ var BackupScheduleSchema = object({
10746
10768
  retentionCount: number().int().min(1).max(1e3),
10747
10769
  /** Optional subset of source locations to include; omitted = all. */
10748
10770
  dataSources: array(string()).readonly().optional(),
10749
- /** ms-epoch of last successful run. */
10750
- lastRunAt: number().optional(),
10771
+ /**
10772
+ * ms-epoch of the last tick that FIRED this schedule. Stamped before the
10773
+ * archive runs (it is the dedupe anchor), so it says "attempted", never
10774
+ * "succeeded" — a run refused by every destination stamps it too.
10775
+ */
10776
+ lastAttemptAt: number().optional(),
10777
+ /** ms-epoch of the last run of this schedule that landed at ≥1 destination. */
10778
+ lastSuccessAt: number().optional(),
10779
+ /** Why the last fired run failed, verbatim. Absent when it succeeded. */
10780
+ lastError: string().optional(),
10751
10781
  /** ms-epoch of next computed firing (read-only, filled on list). */
10752
10782
  nextRunAt: number().optional()
10753
10783
  });
@@ -10796,14 +10826,7 @@ method(_void(), array(BackupDestinationInfoSchema).readonly(), { auth: "admin" }
10796
10826
  locationId: string(),
10797
10827
  enabled: boolean(),
10798
10828
  retentionCount: number().int().min(1).max(1e3),
10799
- label: string().optional(),
10800
- /**
10801
- * Per-destination cron expression. Empty string clears the
10802
- * schedule (manual-only). Validated server-side via croner;
10803
- * malformed expressions reject the upsert with an actionable
10804
- * message.
10805
- */
10806
- cron: string().optional()
10829
+ label: string().optional()
10807
10830
  }), _void(), {
10808
10831
  kind: "mutation",
10809
10832
  auth: "admin"
@@ -21635,7 +21658,7 @@ method(object({
21635
21658
  downloadId: string(),
21636
21659
  offset: number(),
21637
21660
  length: number()
21638
- }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(object({ type: StorageLocationTypeSchema }), StorageLocationSchema.nullable()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
21661
+ }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
21639
21662
  createdAt: true,
21640
21663
  updatedAt: true
21641
21664
  }), StorageLocationSchema, {
@@ -34484,12 +34507,6 @@ Object.freeze({
34484
34507
  addonId: null,
34485
34508
  access: "view"
34486
34509
  },
34487
- "storage.getDefaultLocation": {
34488
- capName: "storage",
34489
- capScope: "system",
34490
- addonId: null,
34491
- access: "view"
34492
- },
34493
34510
  "storage.list": {
34494
34511
  capName: "storage",
34495
34512
  capScope: "system",
@@ -7639,111 +7639,6 @@ var CameraSwitchGroupSchema = object({
7639
7639
  fetchedAt: number()
7640
7640
  });
7641
7641
  /**
7642
- * Per-component log CHANNELS — the gate a hot path consults, and the registry
7643
- * an addon declares its channels in.
7644
- *
7645
- * ## Two axes, deliberately separated
7646
- *
7647
- * - **DECLARATION** — which channels exist. Only the addon knows:
7648
- * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7649
- * baichuan/handshake. A hand-wired central list rots at the first addition,
7650
- * and rots silently. So a channel is declared where it is consulted, and the
7651
- * `log-channels` capability enumerates the declarations.
7652
- * - **VALUE** — at which level, for which scope, until when. That stays ONE
7653
- * thing: the logging settings document on the `system` cap. Two authorities
7654
- * over the values is the exact defect
7655
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7656
- * remove; re-introducing it from the cure side would be grotesque.
7657
- *
7658
- * Nothing in this file reads a clock, an env var or a store. The registry is
7659
- * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7660
- * the hot path with a value somebody actually read, and by
7661
- * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7662
- * never reaches here, so it can neither disarm an armed channel nor arm a
7663
- * disarmed one (D49).
7664
- *
7665
- * ## The canonical call shape
7666
- *
7667
- * ```ts
7668
- * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7669
- * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7670
- * }
7671
- * ```
7672
- *
7673
- * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7674
- * read. Disarmed, a call site costs one load and one branch, and the `extras`
7675
- * object literal is never constructed because it lives inside the branch. It
7676
- * is the same shape already proven in production at `stream-broker.ts:1650`,
7677
- * and the same discipline `LoggingGate.allowsDestination` uses for the
7678
- * destination floor (measured at 1.93 ns/call when off).
7679
- *
7680
- * ## Why a channel emits at `info`
7681
- *
7682
- * `loki-logging.addon.ts` pins the destination default at `info` and
7683
- * `loki-destination.ts` drops everything below it, so a line emitted at
7684
- * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7685
- * minutes. A diagnostic that cannot be read an hour later is worse than no
7686
- * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7687
- * emits at the channel's declared level, whose schema floor is `info`.
7688
- */
7689
- /**
7690
- * The level a channel writes at once armed.
7691
- *
7692
- * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7693
- * not leave the process for Loki, and the whole point of arming a channel is
7694
- * to read it later.
7695
- */
7696
- var LogChannelLevelSchema = _enum([
7697
- "info",
7698
- "warn",
7699
- "error"
7700
- ]);
7701
- /**
7702
- * What an addon declares about one channel. No value, no state — a
7703
- * declaration is inert.
7704
- */
7705
- var LogChannelDescriptorSchema = object({
7706
- /**
7707
- * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7708
- * the addon's short name so an operator reading a channel list can tell who
7709
- * owns it without a second lookup.
7710
- */
7711
- name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7712
- /** One sentence: what the operator will SEE after arming it. */
7713
- description: string().min(1),
7714
- /** The level its lines are emitted at. Never below `info`. */
7715
- defaultLevel: LogChannelLevelSchema,
7716
- /**
7717
- * Whether this channel can be narrowed to a camera.
7718
- *
7719
- * `true` is a PROMISE with two halves, and both must hold: the gate is
7720
- * consulted with the numeric device id, AND every line the channel admits
7721
- * carries `tags: { deviceId }` with that same numeric id. The second half is
7722
- * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7723
- * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7724
- * the body is the only way to filter.
7725
- *
7726
- * A channel whose lines carry the device only in `meta` (or not at all) is
7727
- * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7728
- * the operator narrows to one camera, sees nothing, and concludes the code
7729
- * path was never taken.
7730
- */
7731
- perDevice: boolean()
7732
- });
7733
- /**
7734
- * An armed window over one channel, as the document hands it to a mirror.
7735
- *
7736
- * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7737
- * expires by itself, which is the one failure a boolean cannot avoid.
7738
- */
7739
- var LogChannelWindowSchema = object({
7740
- channel: string().min(1),
7741
- /** Epoch ms the window closes at. */
7742
- armedUntilMs: number(),
7743
- /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7744
- deviceIds: array(number().int()).readonly().nullable()
7745
- });
7746
- /**
7747
7642
  * Ops-log — the durable, append-only operations audit shared by the
7748
7643
  * recordings and events management surfaces.
7749
7644
  *
@@ -8689,8 +8584,11 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8689
8584
  * `STORAGE_LOCATION_CARDINALITY` map has been removed.
8690
8585
  *
8691
8586
  * `id` is a stable namespaced string of the form `<type>:<slug>`.
8692
- * The default location for a type uses `id === <type>:default` by
8693
- * convention (the bare type ref like `'backups'` resolves to it).
8587
+ * The seed names its first instance `<type>:default` — a NAME, not a flag.
8588
+ * There is no default location any more (D383): `enabled` is the whole write
8589
+ * model, and a bare type ref resolves to the sole location of the type, or —
8590
+ * transitionally, only while legacy NULL-stamped rows exist — to the row whose
8591
+ * slug is `default`.
8694
8592
  *
8695
8593
  * `isSystem` is a legacy persisted flag. Seed still creates the initial
8696
8594
  * `<type>:default` locations; the flag is no longer a lock, a badge, or a
@@ -8711,21 +8609,20 @@ var StorageLocationSchema = object({
8711
8609
  * flag at upsert time, not here (the schema is provider-agnostic).
8712
8610
  */
8713
8611
  nodeId: string().optional(),
8714
- isDefault: boolean().default(false),
8715
8612
  isSystem: boolean().default(false),
8716
8613
  /**
8717
- * Operator opt-in: whether consumers that BALANCE across several locations
8718
- * of a type may write here. Recordings reads it today; event media and
8719
- * backups are the next consumers, which is why the flag lives on the
8720
- * location rather than in any one addon's store — nothing has to be
8721
- * extended to add the next consumer.
8614
+ * THE write switch, and the only one (D383). `enabled: true` means every
8615
+ * consumer that chooses a write target for this type may write here, and all
8616
+ * enabled locations of a type are used TOGETHER; `false` means read-only —
8617
+ * still read, still played back, still age-swept, still drained, never
8618
+ * written.
8722
8619
  *
8723
- * OPTIONAL, and ABSENT MEANS ACTIVE. Every location persisted before the
8724
- * flag existed reads back with no flag and keeps working exactly as before;
8725
- * that is the whole compat story, and it is why no migration ships with it.
8726
- * A newly CREATED sibling is stamped `false` by the orchestrator (creating a
8727
- * disk must not silently start writing to it); the default of a type is
8728
- * always stamped `true`.
8620
+ * OPTIONAL only for the wire: an upsert that omits it means "leave what is
8621
+ * stored" on an update and "born inert unless it is the first location of its
8622
+ * type" on a create. On a PERSISTED row absence is legacy and it means
8623
+ * enabled — {@link isLocationEnabled} is the one place that says so, and the
8624
+ * orchestrator stamps every flagless row `true` once at hydrate so absence
8625
+ * stops existing rather than being re-derived on every read.
8729
8626
  */
8730
8627
  enabled: boolean().optional(),
8731
8628
  /** COMPUTED at read time by the orchestrator (statfs of the backing volume
@@ -8738,10 +8635,12 @@ var StorageLocationSchema = object({
8738
8635
  createdAt: number(),
8739
8636
  updatedAt: number()
8740
8637
  });
8638
+ object({ isDefault: boolean().optional() });
8741
8639
  /**
8742
8640
  * Reference accepted by consumer-facing `api.storage.*` calls.
8743
8641
  * Either:
8744
- * - a `StorageLocationType` (e.g. `'backups'`) → orchestrator resolves to the default of that type
8642
+ * - a `StorageLocationType` (e.g. `'backups'`) → the sole location of that type
8643
+ * (transitionally, the `<type>:default`-slugged row when several exist)
8745
8644
  * - a fully-qualified id (e.g. `'backups:nas-01'`) → addresses a specific instance
8746
8645
  *
8747
8646
  * The orchestrator's `resolveRef(ref)` handles both cases.
@@ -8917,6 +8816,111 @@ var DecoderSessionConfigSchema = object({
8917
8816
  */
8918
8817
  debug: boolean().optional()
8919
8818
  });
8819
+ /**
8820
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
8821
+ * an addon declares its channels in.
8822
+ *
8823
+ * ## Two axes, deliberately separated
8824
+ *
8825
+ * - **DECLARATION** — which channels exist. Only the addon knows:
8826
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
8827
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
8828
+ * and rots silently. So a channel is declared where it is consulted, and the
8829
+ * `log-channels` capability enumerates the declarations.
8830
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
8831
+ * thing: the logging settings document on the `system` cap. Two authorities
8832
+ * over the values is the exact defect
8833
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
8834
+ * remove; re-introducing it from the cure side would be grotesque.
8835
+ *
8836
+ * Nothing in this file reads a clock, an env var or a store. The registry is
8837
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
8838
+ * the hot path with a value somebody actually read, and by
8839
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
8840
+ * never reaches here, so it can neither disarm an armed channel nor arm a
8841
+ * disarmed one (D49).
8842
+ *
8843
+ * ## The canonical call shape
8844
+ *
8845
+ * ```ts
8846
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
8847
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
8848
+ * }
8849
+ * ```
8850
+ *
8851
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
8852
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
8853
+ * object literal is never constructed because it lives inside the branch. It
8854
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
8855
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
8856
+ * destination floor (measured at 1.93 ns/call when off).
8857
+ *
8858
+ * ## Why a channel emits at `info`
8859
+ *
8860
+ * `loki-logging.addon.ts` pins the destination default at `info` and
8861
+ * `loki-destination.ts` drops everything below it, so a line emitted at
8862
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
8863
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
8864
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
8865
+ * emits at the channel's declared level, whose schema floor is `info`.
8866
+ */
8867
+ /**
8868
+ * The level a channel writes at once armed.
8869
+ *
8870
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
8871
+ * not leave the process for Loki, and the whole point of arming a channel is
8872
+ * to read it later.
8873
+ */
8874
+ var LogChannelLevelSchema = _enum([
8875
+ "info",
8876
+ "warn",
8877
+ "error"
8878
+ ]);
8879
+ /**
8880
+ * What an addon declares about one channel. No value, no state — a
8881
+ * declaration is inert.
8882
+ */
8883
+ var LogChannelDescriptorSchema = object({
8884
+ /**
8885
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
8886
+ * the addon's short name so an operator reading a channel list can tell who
8887
+ * owns it without a second lookup.
8888
+ */
8889
+ name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
8890
+ /** One sentence: what the operator will SEE after arming it. */
8891
+ description: string().min(1),
8892
+ /** The level its lines are emitted at. Never below `info`. */
8893
+ defaultLevel: LogChannelLevelSchema,
8894
+ /**
8895
+ * Whether this channel can be narrowed to a camera.
8896
+ *
8897
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
8898
+ * consulted with the numeric device id, AND every line the channel admits
8899
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
8900
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
8901
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
8902
+ * the body is the only way to filter.
8903
+ *
8904
+ * A channel whose lines carry the device only in `meta` (or not at all) is
8905
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
8906
+ * the operator narrows to one camera, sees nothing, and concludes the code
8907
+ * path was never taken.
8908
+ */
8909
+ perDevice: boolean()
8910
+ });
8911
+ /**
8912
+ * An armed window over one channel, as the document hands it to a mirror.
8913
+ *
8914
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
8915
+ * expires by itself, which is the one failure a boolean cannot avoid.
8916
+ */
8917
+ var LogChannelWindowSchema = object({
8918
+ channel: string().min(1),
8919
+ /** Epoch ms the window closes at. */
8920
+ armedUntilMs: number(),
8921
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
8922
+ deviceIds: array(number().int()).readonly().nullable()
8923
+ });
8920
8924
  var MODEL_FORMATS = [
8921
8925
  "onnx",
8922
8926
  "coreml",
@@ -10573,12 +10577,30 @@ var BackupDestinationInfoSchema = object({
10573
10577
  lastSuccessAt: number().optional(),
10574
10578
  /** Newest-archive size from `manifests.json`, or undefined. */
10575
10579
  lastSuccessSizeBytes: number().optional(),
10576
- /** Per-destination cron expression. Empty = manual-only (no schedule). */
10580
+ /**
10581
+ * Cron cadence(s) of the ENABLED schedules that fan out to this
10582
+ * destination, comma-joined. Absent when no enabled schedule targets it
10583
+ * — a destination nothing is scheduled to write to must not advertise a
10584
+ * cadence (D384). This is never the `backup_destination_policies.cron`
10585
+ * column: that per-location cron has scheduled nothing since 2026-07-28
10586
+ * and reading it made a destination with a DISABLED schedule claim a
10587
+ * nightly run.
10588
+ */
10577
10589
  cron: string().optional(),
10578
- /** ms-epoch of next computed firing for this destination's cron, if any. */
10590
+ /** ms-epoch of the next firing across those schedules (earliest), if any. */
10579
10591
  nextRunAt: number().optional(),
10580
- /** ms-epoch of last successful scheduled run (mirrors policy.lastRunAt). */
10581
- lastRunAt: number().optional()
10592
+ /**
10593
+ * ms-epoch of the last time a run ATTEMPTED to write here — success or
10594
+ * failure. Never a success stamp: pair it with `lastSuccessAt` (the
10595
+ * newest archive that actually landed) and `lastError`.
10596
+ */
10597
+ lastAttemptAt: number().optional(),
10598
+ /**
10599
+ * Why the last attempt failed, verbatim. Absent when the last attempt
10600
+ * landed the archive. A destination that has never been written to has
10601
+ * neither this nor `lastAttemptAt`.
10602
+ */
10603
+ lastError: string().optional()
10582
10604
  });
10583
10605
  /**
10584
10606
  * Per-archive entry returned by `backup.listArchives({ destinationId })`.
@@ -10741,8 +10763,16 @@ var BackupScheduleSchema = object({
10741
10763
  retentionCount: number().int().min(1).max(1e3),
10742
10764
  /** Optional subset of source locations to include; omitted = all. */
10743
10765
  dataSources: array(string()).readonly().optional(),
10744
- /** ms-epoch of last successful run. */
10745
- lastRunAt: number().optional(),
10766
+ /**
10767
+ * ms-epoch of the last tick that FIRED this schedule. Stamped before the
10768
+ * archive runs (it is the dedupe anchor), so it says "attempted", never
10769
+ * "succeeded" — a run refused by every destination stamps it too.
10770
+ */
10771
+ lastAttemptAt: number().optional(),
10772
+ /** ms-epoch of the last run of this schedule that landed at ≥1 destination. */
10773
+ lastSuccessAt: number().optional(),
10774
+ /** Why the last fired run failed, verbatim. Absent when it succeeded. */
10775
+ lastError: string().optional(),
10746
10776
  /** ms-epoch of next computed firing (read-only, filled on list). */
10747
10777
  nextRunAt: number().optional()
10748
10778
  });
@@ -10791,14 +10821,7 @@ method(_void(), array(BackupDestinationInfoSchema).readonly(), { auth: "admin" }
10791
10821
  locationId: string(),
10792
10822
  enabled: boolean(),
10793
10823
  retentionCount: number().int().min(1).max(1e3),
10794
- label: string().optional(),
10795
- /**
10796
- * Per-destination cron expression. Empty string clears the
10797
- * schedule (manual-only). Validated server-side via croner;
10798
- * malformed expressions reject the upsert with an actionable
10799
- * message.
10800
- */
10801
- cron: string().optional()
10824
+ label: string().optional()
10802
10825
  }), _void(), {
10803
10826
  kind: "mutation",
10804
10827
  auth: "admin"
@@ -21630,7 +21653,7 @@ method(object({
21630
21653
  downloadId: string(),
21631
21654
  offset: number(),
21632
21655
  length: number()
21633
- }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(object({ type: StorageLocationTypeSchema }), StorageLocationSchema.nullable()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
21656
+ }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
21634
21657
  createdAt: true,
21635
21658
  updatedAt: true
21636
21659
  }), StorageLocationSchema, {
@@ -34479,12 +34502,6 @@ Object.freeze({
34479
34502
  addonId: null,
34480
34503
  access: "view"
34481
34504
  },
34482
- "storage.getDefaultLocation": {
34483
- capName: "storage",
34484
- capScope: "system",
34485
- addonId: null,
34486
- access: "view"
34487
- },
34488
34505
  "storage.list": {
34489
34506
  capName: "storage",
34490
34507
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-mqtt-broker",
3
- "version": "1.2.77",
3
+ "version": "1.2.79",
4
4
  "description": "MQTT broker registry addon for CamStack — manages external broker entries + an optional embedded aedes broker. Consumers spin up their own `mqtt.js` clients via the `mqtt-broker` cap.",
5
5
  "keywords": [
6
6
  "camstack",