@camstack/addon-matter-broker 0.2.76 → 0.2.78

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 (3) hide show
  1. package/dist/addon.js +158 -141
  2. package/dist/addon.mjs +158 -141
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7629,111 +7629,6 @@ var CameraSwitchGroupSchema = object({
7629
7629
  fetchedAt: number()
7630
7630
  });
7631
7631
  /**
7632
- * Per-component log CHANNELS — the gate a hot path consults, and the registry
7633
- * an addon declares its channels in.
7634
- *
7635
- * ## Two axes, deliberately separated
7636
- *
7637
- * - **DECLARATION** — which channels exist. Only the addon knows:
7638
- * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7639
- * baichuan/handshake. A hand-wired central list rots at the first addition,
7640
- * and rots silently. So a channel is declared where it is consulted, and the
7641
- * `log-channels` capability enumerates the declarations.
7642
- * - **VALUE** — at which level, for which scope, until when. That stays ONE
7643
- * thing: the logging settings document on the `system` cap. Two authorities
7644
- * over the values is the exact defect
7645
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7646
- * remove; re-introducing it from the cure side would be grotesque.
7647
- *
7648
- * Nothing in this file reads a clock, an env var or a store. The registry is
7649
- * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7650
- * the hot path with a value somebody actually read, and by
7651
- * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7652
- * never reaches here, so it can neither disarm an armed channel nor arm a
7653
- * disarmed one (D49).
7654
- *
7655
- * ## The canonical call shape
7656
- *
7657
- * ```ts
7658
- * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7659
- * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7660
- * }
7661
- * ```
7662
- *
7663
- * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7664
- * read. Disarmed, a call site costs one load and one branch, and the `extras`
7665
- * object literal is never constructed because it lives inside the branch. It
7666
- * is the same shape already proven in production at `stream-broker.ts:1650`,
7667
- * and the same discipline `LoggingGate.allowsDestination` uses for the
7668
- * destination floor (measured at 1.93 ns/call when off).
7669
- *
7670
- * ## Why a channel emits at `info`
7671
- *
7672
- * `loki-logging.addon.ts` pins the destination default at `info` and
7673
- * `loki-destination.ts` drops everything below it, so a line emitted at
7674
- * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7675
- * minutes. A diagnostic that cannot be read an hour later is worse than no
7676
- * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7677
- * emits at the channel's declared level, whose schema floor is `info`.
7678
- */
7679
- /**
7680
- * The level a channel writes at once armed.
7681
- *
7682
- * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7683
- * not leave the process for Loki, and the whole point of arming a channel is
7684
- * to read it later.
7685
- */
7686
- var LogChannelLevelSchema = _enum([
7687
- "info",
7688
- "warn",
7689
- "error"
7690
- ]);
7691
- /**
7692
- * What an addon declares about one channel. No value, no state — a
7693
- * declaration is inert.
7694
- */
7695
- var LogChannelDescriptorSchema = object({
7696
- /**
7697
- * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7698
- * the addon's short name so an operator reading a channel list can tell who
7699
- * owns it without a second lookup.
7700
- */
7701
- name: string$2().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7702
- /** One sentence: what the operator will SEE after arming it. */
7703
- description: string$2().min(1),
7704
- /** The level its lines are emitted at. Never below `info`. */
7705
- defaultLevel: LogChannelLevelSchema,
7706
- /**
7707
- * Whether this channel can be narrowed to a camera.
7708
- *
7709
- * `true` is a PROMISE with two halves, and both must hold: the gate is
7710
- * consulted with the numeric device id, AND every line the channel admits
7711
- * carries `tags: { deviceId }` with that same numeric id. The second half is
7712
- * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7713
- * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7714
- * the body is the only way to filter.
7715
- *
7716
- * A channel whose lines carry the device only in `meta` (or not at all) is
7717
- * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7718
- * the operator narrows to one camera, sees nothing, and concludes the code
7719
- * path was never taken.
7720
- */
7721
- perDevice: boolean()
7722
- });
7723
- /**
7724
- * An armed window over one channel, as the document hands it to a mirror.
7725
- *
7726
- * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7727
- * expires by itself, which is the one failure a boolean cannot avoid.
7728
- */
7729
- var LogChannelWindowSchema = object({
7730
- channel: string$2().min(1),
7731
- /** Epoch ms the window closes at. */
7732
- armedUntilMs: number(),
7733
- /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7734
- deviceIds: array(number().int()).readonly().nullable()
7735
- });
7736
- /**
7737
7632
  * Ops-log — the durable, append-only operations audit shared by the
7738
7633
  * recordings and events management surfaces.
7739
7634
  *
@@ -8679,8 +8574,11 @@ var StorageLocationTypeSchema = string$2().regex(/^[a-z][a-zA-Z0-9-]*$/);
8679
8574
  * `STORAGE_LOCATION_CARDINALITY` map has been removed.
8680
8575
  *
8681
8576
  * `id` is a stable namespaced string of the form `<type>:<slug>`.
8682
- * The default location for a type uses `id === <type>:default` by
8683
- * convention (the bare type ref like `'backups'` resolves to it).
8577
+ * The seed names its first instance `<type>:default` — a NAME, not a flag.
8578
+ * There is no default location any more (D383): `enabled` is the whole write
8579
+ * model, and a bare type ref resolves to the sole location of the type, or —
8580
+ * transitionally, only while legacy NULL-stamped rows exist — to the row whose
8581
+ * slug is `default`.
8684
8582
  *
8685
8583
  * `isSystem` is a legacy persisted flag. Seed still creates the initial
8686
8584
  * `<type>:default` locations; the flag is no longer a lock, a badge, or a
@@ -8701,21 +8599,20 @@ var StorageLocationSchema = object({
8701
8599
  * flag at upsert time, not here (the schema is provider-agnostic).
8702
8600
  */
