@camstack/system 1.2.115 → 1.2.117

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 (62) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  9. package/dist/builtins/console-logging/index.js +1 -1
  10. package/dist/builtins/console-logging/index.mjs +1 -1
  11. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  12. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  14. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  15. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  17. package/dist/builtins/hub-forwarder/index.js +1 -1
  18. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  19. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  20. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  21. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  22. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  23. package/dist/builtins/local-network/ipv6-lan.d.ts +26 -0
  24. package/dist/builtins/local-network/local-hostname.d.ts +15 -0
  25. package/dist/builtins/local-network/local-network.addon.d.ts +59 -21
  26. package/dist/builtins/local-network/local-network.addon.js +369 -50
  27. package/dist/builtins/local-network/local-network.addon.mjs +365 -51
  28. package/dist/builtins/loki-logging/index.js +1 -1
  29. package/dist/builtins/loki-logging/index.mjs +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  31. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  32. package/dist/builtins/platform-probe/index.js +1 -1
  33. package/dist/builtins/platform-probe/index.mjs +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  35. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  36. package/dist/builtins/snapshot/index.js +1 -1
  37. package/dist/builtins/snapshot/index.mjs +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  39. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
  41. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  44. package/dist/builtins/system-config/system-config.addon.js +1 -1
  45. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  46. package/dist/builtins/winston-logging/index.js +1 -1
  47. package/dist/builtins/winston-logging/index.mjs +1 -1
  48. package/dist/{dist-Drb54SZD.mjs → dist-D4Vmj8iw.mjs} +151 -9
  49. package/dist/{dist-iAwSA2_f.js → dist-DMEGlqg6.js} +151 -9
  50. package/dist/http/lan-http-bind.d.ts +30 -0
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.js +30 -472
  53. package/dist/index.mjs +11 -468
  54. package/dist/lan-http-bind-DmgpFP6_.js +890 -0
  55. package/dist/lan-http-bind-jKrj6OjQ.mjs +752 -0
  56. package/dist/{retired-settings-keys-Dz5-7gjt.mjs → retired-settings-keys--aM_vGF9.mjs} +1 -1
  57. package/dist/{retired-settings-keys-OfhqQio4.js → retired-settings-keys-DET4YkAU.js} +1 -1
  58. package/dist/tls/cert-manager.d.ts +11 -0
  59. package/dist/tls/index.d.ts +3 -1
  60. package/dist/tls/tls-status.d.ts +16 -0
  61. package/dist/tls/uploaded-cert.d.ts +14 -0
  62. package/package.json +1 -1
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-iAwSA2_f.js");
6
+ const require_dist = require("../../dist-DMEGlqg6.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Gt as EventCategory, v as addonPagesCapability, vt as errMsg, yt as BaseAddon } from "../../dist-Drb54SZD.mjs";
1
+ import { Gt as EventCategory, v as addonPagesCapability, vt as errMsg, yt as BaseAddon } from "../../dist-D4Vmj8iw.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-iAwSA2_f.js");
6
+ const require_dist = require("../../dist-DMEGlqg6.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Gt as EventCategory, b as addonWidgetsCapability, vt as errMsg, yt as BaseAddon } from "../../dist-Drb54SZD.mjs";
1
+ import { Gt as EventCategory, b as addonWidgetsCapability, vt as errMsg, yt as BaseAddon } from "../../dist-D4Vmj8iw.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-iAwSA2_f.js");
6
+ const require_dist = require("../../dist-DMEGlqg6.js");
7
7
  //#region src/builtins/alerts/prune-policy.ts