8703
8601
  nodeId: string$2().optional(),
8704
- isDefault: boolean().default(false),
8705
8602
  isSystem: boolean().default(false),
8706
8603
  /**
8707
- * Operator opt-in: whether consumers that BALANCE across several locations
8708
- * of a type may write here. Recordings reads it today; event media and
8709
- * backups are the next consumers, which is why the flag lives on the
8710
- * location rather than in any one addon's store — nothing has to be
8711
- * extended to add the next consumer.
8604
+ * THE write switch, and the only one (D383). `enabled: true` means every
8605
+ * consumer that chooses a write target for this type may write here, and all
8606
+ * enabled locations of a type are used TOGETHER; `false` means read-only —
8607
+ * still read, still played back, still age-swept, still drained, never
8608
+ * written.
8712
8609
  *
8713
- * OPTIONAL, and ABSENT MEANS ACTIVE. Every location persisted before the
8714
- * flag existed reads back with no flag and keeps working exactly as before;
8715
- * that is the whole compat story, and it is why no migration ships with it.
8716
- * A newly CREATED sibling is stamped `false` by the orchestrator (creating a
8717
- * disk must not silently start writing to it); the default of a type is
8718
- * always stamped `true`.
8610
+ * OPTIONAL only for the wire: an upsert that omits it means "leave what is
8611
+ * stored" on an update and "born inert unless it is the first location of its
8612
+ * type" on a create. On a PERSISTED row absence is legacy and it means
8613
+ * enabled — {@link isLocationEnabled} is the one place that says so, and the
8614
+ * orchestrator stamps every flagless row `true` once at hydrate so absence
8615
+ * stops existing rather than being re-derived on every read.
8719
8616
  */
8720
8617
  enabled: boolean().optional(),
8721
8618
  /** COMPUTED at read time by the orchestrator (statfs of the backing volume
@@ -8728,10 +8625,12 @@ var StorageLocationSchema = object({
8728
8625
  createdAt: number(),
8729
8626
  updatedAt: number()
8730
8627
  });
8628
+ object({ isDefault: boolean().optional() });
8731
8629
  /**
8732
8630
  * Reference accepted by consumer-facing `api.storage.*` calls.
8733
8631
  * Either:
8734
- * - a `StorageLocationType` (e.g. `'backups'`) → orchestrator resolves to the default of that type
8632
+ * - a `StorageLocationType` (e.g. `'backups'`) → the sole location of that type
8633
+ * (transitionally, the `<type>:default`-slugged row when several exist)
8735
8634
  * - a fully-qualified id (e.g. `'backups:nas-01'`) → addresses a specific instance
8736
8635
  *
8737
8636
  * The orchestrator's `resolveRef(ref)` handles both cases.
@@ -8907,6 +8806,111 @@ var DecoderSessionConfigSchema = object({
8907
8806
  */
8908
8807
  debug: boolean().optional()
8909
8808
  });
8809
+ /**
8810
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
8811
+ * an addon declares its channels in.
8812
+ *
8813
+ * ## Two axes, deliberately separated
8814
+ *
8815
+ * - **DECLARATION** — which channels exist. Only the addon knows:
8816
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
8817
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
8818
+ * and rots silently. So a channel is declared where it is consulted, and the
8819
+ * `log-channels` capability enumerates the declarations.
8820
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
8821
+ * thing: the logging settings document on the `system` cap. Two authorities
8822
+ * over the values is the exact defect
8823
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
8824
+ * remove; re-introducing it from the cure side would be grotesque.
8825
+ *
8826
+ * Nothing in this file reads a clock, an env var or a store. The registry is
8827
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
8828
+ * the hot path with a value somebody actually read, and by
8829
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
8830
+ * never reaches here, so it can neither disarm an armed channel nor arm a
8831
+ * disarmed one (D49).
8832
+ *
8833
+ * ## The canonical call shape
8834
+ *
8835
+ * ```ts
8836
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
8837
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
8838
+ * }
8839
+ * ```
8840
+ *
8841
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
8842
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
8843
+ * object literal is never constructed because it lives inside the branch. It
8844
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
8845
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
8846
+ * destination floor (measured at 1.93 ns/call when off).
8847
+ *
8848
+ * ## Why a channel emits at `info`
8849
+ *
8850
+ * `loki-logging.addon.ts` pins the destination default at `info` and
8851
+ * `loki-destination.ts` drops everything below it, so a line emitted at
8852
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
8853
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
8854
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
8855
+ * emits at the channel's declared level, whose schema floor is `info`.
8856
+ */
8857
+ /**
8858
+ * The level a channel writes at once armed.
8859
+ *
8860
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
8861
+ * not leave the process for Loki, and the whole point of arming a channel is
8862
+ * to read it later.
8863
+ */
8864
+ var LogChannelLevelSchema = _enum([
8865
+ "info",
8866
+ "warn",
8867
+ "error"
8868
+ ]);
8869
+ /**
8870
+ * What an addon declares about one channel. No value, no state — a
8871
+ * declaration is inert.
8872
+ */
8873
+ var LogChannelDescriptorSchema = object({
8874
+ /**
8875
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
8876
+ * the addon's short name so an operator reading a channel list can tell who
8877
+ * owns it without a second lookup.
8878
+ */
8879
+ name: string$2().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
8880
+ /** One sentence: what the operator will SEE after arming it. */
8881
+ description: string$2().min(1),
8882
+ /** The level its lines are emitted at. Never below `info`. */
8883
+ defaultLevel: LogChannelLevelSchema,
8884
+ /**
8885
+ * Whether this channel can be narrowed to a camera.
8886
+ *
8887
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
8888
+ * consulted with the numeric device id, AND every line the channel admits
8889
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
8890
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
8891
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
8892
+ * the body is the only way to filter.
8893
+ *
8894
+ * A channel whose lines carry the device only in `meta` (or not at all) is
8895
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
8896
+ * the operator narrows to one camera, sees nothing, and concludes the code
8897
+ * path was never taken.
8898
+ */
8899
+ perDevice: boolean()
8900
+ });
8901
+ /**
8902
+ * An armed window over one channel, as the document hands it to a mirror.
8903
+ *
8904
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
8905
+ * expires by itself, which is the one failure a boolean cannot avoid.
8906
+ */
8907
+ var LogChannelWindowSchema = object({
8908
+ channel: string$2().min(1),
8909
+ /** Epoch ms the window closes at. */
8910
+ armedUntilMs: number(),
8911
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
8912
+ deviceIds: array(number().int()).readonly().nullable()
8913
+ });
8910
8914
  var MODEL_FORMATS = [
8911
8915
  "onnx",
8912
8916
  "coreml",
@@ -10563,12 +10567,30 @@ var BackupDestinationInfoSchema = object({
10563
10567
  lastSuccessAt: number().optional(),
10564
10568
  /** Newest-archive size from `manifests.json`, or undefined. */
10565
10569
  lastSuccessSizeBytes: number().optional(),
10566
- /** Per-destination cron expression. Empty = manual-only (no schedule). */
10570
+ /**
10571
+ * Cron cadence(s) of the ENABLED schedules that fan out to this
10572
+ * destination, comma-joined. Absent when no enabled schedule targets it
10573
+ * — a destination nothing is scheduled to write to must not advertise a
10574
+ * cadence (D384). This is never the `backup_destination_policies.cron`
10575
+ * column: that per-location cron has scheduled nothing since 2026-07-28
10576
+ * and reading it made a destination with a DISABLED schedule claim a
10577
+ * nightly run.
10578
+ */
10567
10579
  cron: string$2().optional(),
10568
- /** ms-epoch of next computed firing for this destination's cron, if any. */
10580
+ /** ms-epoch of the next firing across those schedules (earliest), if any. */
10569
10581
  nextRunAt: number().optional(),
10570
- /** ms-epoch of last successful scheduled run (mirrors policy.lastRunAt). */
10571
- lastRunAt: number().optional()
10582
+ /**
10583
+ * ms-epoch of the last time a run ATTEMPTED to write here — success or
10584
+ * failure. Never a success stamp: pair it with `lastSuccessAt` (the
10585
+ * newest archive that actually landed) and `lastError`.
10586
+ */
10587
+ lastAttemptAt: number().optional(),
10588
+ /**
10589
+ * Why the last attempt failed, verbatim. Absent when the last attempt
10590
+ * landed the archive. A destination that has never been written to has
10591
+ * neither this nor `lastAttemptAt`.
10592
+ */
10593
+ lastError: string$2().optional()
10572
10594
  });
10573
10595
  /**
10574
10596
  * Per-archive entry returned by `backup.listArchives({ destinationId })`.
@@ -10731,8 +10753,16 @@ var BackupScheduleSchema = object({
10731
10753
  retentionCount: number().int().min(1).max(1e3),
10732
10754
  /** Optional subset of source locations to include; omitted = all. */
10733
10755
  dataSources: array(string$2()).readonly().optional(),
10734
- /** ms-epoch of last successful run. */
10735
- lastRunAt: number().optional(),
10756
+ /**
10757
+ * ms-epoch of the last tick that FIRED this schedule. Stamped before the
10758
+ * archive runs (it is the dedupe anchor), so it says "attempted", never
10759
+ * "succeeded" — a run refused by every destination stamps it too.
10760
+ */
10761
+ lastAttemptAt: number().optional(),
10762
+ /** ms-epoch of the last run of this schedule that landed at ≥1 destination. */
10763
+ lastSuccessAt: number().optional(),
10764
+ /** Why the last fired run failed, verbatim. Absent when it succeeded. */
10765
+ lastError: string$2().optional(),
10736
10766
  /** ms-epoch of next computed firing (read-only, filled on list). */
10737
10767
  nextRunAt: number().optional()
10738
10768
  });