8
8
  /**
9
9
  * Ids older than `cutoffMs`, oldest first.
@@ -1,4 +1,4 @@
1
- import { Gt as EventCategory, Mt as createEvent, n as AlertSchema, vt as errMsg, x as alertsCapability, yt as BaseAddon } from "../../dist-Drb54SZD.mjs";
1
+ import { Gt as EventCategory, Mt as createEvent, n as AlertSchema, vt as errMsg, x as alertsCapability, yt as BaseAddon } from "../../dist-D4Vmj8iw.mjs";
2
2
  //#region src/builtins/alerts/prune-policy.ts
3
3
  /**
4
4
  * Ids older than `cutoffMs`, oldest first.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-iAwSA2_f.js");
6
+ const require_dist = require("../../dist-DMEGlqg6.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let buffer = require("buffer");
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -1,4 +1,4 @@
1
- import { C as backupCapability, Gt as EventCategory, yt as BaseAddon } from "../../dist-Drb54SZD.mjs";
1
+ import { C as backupCapability, Gt as EventCategory, yt as BaseAddon } from "../../dist-D4Vmj8iw.mjs";
2
2
  import { randomBytes, randomUUID } from "node:crypto";
3
3
  import { Buffer as Buffer$1 } from "buffer";
4
4
  import * as fsp from "node:fs/promises";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-iAwSA2_f.js");
6
+ const require_dist = require("../../dist-DMEGlqg6.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/console-logging/console-destination.ts
9
9
  var LEVEL_RANK = {
@@ -1,4 +1,4 @@
1
- import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-Drb54SZD.mjs";
1
+ import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-D4Vmj8iw.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/console-logging/console-destination.ts
4
4
  var LEVEL_RANK = {
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-iAwSA2_f.js");
3
+ const require_dist = require("../../dist-DMEGlqg6.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,4 +1,4 @@
1
- import { D as coreBlockAddonId, O as coreBlockIdFromAddonId, c as CoreBlockSchema, k as coreBlocksCapability, l as DeclaredDevices, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, vt as errMsg, yt as BaseAddon } from "../../dist-Drb54SZD.mjs";
1
+ import { D as coreBlockAddonId, O as coreBlockIdFromAddonId, c as CoreBlockSchema, k as coreBlocksCapability, l as DeclaredDevices, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, vt as errMsg, yt as BaseAddon } from "../../dist-D4Vmj8iw.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-iAwSA2_f.js");
7
- const require_retired_settings_keys = require("../../retired-settings-keys-OfhqQio4.js");
6
+ const require_dist = require("../../dist-DMEGlqg6.js");
7
+ const require_retired_settings_keys = require("../../retired-settings-keys-DET4YkAU.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let _camstack_types_node = require("@camstack/types/node");
10
10
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -1,5 +1,5 @@
1
- import { Ct as DeviceFeature, E as buildStreamParamsConfigSchema, F as deviceStatusCapability, Gt as EventCategory, Lt as isDeviceConfigCap, N as deviceManagerCapability, Ot as WELL_KNOWN_TAB_MAP, P as deviceStateCapability, R as enumerateItemArrayFields, Tt as DeviceType, Wt as sleep, a as CAP_NAMES_WITH_STATUS, at as runtimeStatePolicyFor, et as normalizeUnit, nt as parseStreamParamsFormPatch, p as STREAM_PROFILE_META, t as ALL_CAPABILITY_DEFINITIONS, u as DeviceStatusSchema, vt as errMsg, wt as DeviceRole, yt as BaseAddon, z as enumerateSchemaFields } from "../../dist-Drb54SZD.mjs";
2
- import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-Dz5-7gjt.mjs";
1
+ import { Ct as DeviceFeature, E as buildStreamParamsConfigSchema, F as deviceStatusCapability, Gt as EventCategory, Lt as isDeviceConfigCap, N as deviceManagerCapability, Ot as WELL_KNOWN_TAB_MAP, P as deviceStateCapability, R as enumerateItemArrayFields, Tt as DeviceType, Wt as sleep, a as CAP_NAMES_WITH_STATUS, at as runtimeStatePolicyFor, et as normalizeUnit, nt as parseStreamParamsFormPatch, p as STREAM_PROFILE_META, t as ALL_CAPABILITY_DEFINITIONS, u as DeviceStatusSchema, vt as errMsg, wt as DeviceRole, yt as BaseAddon, z as enumerateSchemaFields } from "../../dist-D4Vmj8iw.mjs";
2
+ import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys--aM_vGF9.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
5
5
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-iAwSA2_f.js");
6
+ const require_dist = require("../../dist-DMEGlqg6.js");
7
7
  //#region src/builtins/doorbell/trigger-engine.ts
8
8
  /**
9
9
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -1,4 +1,4 @@
1
- import { G as isSameAddonId, Gt as EventCategory, I as doorbellCapability, Mt as createEvent, Tt as DeviceType, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-Drb54SZD.mjs";
1
+ import { G as isSameAddonId, Gt as EventCategory, I as doorbellCapability, Mt as createEvent, Tt as DeviceType, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-D4Vmj8iw.mjs";
2
2
  //#region src/builtins/doorbell/trigger-engine.ts
3
3
  /**
4
4
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-iAwSA2_f.js");
6
+ const require_dist = require("../../dist-DMEGlqg6.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
9
9
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,4 +1,4 @@
1
- import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-Drb54SZD.mjs";
1
+ import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-D4Vmj8iw.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
4
4
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-iAwSA2_f.js");
3
+ const require_dist = require("../../dist-DMEGlqg6.js");
4
4
  //#region src/builtins/liveness-monitor/liveness-checks.ts
5
5
  var NO_DEVICES = "liveness:no-devices";
6
6
  var ALL_OFFLINE = "liveness:all-devices-offline";
@@ -1,4 +1,4 @@
1
- import { Gt as EventCategory, Mt as createEvent, vt as errMsg, yt as BaseAddon } from "../../dist-Drb54SZD.mjs";
1
+ import { Gt as EventCategory, Mt as createEvent, vt as errMsg, yt as BaseAddon } from "../../dist-D4Vmj8iw.mjs";
2
2
  //#region src/builtins/liveness-monitor/liveness-checks.ts
3
3
  var NO_DEVICES = "liveness:no-devices";
4
4
  var ALL_OFFLINE = "liveness:all-devices-offline";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-iAwSA2_f.js");
6
+ const require_dist = require("../../dist-DMEGlqg6.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  node_crypto = require_chunk.__toESM(node_crypto);
9
9
  let crypto$1 = require("crypto");
@@ -1,5 +1,5 @@
1
1
  import { i as __require, o as __toESM, t as __commonJSMin } from "../../chunk-CNf5ZN-e.mjs";
2
- import { S as authProviderCapability, _ as UserRecordSchema, ht as userManagementCapability, m as ScopedTokenSchema, r as ApiKeyRecordSchema, yt as BaseAddon } from "../../dist-Drb54SZD.mjs";
2
+ import { S as authProviderCapability, _ as UserRecordSchema, ht as userManagementCapability, m as ScopedTokenSchema, r as ApiKeyRecordSchema, yt as BaseAddon } from "../../dist-D4Vmj8iw.mjs";
3
3
  import * as crypto$2 from "node:crypto";
4
4
  import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
5
5
  import nodeCrypto from "crypto";
@@ -0,0 +1,26 @@
1
+ /**
2
+ * IPv6 helpers for LAN advertisement: prefix matching (privacy addresses
3
+ * rotate inside a /64), Linux `/proc/net/if_inet6` flags, and the gate
4
+ * that keeps temporary GUA off the connection candidate list.
5
+ */
6
+ /** First 64 bits, canonical hex groups, or null if the address is not IPv6. */
7
+ export declare function ipv6Prefix64Key(address: string): string | null;
8
+ /** 32-char lowercase hex (same encoding as `/proc/net/if_inet6`). */
9
+ export declare function ipv6ToHex32(address: string): string | null;
10
+ /**
11
+ * Parse Linux `if_inet6` into hex32 → flags. Unknown / malformed lines
12
+ * are skipped. Keys match {@link ipv6ToHex32}.
13
+ */
14
+ export declare function parseProcIfInet6(text: string): ReadonlyMap<string, number>;
15
+ /**
16
+ * Whether this IPv6 should be auto-seeded / advertised.
17
+ * `flags === undefined` means the platform did not tell us (macOS, tests
18
+ * without a proc snapshot) — keep every non-link-local plausible address.
19
+ *
20
+ * On Linux, drop addresses marked TEMPORARY / DEPRECATED / TENTATIVE, and
21
+ * `flags === 0` which is how this kernel reports RFC 4941 privacy temps
22
+ * (the stable GUA is PERMANENT|NODAD). Do **not** require PERMANENT —
23
+ * stateful DHCPv6 GUA often has only NODAD (0x02).
24
+ */
25
+ export declare function shouldAdvertiseIpv6(address: string, flags: number | undefined): boolean;
26
+ export declare function ipv6AllowlistMatches(address: string, allowed: readonly string[]): boolean;
@@ -0,0 +1,15 @@
1
+ export type ParseLocalHostnameResult = {
2
+ readonly ok: true;
3
+ readonly value: string | null;
4
+ } | {
5
+ readonly ok: false;
6
+ readonly error: string;
7
+ };
8
+ /**
9
+ * Parse the operator-typed local hostname.
10
+ *
11
+ * Empty / whitespace → `null` (advertise the raw LAN IP). A DNS name is
12
+ * lowercased. A URL, port suffix, path, or bare IP is rejected so it cannot
13
+ * leak into a SAN or a candidate URL.
14
+ */
15
+ export declare function parseLocalHostname(raw: string): ParseLocalHostnameResult;
@@ -10,6 +10,8 @@ interface MeshEndpointCache {
10
10
  /** Hub HTTPS port the mesh endpoint targets. */
11
11
  readonly port: number;
12
12
  }
13
+ /** Drop rotated IPv6 / dead extras from the persisted allowlist. */
14
+ export declare const ALLOWLIST_RECONCILE_INTERVAL_MS: number;
13
15
  /** The three places a hub listen port can be learned, most authoritative first. */
14
16
  export interface ListenPortSources {
15
17
  /** `server.port` from the hub's live config section; null when unreadable. */
@@ -37,9 +39,17 @@ interface LocalNetworkConfig {
37
39
  * different address after a reboot.
38
40
  */
39
41
  readonly notificationBaseUrl: string;
42
+ /** LAN HTTP listener. Default true — native apps skip the private cert. */
43
+ readonly httpEnabled: boolean;
44
+ /** Dedicated HTTP port. Must not equal the HTTPS listen port. */
45
+ readonly httpPort: number;
46
+ /** Operator-declared DNS name advertised instead of the raw LAN IP. Empty = none. */
47
+ readonly localHostname: string;
40
48
  }
41
49
  export declare class LocalNetworkAddon extends BaseAddon<LocalNetworkConfig> {
42
50
  private pollTimer;
51
+ private allowlistTimer;
52
+ private allowlistReconcilePending;
43
53
  private lastSnapshotKey;
44
54
  /** Optional public hostname tracked from `NetworkTunnelStarted`/
45
55
  * `Stopped` events on the bus. */
@@ -61,6 +71,8 @@ export declare class LocalNetworkAddon extends BaseAddon<LocalNetworkConfig> {
61
71
  */
62
72
  private resolveNotificationEndpoint;
63
73
  protected onInitialize(): Promise<ProviderRegistration[]>;
74
+ protected globalSettingsSchema(): import('@camstack/types').ConfigUISchema;
75
+ protected onConfigChanged(): Promise<void>;
64
76
  protected onShutdown(): Promise<void>;
65
77
  /**
66
78
  * Other hub addons (e.g. cloudflare-tunnel) signal the active public
@@ -86,6 +98,15 @@ export declare class LocalNetworkAddon extends BaseAddon<LocalNetworkConfig> {
86
98
  * not joined simply contributes nothing.
87
99
  */
88
100
  private reconcileMeshEndpoint;
101
+ private resolvedHttpPort;
102
+ private tlsDataDir;
103
+ private snapshotTls;
104
+ /**
105
+ * Bind or unbind the LAN HTTP sibling from the addon's own settings, and
106
+ * keep the leaf SAN in sync with `localHostname`. This is the settings
107
+ * side-effect path — there is no second persist API.
108
+ */
109
+ private applyLanAccessFromSettings;
89
110
  /**
90
111
  * The port this hub is actually listening on, plus the provenance of the
91
112
  * number. The boot loader merges the `CAMSTACK_PORT` env override into the
@@ -98,6 +119,11 @@ export declare class LocalNetworkAddon extends BaseAddon<LocalNetworkConfig> {
98
119
  /** `server.port` from the live config section, or null when unreadable. */
99
120
  private readConfiguredPort;
100
121
  private enumerate;
122
+ /**
123
+ * Replace rotated IPv6 and drop addresses that no longer exist, without
124
+ * clearing operator extras that are still live (docker/vpn opt-in).
125
+ */
126
+ private persistReconciledAllowlist;
101
127
  private detectChanges;
102
128
  }
103
129
  /**
@@ -125,12 +151,14 @@ interface OsNetworkInterfaceInfo {
125
151
  readonly internal: boolean;
126
152
  readonly mac: string;
127
153
  }
128
- export declare function enumerateOsInterfaces(ifaces: NodeJS.Dict<readonly OsNetworkInterfaceInfo[]>): readonly LocalInterface[];
154
+ export declare function enumerateOsInterfaces(ifaces: NodeJS.Dict<readonly OsNetworkInterfaceInfo[]>, ipv6Flags?: ReadonlyMap<string, number>): readonly LocalInterface[];
129
155
  /**
130
156
  * Filter the interface list by the operator's allowlist. Empty
131
157
  * allowlist = no-op (every interface passes); otherwise only entries
132
- * whose `address` matches an allowlist entry remain. Loopback always
133
- * survives so the SDK keeps `127.0.0.1` as the last-resort fallback.
158
+ * whose `address` matches an allowlist entry remain. IPv6 matches the
159
+ * allowlisted /64 so a rotated privacy address does not hide the live
160
+ * GUA. Loopback always survives so the SDK keeps `127.0.0.1` as the
161
+ * last-resort fallback.
134
162
  */
135
163
  export declare function applyAllowlist(interfaces: readonly LocalInterface[], allowed: readonly string[]): readonly LocalInterface[];
136
164
  /**
@@ -140,37 +168,41 @@ export declare function applyAllowlist(interfaces: readonly LocalInterface[], al
140
168
  export declare function pickPreferred(interfaces: readonly LocalInterface[]): LocalInterface | null;
141
169
  /**
142
170
  * Build the ordered candidate URL list. Priority schema:
143
- * 0 — preferred LAN IPv4
144
- * 10+ — other LAN IPv4
171
+ * 0 — preferred LAN IPv4 (or local hostname)
172
+ * 10+ — other LAN IPv4 and stable LAN IPv6
145
173
  * 100 — public tunnel hostname (always HTTPS)
146
174
  * 150 — mesh endpoint (Tailscale MagicDNS / 100.x, always HTTPS)
147
- * 200+ — LAN IPv6
148
175
  * 1000 — loopback (last resort)
149
176
  *
150
177
  * The mesh endpoint ranks just below a true public funnel (a Funnel is
151
178
  * reachable by ANY client; the mesh needs the peer on the same tailnet)
152
- * but above the IPv6 / loopback fallbacks giving remote, mesh-joined
153
- * clients a working candidate when no public tunnel is up.
179
+ * but above loopback. LAN IPv6 sits in the LAN band with IPv4 so a
180
+ * dual-stack client can win the race on IPv6 when it is faster.
154
181
  *
155
- * `scheme` controls LAN + loopback URLs. Browsers running over HTTPS
156
- * block `http://` candidates as mixed content, so callers loaded over
157
- * HTTPS should pass `scheme: 'https'` even when probing a LAN IP — the
158
- * hub's cert manager already issues a SAN-multi cert covering local
159
- * interfaces. The public tunnel + mesh endpoint always emit `https://`
160
- * (the tunnel edge / the hub itself terminates TLS).
182
+ * `scheme` controls loopback URLs and, when LAN HTTP is off, LAN URLs.
183
+ * When {@link LanHttpAdvertisement} is enabled and listening, LAN (and
184
+ * the local-hostname alias) switch to `http://…:httpPort` so a native
185
+ * client can skip the private certificate. Public tunnel + mesh always
186
+ * emit `https://`. Loopback stays on `scheme`/`port` it is not a LAN
187
+ * candidate.
161
188
  */
162
- export declare function buildEndpoints(interfaces: readonly LocalInterface[], port: number, includeLoopback: boolean, ipv4Only: boolean, publicHostname: string, scheme?: 'http' | 'https', meshEndpoint?: MeshEndpointCache | null): ConnectionEndpoint[];
189
+ export interface LanHttpAdvertisement {
190
+ readonly enabled: boolean;
191
+ readonly listening: boolean;
192
+ readonly httpPort: number;
193
+ readonly httpsPort: number;
194
+ readonly localHostname: string | null;
195
+ }
196
+ export declare function buildEndpoints(interfaces: readonly LocalInterface[], port: number, includeLoopback: boolean, ipv4Only: boolean, publicHostname: string, scheme?: 'http' | 'https', meshEndpoint?: MeshEndpointCache | null, lanHttp?: LanHttpAdvertisement | null): ConnectionEndpoint[];
163
197
  /**
164
198
  * First-boot heuristic: which addresses should the allowlist start
165
199
  * with? Includes LAN + Wi-Fi IPv4 addresses + plausible IPv6:
166
200
  *
167
201
  * - **IPv4**: skip link-local (`169.254.*`), keep the rest.
168
202
  * - **IPv6**: skip link-local (`fe80::*`), unspecified, and
169
- * multicast. Keep ULAs (`fc00::/7` `fc??:` / `fd??:`) and Global
170
- * Unicast addresses (`2000::/3` `2???`/`3???`). Privacy-extension
171
- * temporary addresses get included by default; the operator can
172
- * prune them from the Network Addresses tab if the rotating IPs
173
- * become a nuisance.
203
+ * multicast. Keep ULAs (`fc00::/7`) and Global Unicast (`2000::/3`).
204
+ * Privacy-extension temporary addresses are skipped when Linux flags
205
+ * mark them non-permanent (`plausible === false`).
174
206
  *
175
207
  * Skips docker/vpn/loopback/other entirely — those stay opt-in.
176
208
  */
@@ -195,4 +227,10 @@ export declare function isPlausibleAutoSeed(address: string, family: 'IPv4' | 'I
195
227
  export declare function classifyKind(name: string, address: string, internal: boolean): LocalInterface['kind'];
196
228
  /** Convert an IPv4/IPv6 netmask string to its prefix length (CIDR). */
197
229
  export declare function prefixLen(netmask: string): number;
198
- export {};
230
+ /**
231
+ * Rewrite a persisted allowlist against the live interface set:
232
+ * current auto-seed (LAN/Wi-Fi, stable IPv6) plus still-live operator
233
+ * extras (docker/vpn). Empty stays empty — that is "unrestricted".
234
+ */
235
+ export declare function reconcileAllowlist(current: readonly string[], interfaces: readonly LocalInterface[]): string[];
236
+ export { ipv6Prefix64Key, parseProcIfInet6, shouldAdvertiseIpv6, } from './ipv6-lan.js';