@@ -10781,14 +10811,7 @@ method(_void(), array(BackupDestinationInfoSchema).readonly(), { auth: "admin" }
10781
10811
  locationId: string$2(),
10782
10812
  enabled: boolean(),
10783
10813
  retentionCount: number().int().min(1).max(1e3),
10784
- label: string$2().optional(),
10785
- /**
10786
- * Per-destination cron expression. Empty string clears the
10787
- * schedule (manual-only). Validated server-side via croner;
10788
- * malformed expressions reject the upsert with an actionable
10789
- * message.
10790
- */
10791
- cron: string$2().optional()
10814
+ label: string$2().optional()
10792
10815
  }), _void(), {
10793
10816
  kind: "mutation",
10794
10817
  auth: "admin"
@@ -21986,7 +22009,7 @@ method(object({
21986
22009
  downloadId: string$2(),
21987
22010
  offset: number(),
21988
22011
  length: number()
21989
- }), _instanceof(Uint8Array)), method(object({ downloadId: string$2() }), _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({
22012
+ }), _instanceof(Uint8Array)), method(object({ downloadId: string$2() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
21990
22013
  createdAt: true,
21991
22014
  updatedAt: true
21992
22015
  }), StorageLocationSchema, {
@@ -38848,12 +38871,6 @@ Object.freeze({
38848
38871
  addonId: null,
38849
38872
  access: "view"
38850
38873
  },
38851
- "storage.getDefaultLocation": {
38852
- capName: "storage",
38853
- capScope: "system",
38854
- addonId: null,
38855
- access: "view"
38856
- },
38857
38874
  "storage.list": {
38858
38875
  capName: "storage",
38859
38876
  capScope: "system",
package/dist/addon.mjs CHANGED
@@ -7627,111 +7627,6 @@ var CameraSwitchGroupSchema = object({
7627
7627
  fetchedAt: number()
7628
7628
  });
7629
7629
  /**
7630
- * Per-component log CHANNELS — the gate a hot path consults, and the registry
7631
- * an addon declares its channels in.
7632
- *
7633
- * ## Two axes, deliberately separated
7634
- *
7635
- * - **DECLARATION** — which channels exist. Only the addon knows:
7636
- * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7637
- * baichuan/handshake. A hand-wired central list rots at the first addition,
7638
- * and rots silently. So a channel is declared where it is consulted, and the
7639
- * `log-channels` capability enumerates the declarations.
7640
- * - **VALUE** — at which level, for which scope, until when. That stays ONE
7641
- * thing: the logging settings document on the `system` cap. Two authorities
7642
- * over the values is the exact defect
7643
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7644
- * remove; re-introducing it from the cure side would be grotesque.
7645
- *
7646
- * Nothing in this file reads a clock, an env var or a store. The registry is
7647
- * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7648
- * the hot path with a value somebody actually read, and by
7649
- * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7650
- * never reaches here, so it can neither disarm an armed channel nor arm a
7651
- * disarmed one (D49).
7652
- *
7653
- * ## The canonical call shape
7654
- *
7655
- * ```ts
7656
- * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7657
- * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7658
- * }
7659
- * ```
7660
- *
7661
- * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7662
- * read. Disarmed, a call site costs one load and one branch, and the `extras`
7663
- * object literal is never constructed because it lives inside the branch. It
7664
- * is the same shape already proven in production at `stream-broker.ts:1650`,
7665
- * and the same discipline `LoggingGate.allowsDestination` uses for the
7666
- * destination floor (measured at 1.93 ns/call when off).
7667
- *
7668
- * ## Why a channel emits at `info`
7669
- *
7670
- * `loki-logging.addon.ts` pins the destination default at `info` and
7671
- * `loki-destination.ts` drops everything below it, so a line emitted at
7672
- * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7673
- * minutes. A diagnostic that cannot be read an hour later is worse than no
7674
- * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7675
- * emits at the channel's declared level, whose schema floor is `info`.
7676
- */
7677
- /**
7678
- * The level a channel writes at once armed.
7679
- *
7680
- * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7681
- * not leave the process for Loki, and the whole point of arming a channel is
7682
- * to read it later.
7683
- */
7684
- var LogChannelLevelSchema = _enum([
7685
- "info",
7686
- "warn",
7687
- "error"
7688
- ]);
7689
- /**
7690
- * What an addon declares about one channel. No value, no state — a
7691
- * declaration is inert.
7692
- */
7693
- var LogChannelDescriptorSchema = object({
7694
- /**
7695
- * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7696
- * the addon's short name so an operator reading a channel list can tell who
7697
- * owns it without a second lookup.
7698
- */
7699
- name: string$2().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7700
- /** One sentence: what the operator will SEE after arming it. */
7701
- description: string$2().min(1),
7702
- /** The level its lines are emitted at. Never below `info`. */
7703
- defaultLevel: LogChannelLevelSchema,
7704
- /**
7705
- * Whether this channel can be narrowed to a camera.
7706
- *
7707
- * `true` is a PROMISE with two halves, and both must hold: the gate is
7708
- * consulted with the numeric device id, AND every line the channel admits
7709
- * carries `tags: { deviceId }` with that same numeric id. The second half is
7710
- * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7711
- * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7712
- * the body is the only way to filter.
7713
- *
7714
- * A channel whose lines carry the device only in `meta` (or not at all) is
7715
- * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7716
- * the operator narrows to one camera, sees nothing, and concludes the code
7717
- * path was never taken.
7718
- */
7719
- perDevice: boolean()
7720
- });
7721
- /**
7722
- * An armed window over one channel, as the document hands it to a mirror.
7723
- *
7724
- * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7725
- * expires by itself, which is the one failure a boolean cannot avoid.
7726
- */
7727
- var LogChannelWindowSchema = object({
7728
- channel: string$2().min(1),
7729
- /** Epoch ms the window closes at. */
7730
- armedUntilMs: number(),
7731
- /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7732
- deviceIds: array(number().int()).readonly().nullable()
7733
- });
7734
- /**
7735
7630
  * Ops-log — the durable, append-only operations audit shared by the
7736
7631
  * recordings and events management surfaces.
7737
7632
  *
@@ -8677,8 +8572,11 @@ var StorageLocationTypeSchema = string$2().regex(/^[a-z][a-zA-Z0-9-]*$/);
8677
8572
  * `STORAGE_LOCATION_CARDINALITY` map has been removed.
8678
8573
  *
8679
8574
  * `id` is a stable namespaced string of the form `<type>:<slug>`.
8680
- * The default location for a type uses `id === <type>:default` by
8681
- * convention (the bare type ref like `'backups'` resolves to it).
8575
+ * The seed names its first instance `<type>:default` — a NAME, not a flag.
8576
+ * There is no default location any more (D383): `enabled` is the whole write
8577
+ * model, and a bare type ref resolves to the sole location of the type, or —
8578
+ * transitionally, only while legacy NULL-stamped rows exist — to the row whose
8579
+ * slug is `default`.
8682
8580
  *
8683
8581
  * `isSystem` is a legacy persisted flag. Seed still creates the initial
8684
8582
  * `<type>:default` locations; the flag is no longer a lock, a badge, or a
@@ -8699,21 +8597,20 @@ var StorageLocationSchema = object({
8699
8597
  * flag at upsert time, not here (the schema is provider-agnostic).
8700
8598
  */
8701
8599
  nodeId: string$2().optional(),
8702
- isDefault: boolean().default(false),
8703
8600
  isSystem: boolean().default(false),
8704
8601
  /**
8705
- * Operator opt-in: whether consumers that BALANCE across several locations
8706
- * of a type may write here. Recordings reads it today; event media and
8707
- * backups are the next consumers, which is why the flag lives on the
8708
- * location rather than in any one addon's store — nothing has to be
8709
- * extended to add the next consumer.
8602
+ * THE write switch, and the only one (D383). `enabled: true` means every
8603
+ * consumer that chooses a write target for this type may write here, and all
8604
+ * enabled locations of a type are used TOGETHER; `false` means read-only —
8605
+ * still read, still played back, still age-swept, still drained, never
8606
+ * written.
8710
8607
  *
8711
- * OPTIONAL, and ABSENT MEANS ACTIVE. Every location persisted before the
8712
- * flag existed reads back with no flag and keeps working exactly as before;
8713
- * that is the whole compat story, and it is why no migration ships with it.
8714
- * A newly CREATED sibling is stamped `false` by the orchestrator (creating a
8715
- * disk must not silently start writing to it); the default of a type is
8716
- * always stamped `true`.
8608
+ * OPTIONAL only for the wire: an upsert that omits it means "leave what is
8609
+ * stored" on an update and "born inert unless it is the first location of its
8610
+ * type" on a create. On a PERSISTED row absence is legacy and it means
8611
+ * enabled — {@link isLocationEnabled} is the one place that says so, and the
8612
+ * orchestrator stamps every flagless row `true` once at hydrate so absence
8613
+ * stops existing rather than being re-derived on every read.
8717
8614
  */
8718
8615
  enabled: boolean().optional(),
8719
8616
  /** COMPUTED at read time by the orchestrator (statfs of the backing volume
@@ -8726,10 +8623,12 @@ var StorageLocationSchema = object({
8726
8623
  createdAt: number(),
8727
8624
  updatedAt: number()
8728
8625
  });
8626
+ object({ isDefault: boolean().optional() });
8729
8627
  /**
8730
8628
  * Reference accepted by consumer-facing `api.storage.*` calls.
8731
8629
  * Either:
8732
- * - a `StorageLocationType` (e.g. `'backups'`) → orchestrator resolves to the default of that type
8630
+ * - a `StorageLocationType` (e.g. `'backups'`) → the sole location of that type
8631
+ * (transitionally, the `<type>:default`-slugged row when several exist)
8733
8632
  * - a fully-qualified id (e.g. `'backups:nas-01'`) → addresses a specific instance
8734
8633
  *
8735
8634
  * The orchestrator's `resolveRef(ref)` handles both cases.
@@ -8905,6 +8804,111 @@ var DecoderSessionConfigSchema = object({
8905
8804
  */
8906
8805
  debug: boolean().optional()
8907
8806
  });
8807
+ /**
8808
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
8809
+ * an addon declares its channels in.
8810
+ *
8811
+ * ## Two axes, deliberately separated
8812
+ *
8813
+ * - **DECLARATION** — which channels exist. Only the addon knows:
8814
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
8815
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
8816
+ * and rots silently. So a channel is declared where it is consulted, and the
8817
+ * `log-channels` capability enumerates the declarations.
8818
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
8819
+ * thing: the logging settings document on the `system` cap. Two authorities
8820
+ * over the values is the exact defect
8821
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
8822
+ * remove; re-introducing it from the cure side would be grotesque.
8823
+ *
8824
+ * Nothing in this file reads a clock, an env var or a store. The registry is
8825
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
8826
+ * the hot path with a value somebody actually read, and by
8827
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
8828
+ * never reaches here, so it can neither disarm an armed channel nor arm a
8829
+ * disarmed one (D49).
8830
+ *
8831
+ * ## The canonical call shape
8832
+ *
8833
+ * ```ts
8834
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
8835
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
8836
+ * }
8837
+ * ```
8838
+ *
8839
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
8840
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
8841
+ * object literal is never constructed because it lives inside the branch. It
8842
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
8843
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
8844
+ * destination floor (measured at 1.93 ns/call when off).
8845
+ *
8846
+ * ## Why a channel emits at `info`
8847
+ *
8848
+ * `loki-logging.addon.ts` pins the destination default at `info` and
8849
+ * `loki-destination.ts` drops everything below it, so a line emitted at
8850
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
8851
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
8852
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
8853
+ * emits at the channel's declared level, whose schema floor is `info`.
8854
+ */
8855
+ /**
8856
+ * The level a channel writes at once armed.
8857
+ *
8858
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
8859
+ * not leave the process for Loki, and the whole point of arming a channel is
8860
+ * to read it later.
8861
+ */
8862
+ var LogChannelLevelSchema = _enum([
8863
+ "info",
8864
+ "warn",
8865
+ "error"
8866
+ ]);
8867
+ /**
8868
+ * What an addon declares about one channel. No value, no state — a
8869
+ * declaration is inert.
8870
+ */
8871
+ var LogChannelDescriptorSchema = object({
8872
+ /**
8873
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
8874
+ * the addon's short name so an operator reading a channel list can tell who
8875
+ * owns it without a second lookup.
8876
+ */
8877
+ name: string$2().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
8878
+ /** One sentence: what the operator will SEE after arming it. */
8879
+ description: string$2().min(1),
8880
+ /** The level its lines are emitted at. Never below `info`. */
8881
+ defaultLevel: LogChannelLevelSchema,
8882
+ /**
8883
+ * Whether this channel can be narrowed to a camera.
8884
+ *
8885
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
8886
+ * consulted with the numeric device id, AND every line the channel admits
8887
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
8888
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
8889
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
8890
+ * the body is the only way to filter.
8891
+ *
8892
+ * A channel whose lines carry the device only in `meta` (or not at all) is
8893
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
8894
+ * the operator narrows to one camera, sees nothing, and concludes the code
8895
+ * path was never taken.
8896
+ */
8897
+ perDevice: boolean()
8898
+ });
8899
+ /**
8900
+ * An armed window over one channel, as the document hands it to a mirror.
8901
+ *
8902
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
8903
+ * expires by itself, which is the one failure a boolean cannot avoid.
8904
+ */
8905
+ var LogChannelWindowSchema = object({
8906
+ channel: string$2().min(1),
8907
+ /** Epoch ms the window closes at. */
8908
+ armedUntilMs: number(),
8909
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
8910
+ deviceIds: array(number().int()).readonly().nullable()
8911
+ });
8908
8912
  var MODEL_FORMATS = [
8909
8913
  "onnx",
8910
8914
  "coreml",
@@ -10561,12 +10565,30 @@ var BackupDestinationInfoSchema = object({
10561
10565
  lastSuccessAt: number().optional(),
10562
10566
  /** Newest-archive size from `manifests.json`, or undefined. */
10563
10567
  lastSuccessSizeBytes: number().optional(),
10564
- /** Per-destination cron expression. Empty = manual-only (no schedule). */
10568
+ /**
10569
+ * Cron cadence(s) of the ENABLED schedules that fan out to this
10570
+ * destination, comma-joined. Absent when no enabled schedule targets it
10571
+ * — a destination nothing is scheduled to write to must not advertise a
10572
+ * cadence (D384). This is never the `backup_destination_policies.cron`
10573
+ * column: that per-location cron has scheduled nothing since 2026-07-28
10574
+ * and reading it made a destination with a DISABLED schedule claim a
10575
+ * nightly run.
10576
+ */
10565
10577
  cron: string$2().optional(),
10566
- /** ms-epoch of next computed firing for this destination's cron, if any. */
10578
+ /** ms-epoch of the next firing across those schedules (earliest), if any. */
10567
10579
  nextRunAt: number().optional(),
10568
- /** ms-epoch of last successful scheduled run (mirrors policy.lastRunAt). */
10569
- lastRunAt: number().optional()
10580
+ /**
10581
+ * ms-epoch of the last time a run ATTEMPTED to write here — success or
10582
+ * failure. Never a success stamp: pair it with `lastSuccessAt` (the
10583
+ * newest archive that actually landed) and `lastError`.
10584
+ */
10585
+ lastAttemptAt: number().optional(),
10586
+ /**
10587
+ * Why the last attempt failed, verbatim. Absent when the last attempt
10588
+ * landed the archive. A destination that has never been written to has
10589
+ * neither this nor `lastAttemptAt`.
10590
+ */
10591
+ lastError: string$2().optional()
10570
10592
  });
10571
10593
  /**
10572
10594
  * Per-archive entry returned by `backup.listArchives({ destinationId })`.
@@ -10729,8 +10751,16 @@ var BackupScheduleSchema = object({
10729
10751
  retentionCount: number().int().min(1).max(1e3),
10730
10752
  /** Optional subset of source locations to include; omitted = all. */
10731
10753
  dataSources: array(string$2()).readonly().optional(),
10732
- /** ms-epoch of last successful run. */
10733
- lastRunAt: number().optional(),
10754
+ /**
10755
+ * ms-epoch of the last tick that FIRED this schedule. Stamped before the
10756
+ * archive runs (it is the dedupe anchor), so it says "attempted", never
10757
+ * "succeeded" — a run refused by every destination stamps it too.
10758
+ */
10759
+ lastAttemptAt: number().optional(),
10760
+ /** ms-epoch of the last run of this schedule that landed at ≥1 destination. */
10761
+ lastSuccessAt: number().optional(),
10762
+ /** Why the last fired run failed, verbatim. Absent when it succeeded. */
10763
+ lastError: string$2().optional(),
10734
10764
  /** ms-epoch of next computed firing (read-only, filled on list). */
10735
10765
  nextRunAt: number().optional()
10736
10766
  });
@@ -10779,14 +10809,7 @@ method(_void(), array(BackupDestinationInfoSchema).readonly(), { auth: "admin" }
10779
10809
  locationId: string$2(),
10780
10810
  enabled: boolean(),
10781
10811
  retentionCount: number().int().min(1).max(1e3),
10782
- label: string$2().optional(),
10783
- /**
10784
- * Per-destination cron expression. Empty string clears the
10785
- * schedule (manual-only). Validated server-side via croner;
10786
- * malformed expressions reject the upsert with an actionable
10787
- * message.
10788
- */
10789
- cron: string$2().optional()
10812
+ label: string$2().optional()
10790
10813
  }), _void(), {
10791
10814
  kind: "mutation",
10792
10815
  auth: "admin"
@@ -21984,7 +22007,7 @@ method(object({
21984
22007
  downloadId: string$2(),
21985
22008
  offset: number(),
21986
22009
  length: number()
21987
- }), _instanceof(Uint8Array)), method(object({ downloadId: string$2() }), _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({
22010
+ }), _instanceof(Uint8Array)), method(object({ downloadId: string$2() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
21988
22011
  createdAt: true,
21989
22012
  updatedAt: true
21990
22013
  }), StorageLocationSchema, {
@@ -38846,12 +38869,6 @@ Object.freeze({
38846
38869
  addonId: null,
38847
38870
  access: "view"
38848
38871
  },
38849
- "storage.getDefaultLocation": {
38850
- capName: "storage",
38851
- capScope: "system",
38852
- addonId: null,
38853
- access: "view"
38854
- },
38855
38872
  "storage.list": {
38856
38873
  capName: "storage",
38857
38874
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-matter-broker",
3
- "version": "0.2.76",
3
+ "version": "0.2.78",
4
4
  "description": "Matter broker addon for CamStack — owns a Matter fabric (commissioning + the long-lived controller) via the matter.js controller and brokers commissioned Matter nodes into CamStack",
5
5
  "keywords": [
6
6
  "camstack",