@camstack/system 1.2.438 → 1.2.440

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 (120) hide show
  1. package/dist/addon-runner.js +743 -79
  2. package/dist/addon-runner.mjs +743 -79
  3. package/dist/addon-utils.js +1 -1
  4. package/dist/addon-utils.mjs +1 -1
  5. package/dist/{agent-https-BZIwS4xF.js → agent-https-DlpTMDqA.js} +252 -22
  6. package/dist/{agent-https-BPfHrI4c.mjs → agent-https-U0d4uvn2.mjs} +211 -23
  7. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  8. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  9. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  10. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  11. package/dist/builtins/alerts/alerts.addon.js +1 -1
  12. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  13. package/dist/builtins/autotrack/index.js +1 -1
  14. package/dist/builtins/autotrack/index.mjs +1 -1
  15. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  16. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  17. package/dist/builtins/calendars/calendars.addon.js +1 -1
  18. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  19. package/dist/builtins/camera-grid/index.js +1 -1
  20. package/dist/builtins/camera-grid/index.mjs +1 -1
  21. package/dist/builtins/composer/composer.addon.js +2 -2
  22. package/dist/builtins/composer/composer.addon.mjs +2 -2
  23. package/dist/builtins/console-logging/index.js +1 -1
  24. package/dist/builtins/console-logging/index.mjs +1 -1
  25. package/dist/builtins/core-blocks/block-settings-store.d.ts +1 -1
  26. package/dist/builtins/core-blocks/core-blocks.addon.js +77 -11
  27. package/dist/builtins/core-blocks/core-blocks.addon.mjs +77 -12
  28. package/dist/builtins/core-blocks/index.mjs +1 -1
  29. package/dist/builtins/device-manager/device-manager.addon.js +197 -60
  30. package/dist/builtins/device-manager/device-manager.addon.mjs +196 -61
  31. package/dist/builtins/device-manager/device-removal-hooks.d.ts +0 -11
  32. package/dist/builtins/device-manager/device-tombstone-store.d.ts +12 -2
  33. package/dist/builtins/hub-forwarder/index.js +1 -1
  34. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  35. package/dist/builtins/keychain/index.js +29 -11
  36. package/dist/builtins/keychain/index.mjs +29 -11
  37. package/dist/builtins/keychain/keychain-service.d.ts +8 -0
  38. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  39. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  40. package/dist/builtins/local-auth/local-auth.addon.js +2 -2
  41. package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
  42. package/dist/builtins/local-network/local-network.addon.js +7 -2
  43. package/dist/builtins/local-network/local-network.addon.mjs +7 -2
  44. package/dist/builtins/loki-logging/index.js +1 -1
  45. package/dist/builtins/loki-logging/index.mjs +1 -1
  46. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  47. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  48. package/dist/builtins/platform-probe/index.js +1 -1
  49. package/dist/builtins/platform-probe/index.mjs +1 -1
  50. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  51. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  52. package/dist/builtins/snapshot/index.js +1 -1
  53. package/dist/builtins/snapshot/index.mjs +1 -1
  54. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  55. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  56. package/dist/builtins/sqlite-storage/index.js +1 -1
  57. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  58. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +38 -7
  59. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +69 -3
  60. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +69 -3
  61. package/dist/builtins/sqlite-storage/storage-admin-provider.d.ts +31 -0
  62. package/dist/builtins/storage-orchestrator/hub-settings-forward.d.ts +3 -3
  63. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +4 -4
  64. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +4 -4
  65. package/dist/builtins/system-config/system-config.addon.js +4 -4
  66. package/dist/builtins/system-config/system-config.addon.mjs +285 -285
  67. package/dist/builtins/winston-logging/index.js +1 -1
  68. package/dist/builtins/winston-logging/index.mjs +1 -1
  69. package/dist/{composition-sources-DpHGCjdL.mjs → composition-sources-CS0D7imL.mjs} +1 -1
  70. package/dist/{composition-sources-DI3Z83vG.js → composition-sources-cBG8fBTu.js} +1 -1
  71. package/dist/{constants-DQK705Sb.mjs → constants-CIzPjw7L.mjs} +1 -1
  72. package/dist/{constants-wkcn5Ij3.js → constants-CRI3QzT4.js} +1 -1
  73. package/dist/{device-ca-DvIZjXIl.mjs → device-ca-DLKXYseS.mjs} +2 -2
  74. package/dist/{device-ca-BSvkXqyI.js → device-ca-DNHjts4_.js} +2 -2
  75. package/dist/{dist-Dc8GKC5H.mjs → dist-9fcsrFU3.mjs} +271 -32
  76. package/dist/{dist-GgG-7amw.js → dist-BKhlcYz6.js} +288 -31
  77. package/dist/file-host-worker.js +1 -1
  78. package/dist/file-host-worker.mjs +1 -1
  79. package/dist/{hub-mdns-ciao-CBLvq2oj.js → hub-mdns-ciao-3Zc9G9MS.js} +377 -69
  80. package/dist/{hub-mdns-ciao-DMlQTdPU.mjs → hub-mdns-ciao-Dy2YjRqC.mjs} +344 -72
  81. package/dist/index.js +372 -339
  82. package/dist/index.mjs +361 -341
  83. package/dist/kernel/addon-settings-store.d.ts +43 -0
  84. package/dist/kernel/config-manager.d.ts +0 -19
  85. package/dist/kernel/device-settings-store.d.ts +74 -0
  86. package/dist/kernel/legacy-blob-guard.d.ts +58 -0
  87. package/dist/kernel/moleculer/runner-addon-settings.d.ts +34 -0
  88. package/dist/kernel/moleculer/runner-device-settings.d.ts +39 -0
  89. package/dist/kernel/prefix-walk.d.ts +39 -0
  90. package/dist/kernel/sections-store.d.ts +20 -0
  91. package/dist/kernel/settings-door-view.d.ts +2 -2
  92. package/dist/kernel/settings-view-types.d.ts +5 -30
  93. package/dist/kernel/store-settings-view.d.ts +0 -3
  94. package/dist/{lib-NNxHSOIE.js → lib-8oZrELLm.js} +1 -1
  95. package/dist/{lib-BeGzVQcd.mjs → lib-DK38ompL.mjs} +1 -1
  96. package/dist/prefix-range-D_KueXbQ.js +27 -0
  97. package/dist/prefix-range-Dl0znowN.mjs +22 -0
  98. package/dist/prefix-walk-DAOBSyH1.mjs +88 -0
  99. package/dist/prefix-walk-Dv6MLZlM.js +99 -0
  100. package/dist/{restore-archive-CTRz8v9o.js → restore-archive-49aZwkp8.js} +1 -1
  101. package/dist/{restore-archive-DPg9duVY.mjs → restore-archive-DgvDecXM.mjs} +82 -82
  102. package/dist/{retired-settings-keys-Is0zO8gX.mjs → retired-settings-keys-Bhwe4ymb.mjs} +2 -2
  103. package/dist/{retired-settings-keys-Dr_4lfKu.js → retired-settings-keys-D_VwZiDt.js} +10 -4
  104. package/dist/tls/agent-https.d.ts +2 -2
  105. package/dist/tls/boot-tls.d.ts +6 -0
  106. package/dist/tls/cert-evaluation.d.ts +1 -1
  107. package/dist/tls/cert-issue.d.ts +5 -1
  108. package/dist/tls/cert-manager.d.ts +18 -0
  109. package/dist/tls/cert-name-constraints.d.ts +53 -0
  110. package/dist/tls/index.d.ts +5 -4
  111. package/dist/tls/tls-actions.d.ts +27 -7
  112. package/dist/tls/tls-status.d.ts +22 -0
  113. package/dist/tls/uploaded-cert.d.ts +36 -4
  114. package/dist/{wal-maintenance-Bx3lcVA7.mjs → wal-maintenance-BZGmF-3U.mjs} +0 -0
  115. package/dist/{wal-maintenance-mjB2i-VR.js → wal-maintenance-C041zn6V.js} +0 -0
  116. package/dist/{x509.es-r7Ht1thA.js → x509.es-C4cOo48M.js} +36 -0
  117. package/dist/{x509.es-CDvj67p1.mjs → x509.es-DK__ETIe.mjs} +1 -1
  118. package/package.json +7 -2
  119. package/dist/{custom-action-registry-Bf_YnagS.js → custom-action-registry-BKmoaqG0.js} +1 -1
  120. package/dist/{custom-action-registry-BQnbN0DP.mjs → custom-action-registry-CyUuhATc.mjs} +1 -1
@@ -17,6 +17,12 @@ export interface BootTls {
17
17
  * a certificate source that changed under the operator is never silent.
18
18
  */
19
19
  readonly uploadedMissing: boolean;
20
+ /**
21
+ * True when an UNCONSTRAINED local CA kept beside an uploaded certificate was replaced
22
+ * by a constrained one (D873). The served pair is untouched; agents' leaves chain to
23
+ * the new CA and clients that trusted the old one must re-trust. The caller logs it.
24
+ */
25
+ readonly dormantCaReplaced: boolean;
20
26
  }
21
27
  /**
22
28
  * Decide what the listener serves at boot. The admin UI (Network → Local
@@ -20,7 +20,7 @@ export declare const CA_RENEWAL_WINDOW_DAYS: number;
20
20
  * verbatim — an operator who trusted the old certificate by hand learns
21
21
  * from this string, not from a browser error.
22
22
  */
23
- export type CertRegenerationReason = "missing" | "unreadable" | "no-local-ca" | "ca-expiring" | "key-mismatch" | "expiring" | "missing-server-auth-eku" | "not-issued-by-local-ca" | "validity-too-long" | "san-coverage-gap";
23
+ export type CertRegenerationReason = "missing" | "unreadable" | "no-local-ca" | "ca-expiring" | "ca-unconstrained" | "key-mismatch" | "expiring" | "missing-server-auth-eku" | "not-issued-by-local-ca" | "validity-too-long" | "san-coverage-gap";
24
24
  export interface CertEvaluationInput {
25
25
  /** Contents of `camstack.crt` — leaf first, then the issuing CA. */
26
26
  readonly chainPem: string;
@@ -19,8 +19,12 @@ export interface CaSigner {
19
19
  * A local root: 10 years, `pathlen:0` so it can sign leaves and nothing
20
20
  * else, and **no EKU** — Apple rejects an anchor asserting
21
21
  * `anyExtendedKeyUsage`, and a root with no EKU constrains nothing.
22
+ *
23
+ * It carries a CRITICAL `nameConstraints` extension (D873): private address
24
+ * ranges and local-only DNS names, plus `hostnames` (the hub's own single-label
25
+ * names at creation). A leaked key therefore cannot vouch for a public site.
22
26
  */
23
- export declare function generateCa(now: Date): Promise<CaSigner>;
27
+ export declare function generateCa(now: Date, hostnames?: readonly string[]): Promise<CaSigner>;
24
28
  /** Re-open a root already on disk so a leaf can be issued under it. */
25
29
  export declare function loadCa(certPem: string, keyPem: string): Promise<CaSigner>;
26
30
  /**
@@ -27,6 +27,11 @@ export interface EnsureTlsResult {
27
27
  readonly validTo: string;
28
28
  readonly sans: readonly string[];
29
29
  /**
30
+ * SAN values the local CA's name constraints (D873) do not permit, left out of the
31
+ * leaf. Empty when everything the machine answers to is certifiable.
32
+ */
33
+ readonly droppedNames: readonly string[];
34
+ /**
30
35
  * Set when a reissue was needed and FAILED, and the previous material is
31
36
  * still being served. Never null-and-degraded silently: this is the line
32
37
  * that says the certificate is not what the rule wants it to be.
@@ -91,6 +96,19 @@ export declare function ensureTlsCert(dataDir: string, options?: CertOptions): P
91
96
  */
92
97
  export declare function reissueTlsLeaf(dataDir: string, options?: CertOptions): Promise<EnsureTlsResult>;
93
98
  /**
99
+ * Uploaded mode keeps a generated CA beside the upload (agents' leaves are still signed
100
+ * by it). When that CA is unconstrained, replace it alone (D873): the served pair is the
101
+ * operator's and is never touched. Returns whether the CA was replaced.
102
+ */
103
+ export declare function replaceUnconstrainedLocalCa(dataDir: string, options?: CertOptions): Promise<boolean>;
104
+ /** Marker path for the status reader and the retire step. */
105
+ export declare function caReplacementMarkerFile(dataDir: string): string;
106
+ /**
107
+ * Called once the HTTPS listener serves the leaf of the replacement CA: drop the retired
108
+ * unconstrained CA, key first. Idempotent; returns whether anything was removed.
109
+ */
110
+ export declare function retirePreviousLocalCa(dataDir: string): boolean;
111
+ /**
94
112
  * Load TLS cert+key from files. Returns Buffers suitable for Node.js TLS
95
113
  * options. `certPath` is a full chain, so the listener presents the local
96
114
  * root alongside the leaf.
@@ -0,0 +1,53 @@
1
+ import { Extension, type X509Certificate as PeculiarCertificate } from "@peculiar/x509";
2
+ import type { CertIdentity } from "./cert-identity.js";
3
+ /** `id-ce-nameConstraints`. */
4
+ export declare const NAME_CONSTRAINTS_OID = "2.5.29.30";
5
+ /** The address space the generated CA may certify. 100.64.0.0/10 is CGNAT, which Tailscale hands out. Loopback is for the hub's own `127.0.0.1` / `::1` SANs. */
6
+ export declare const PERMITTED_IP_RANGES: readonly string[];
7
+ /**
8
+ * Local-only DNS suffixes: none of them is delegated in public DNS. A leading
9
+ * dot permits every name under the suffix and not the suffix itself.
10
+ */
11
+ export declare const PERMITTED_DNS_SUFFIXES: readonly string[];
12
+ /** A name that cannot collide with public DNS: a single label, or under a local-only suffix. */
13
+ export declare function isLocalOnlyHostname(name: string): boolean;
14
+ /** What a CA may certify, as read from (or written into) its extension. */
15
+ export interface NameConstraintSet {
16
+ /** `ip/prefix` strings. */
17
+ readonly ipRanges: readonly string[];
18
+ /** Lower-cased DNS constraints: `.suffix` for a subtree, a bare name for the name and its subdomains. */
19
+ readonly dnsNames: readonly string[];
20
+ readonly critical: boolean;
21
+ }
22
+ /**
23
+ * The constraint set for a new CA. `hostnames` are the hub's own local-only
24
+ * names at creation time (a single-label OS hostname has no suffix to hide
25
+ * under), added as exact entries; anything that could collide with public DNS
26
+ * is dropped here, never constrained in.
27
+ */
28
+ export declare function buildNameConstraintSet(hostnames: readonly string[]): NameConstraintSet;
29
+ /** The `nameConstraints` extension for a CA: permitted subtrees only, marked critical. */
30
+ export declare function createNameConstraintsExtension(set: NameConstraintSet): Extension;
31
+ /** The CA's constraint set, or `null` when the certificate carries no `nameConstraints`. */
32
+ export declare function readNameConstraints(cert: PeculiarCertificate): NameConstraintSet | null;
33
+ /**
34
+ * True when a CA is safe to keep as the hub's anchor: constrained to a
35
+ * non-empty permitted set AND marked critical. A non-critical constraint is
36
+ * ignored by clients that do not know the extension, which is the same as none.
37
+ */
38
+ export declare function isConstrainedCa(cert: PeculiarCertificate): boolean;
39
+ /** Is this SAN value (an IP literal or a DNS name) inside the permitted set? */
40
+ export declare function permitsName(set: NameConstraintSet, value: string): boolean;
41
+ export interface ConstrainedIdentity {
42
+ readonly identity: CertIdentity;
43
+ /** SAN values the CA's constraints do not permit, dropped from the leaf. */
44
+ readonly dropped: readonly string[];
45
+ }
46
+ /**
47
+ * The part of an identity the CA is allowed to certify. A leaf naming
48
+ * anything outside the constraints makes the WHOLE chain fail validation in
49
+ * OpenSSL, so such names are dropped (and reported), never signed. The
50
+ * required lists shrink the same way: otherwise a name the CA cannot certify
51
+ * would count as a permanent SAN gap and reissue the leaf on every boot.
52
+ */
53
+ export declare function permitsIdentity(set: NameConstraintSet, identity: CertIdentity): ConstrainedIdentity;
@@ -1,11 +1,12 @@
1
1
  export { CA_VALIDITY_DAYS, type CertEvaluationInput, LEAF_RENEWAL_WINDOW_DAYS, SERVER_AUTH_OID } from "./cert-evaluation.js";
2
- export { type CertIdentity, type CertOptions, type HostFacts, type CertRegenerationReason, collectCertIdentity, type EnsureTlsResult, ensureTlsCert, reissueTlsLeaf, evaluateExistingCert, loadTlsCert, MAX_LEAF_VALIDITY_DAYS, type TlsCertPair } from "./cert-manager.js";
2
+ export { type CertIdentity, type CertOptions, type HostFacts, type CertRegenerationReason, collectCertIdentity, type EnsureTlsResult, ensureTlsCert, reissueTlsLeaf, replaceUnconstrainedLocalCa, retirePreviousLocalCa, caReplacementMarkerFile, evaluateExistingCert, loadTlsCert, MAX_LEAF_VALIDITY_DAYS, type TlsCertPair } from "./cert-manager.js";
3
3
  export { type BootTls, type BootTlsMode, prepareBootTls } from "./boot-tls.js";
4
4
  export { clearHttpsListener, type HttpsReloadOutcome, leafFingerprintOf, registerHttpsListener, reloadHttpsListener, servedHttpsLeafFingerprint } from "./https-listener.js";
5
- export { applyCertificateToListener, regenerateCertificate, revertToGeneratedCertificate, storeUploadedCertificate, type UploadCertificateInput } from "./tls-actions.js";
5
+ export { applyCertificateToListener, regenerateCertificate, revertToGeneratedCertificate, storeUploadedCertificate, type UploadCertificateInput, useStoredUploadedCertificate } from "./tls-actions.js";
6
6
  export { CA_COMMON_NAME } from "./cert-issue.js";
7
- export { validateUploadedTls, type UploadedTlsValidation } from "./uploaded-cert.js";
8
- export { type TlsAccessStatus, readTlsAccessStatus, splitPemCertificates, UPLOADED_CA_FILE, writeTlsMode, readTlsMode, readExtraSans, writeExtraSans } from "./tls-status.js";
7
+ export { checkUploadedCa, chainsToPublicRoot, validateUploadedTls, type UploadedCaCheck, type UploadedTlsErrorCode, type UploadedTlsValidation } from "./uploaded-cert.js";
8
+ export { buildNameConstraintSet, isConstrainedCa, type NameConstraintSet, permitsIdentity, permitsName, readNameConstraints, PERMITTED_DNS_SUFFIXES, PERMITTED_IP_RANGES } from "./cert-name-constraints.js";
9
+ export { type StoredUploadInfo, type TlsAccessStatus, readTlsAccessStatus, splitPemCertificates, UPLOADED_CA_FILE, writeTlsMode, readTlsMode, readExtraSans, writeExtraSans } from "./tls-status.js";
9
10
  export { acceptAgentSans, type AcceptedSans, AgentHttpsInstallError, type AgentHttpsMaterial, type AgentHttpsPaths, type AgentHttpsRequest, type AgentNames, agentHttpsPaths, certificateNames, collectAgentNames, createAgentHttpsRequest, describeHttpsLeaf, installAgentHttps, isContainerBridgeAddress, isLocalOnlyHostname, isPrivateAddress, issueAgentHttpsLeaf, type IssuedAgentHttps, loadAgentHttps, pemDigest, pinnedHttpsCaFingerprint } from "./agent-https.js";
10
11
  export { AGENT_HTTPS_NAMES_COLLECTION, AGENT_HTTPS_NAMES_COLUMNS, AgentHttpsNames, getProcessAgentHttpsNames, setProcessAgentHttpsNames } from "./agent-https-names.js";
11
12
  export { systemHostFacts } from "./cert-identity.js";
@@ -1,3 +1,4 @@
1
+ import type { X509Certificate } from "node:crypto";
1
2
  import { type CertOptions } from "./cert-manager.js";
2
3
  import { type HttpsReloadOutcome } from "./https-listener.js";
3
4
  /**
@@ -10,17 +11,36 @@ export interface UploadCertificateInput {
10
11
  readonly keyPem: string;
11
12
  readonly caPem?: string | undefined;
12
13
  }
14
+ /** Test seam: the trust anchors that count as "public". Defaults to the ones Node ships. */
15
+ export interface UploadCertificateOptions {
16
+ readonly publicRoots?: readonly X509Certificate[];
17
+ readonly now?: Date;
18
+ }
19
+ /**
20
+ * Store an operator's certificate (D873). The material is validated first, and a
21
+ * certificate that does not lead to a public CA must come with the CA that signed it
22
+ * (validated: it is a CA, in date, and issued the leaf) - otherwise "Download CA" would
23
+ * have nothing to hand out. Nothing is written when anything is refused.
24
+ *
25
+ * The leaf (or chain) and key are written twice: as the served pair, and as a kept copy
26
+ * (`uploaded.crt` / `uploaded.key`, with the issuing CA in `uploaded-ca.crt`) that
27
+ * "Revert to generated" leaves in place so the upload can be selected again. The local
28
+ * CA (`camstack-ca.crt` + key) is never touched - it did not sign the upload, and it is
29
+ * what every device that trusts it still needs.
30
+ */
31
+ export declare function storeUploadedCertificate(dataDir: string, input: UploadCertificateInput, options?: UploadCertificateOptions): Promise<void>;
13
32
  /**
14
- * Store an operator's certificate. The leaf (or chain) and key replace the
15
- * served pair; a CA, when given, goes to its own file. The local CA
16
- * (`camstack-ca.crt` + key) is never touched — it did not sign the upload,
17
- * and it is what "Revert to generated" and every device that trusts it
18
- * still need.
33
+ * Serve the kept upload again after a revert (D873). Refused with a clear error when none
34
+ * is kept; the generated material stays on disk and is untouched.
19
35
  */
20
- export declare function storeUploadedCertificate(dataDir: string, input: UploadCertificateInput): Promise<void>;
36
+ export declare function useStoredUploadedCertificate(dataDir: string): Promise<void>;
21
37
  /** Issue a new generated leaf under the local CA. Refused while an upload is served. */
22
38
  export declare function regenerateCertificate(dataDir: string, options: CertOptions): Promise<void>;
23
- /** Drop the upload and serve the generated local-CA leaf again (D227). */
39
+ /**
40
+ * Serve the generated local-CA leaf again (D227). The uploaded certificate and its CA stay
41
+ * on disk (D873) so it can be selected again; an upload made before they were kept is
42
+ * copied aside first, from the pair being served.
43
+ */
24
44
  export declare function revertToGeneratedCertificate(dataDir: string, options: CertOptions): Promise<void>;
25
45
  /**
26
46
  * Put the pair on disk on the HTTPS listener. Called after every action, so
@@ -1,4 +1,11 @@
1
1
  export type TlsMode = "generated" | "uploaded" | "disabled";
2
+ /** An uploaded certificate kept on disk (D873): selectable again after "Revert to generated". */
3
+ export interface StoredUploadInfo {
4
+ readonly leafFingerprintSha256: string;
5
+ readonly validTo: string;
6
+ /** Whether a CA that signed it is kept with it. */
7
+ readonly hasCa: boolean;
8
+ }
2
9
  export interface TlsAccessStatus {
3
10
  readonly mode: TlsMode;
4
11
  readonly leafFingerprintSha256: string | null;
@@ -8,11 +15,26 @@ export interface TlsAccessStatus {
8
15
  readonly caCertPem: string | null;
9
16
  readonly reissueError: string | null;
10
17
  readonly restartRequired: boolean;
18
+ /**
19
+ * An uploaded certificate is served but no CA that signed it is kept and the chain does
20
+ * not lead to a public CA: "Download CA" has nothing to hand out, so devices cannot be
21
+ * made to trust the hub (D873). `false` otherwise.
22
+ */
23
+ readonly caMissing: boolean;
24
+ /** Does the hub's own generated CA carry a critical name constraint? `null` while it has no CA. */
25
+ readonly localCaConstrained: boolean | null;
26
+ /** ISO time the generated CA was replaced by a constrained one; devices must re-trust it. */
27
+ readonly caReplacedAt: string | null;
28
+ /** The uploaded certificate kept on disk, whether or not it is the one served. */
29
+ readonly storedUpload: StoredUploadInfo | null;
11
30
  }
12
31
  export declare function readTlsMode(dataDir: string): TlsMode;
13
32
  export declare function writeTlsMode(dataDir: string, mode: TlsMode): void;
14
33
  export declare function readExtraSans(dataDir: string): readonly string[];
15
34
  export declare function writeExtraSans(dataDir: string, sans: readonly string[]): void;
35
+ /** The kept copy of the last upload: the chain and key as the operator supplied them. */
36
+ export declare const STORED_UPLOAD_CERT_FILE = "uploaded.crt";
37
+ export declare const STORED_UPLOAD_KEY_FILE = "uploaded.key";
16
38
  /** Where an upload's own CA is kept — never over the local CA's `camstack-ca.crt`. */
17
39
  export declare const UPLOADED_CA_FILE = "uploaded-ca.crt";
18
40
  /** Every certificate in a PEM bundle, in order. */
@@ -1,14 +1,46 @@
1
+ import { X509Certificate } from "node:crypto";
2
+ /** Stable codes of the `server` i18n catalog (D863); the English text always travels beside them. */
3
+ export type UploadedTlsErrorCode = "tls.cert-unparsable" | "tls.key-unparsable" | "tls.key-mismatch" | "tls.missing-server-auth" | "tls.ca-required" | "tls.ca-unparsable" | "tls.ca-not-issuer" | "tls.ca-not-a-ca" | "tls.ca-expired";
4
+ export interface UploadedTlsFailure {
5
+ readonly ok: false;
6
+ readonly code: UploadedTlsErrorCode;
7
+ /** English text; the hub translates by `code`. */
8
+ readonly error: string;
9
+ }
1
10
  export type UploadedTlsValidation = {
2
11
  readonly ok: true;
3
12
  readonly fingerprintSha256: string;
4
13
  readonly validTo: string;
5
- } | {
6
- readonly ok: false;
7
- readonly error: string;
8
- };
14
+ } | UploadedTlsFailure;
9
15
  /**
10
16
  * Operator-supplied material must parse, match its key, and carry
11
17
  * `extendedKeyUsage = serverAuth` — the same leaf defects D227 refused
12
18
  * to keep generating.
13
19
  */
14
20
  export declare function validateUploadedTls(certPem: string, keyPem: string): UploadedTlsValidation;
21
+ /** Every certificate in a PEM bundle, in order. */
22
+ export declare function pemCertificates(pem: string): readonly string[];
23
+ /** The trust anchors of public CAs that ship with Node. */
24
+ export declare function publicRootCertificates(): readonly X509Certificate[];
25
+ /**
26
+ * True when the served chain leads to a public CA that clients already trust:
27
+ * the leaf, or one of the chain's own certificates, is signed by a public root.
28
+ * Such a certificate needs nothing installed on a device, so no CA is required.
29
+ */
30
+ export declare function chainsToPublicRoot(chainPem: string, roots?: readonly X509Certificate[]): boolean;
31
+ export type UploadedCaCheck = {
32
+ readonly ok: true;
33
+ /** The CA that signed the leaf, to be kept for "Download CA"; `null` for a public chain with none supplied. */
34
+ readonly issuerPem: string | null;
35
+ readonly publicChain: boolean;
36
+ } | UploadedTlsFailure;
37
+ /**
38
+ * The CA rule for an upload (D873). A CA the operator supplies must parse, be a CA, be in
39
+ * date and actually have signed the leaf. Without one, the upload is accepted only when
40
+ * its own chain carries the issuer or leads to a public root; anything else is refused,
41
+ * because a device cannot be told to trust a certificate nobody can hand it the CA of.
42
+ */
43
+ export declare function checkUploadedCa(chainPem: string, caPem: string | undefined, options?: {
44
+ readonly now?: Date;
45
+ readonly publicRoots?: readonly X509Certificate[];
46
+ }): UploadedCaCheck;
@@ -11913,6 +11913,12 @@ AsnEcSignatureFormatter.namedCurveSize.set("K-256", 32);
11913
11913
  AsnEcSignatureFormatter.namedCurveSize.set("P-384", 48);
11914
11914
  AsnEcSignatureFormatter.namedCurveSize.set("P-521", 66);
11915
11915
  //#endregion
11916
+ Object.defineProperty(exports, "AsnConvert", {
11917
+ enumerable: true,
11918
+ get: function() {
11919
+ return AsnConvert;
11920
+ }
11921
+ });
11916
11922
  Object.defineProperty(exports, "AuthorityKeyIdentifierExtension", {
11917
11923
  enumerable: true,
11918
11924
  get: function() {
@@ -11931,6 +11937,30 @@ Object.defineProperty(exports, "ExtendedKeyUsageExtension", {
11931
11937
  return ExtendedKeyUsageExtension;
11932
11938
  }
11933
11939
  });
11940
+ Object.defineProperty(exports, "Extension", {
11941
+ enumerable: true,
11942
+ get: function() {
11943
+ return Extension;
11944
+ }
11945
+ });
11946
+ Object.defineProperty(exports, "GeneralName", {
11947
+ enumerable: true,
11948
+ get: function() {
11949
+ return GeneralName$1;
11950
+ }
11951
+ });
11952
+ Object.defineProperty(exports, "GeneralSubtree", {
11953
+ enumerable: true,
11954
+ get: function() {
11955
+ return GeneralSubtree;
11956
+ }
11957
+ });
11958
+ Object.defineProperty(exports, "GeneralSubtrees", {
11959
+ enumerable: true,
11960
+ get: function() {
11961
+ return GeneralSubtrees;
11962
+ }
11963
+ });
11934
11964
  Object.defineProperty(exports, "KeyUsageFlags", {
11935
11965
  enumerable: true,
11936
11966
  get: function() {
@@ -11943,6 +11973,12 @@ Object.defineProperty(exports, "KeyUsagesExtension", {
11943
11973
  return KeyUsagesExtension;
11944
11974
  }
11945
11975
  });
11976
+ Object.defineProperty(exports, "NameConstraints", {
11977
+ enumerable: true,
11978
+ get: function() {
11979
+ return NameConstraints;
11980
+ }
11981
+ });
11946
11982
  Object.defineProperty(exports, "PemConverter", {
11947
11983
  enumerable: true,
11948
11984
  get: function() {
@@ -11913,4 +11913,4 @@ AsnEcSignatureFormatter.namedCurveSize.set("K-256", 32);
11913
11913
  AsnEcSignatureFormatter.namedCurveSize.set("P-384", 48);
11914
11914
  AsnEcSignatureFormatter.namedCurveSize.set("P-521", 66);
11915
11915
  //#endregion
11916
- export { KeyUsagesExtension as a, Pkcs10CertificateRequestGenerator as c, X509Certificate as d, X509CertificateGenerator as f, backdatedNotBefore as g, tslib_es6_exports as h, KeyUsageFlags as i, SubjectAlternativeNameExtension as l, init_tslib_es6 as m, BasicConstraintsExtension as n, PemConverter as o, cryptoProvider as p, ExtendedKeyUsageExtension as r, Pkcs10CertificateRequest as s, AuthorityKeyIdentifierExtension as t, SubjectKeyIdentifierExtension as u };
11916
+ export { backdatedNotBefore as S, NameConstraints as _, KeyUsageFlags as a, tslib_es6_exports as b, Pkcs10CertificateRequest as c, SubjectKeyIdentifierExtension as d, X509Certificate as f, GeneralSubtrees as g, GeneralSubtree as h, Extension as i, Pkcs10CertificateRequestGenerator as l, cryptoProvider as m, BasicConstraintsExtension as n, KeyUsagesExtension as o, X509CertificateGenerator as p, ExtendedKeyUsageExtension as r, PemConverter as s, AuthorityKeyIdentifierExtension as t, SubjectAlternativeNameExtension as u, GeneralName$1 as v, AsnConvert as x, init_tslib_es6 as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.438",
3
+ "version": "1.2.440",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",
@@ -57,7 +57,7 @@
57
57
  "icon": "assets/sqlite-settings/icon.svg",
58
58
  "color": "#0d9488",
59
59
  "entry": "./dist/builtins/sqlite-storage/sqlite-settings.addon.js",
60
- "description": "The relational ENGINE behind the data door (SQLite WAL). Registers `data-store-provider`, an internal collection cap; the public `settings-store` door is owned by `storage-orchestrator`, which dispatches here. Callers never reach this addon directly — that is what makes the engine replaceable. It ALSO registers `vector-store` (sqlite-vec) from the same database file and the same open handle, and `database-snapshot`: the verified copy of its database that the nightly backup archives in place of the live files, written by this runner on a thread over its own connection. Runs in its OWN process (`execution.isolate`) — `better-sqlite3` `Statement.run` does not belong on the event loop that serves the API.",
60
+ "description": "The relational ENGINE behind the data door (SQLite WAL). Registers `data-store-provider`, an internal collection cap; the public `settings-store` door is owned by `storage-orchestrator`, which dispatches here. Callers never reach this addon directly — that is what makes the engine replaceable. It ALSO registers `vector-store` (sqlite-vec) from the same database file and the same open handle, `storage-admin` (list tables, drop one nothing owns) and `database-snapshot`: the verified copy of its database that the nightly backup archives in place of the live files, written by this runner on a thread over its own connection. Runs in its OWN process (`execution.isolate`) — `better-sqlite3` `Statement.run` does not belong on the event loop that serves the API.",
61
61
  "execution": {
62
62
  "isolate": true,
63
63
  "heapProfile": "heavy",
@@ -72,6 +72,9 @@
72
72
  },
73
73
  {
74
74
  "name": "database-snapshot"
75
+ },
76
+ {
77
+ "name": "storage-admin"
75
78
  }
76
79
  ]
77
80
  },
@@ -491,6 +494,8 @@
491
494
  "@camstack/types": "*",
492
495
  "@homebridge/ciao": "^1.3.9",
493
496
  "@msgpack/msgpack": "^3.1.3",
497
+ "@peculiar/asn1-schema": "^2.6.0",
498
+ "@peculiar/asn1-x509": "^2.6.1",
494
499
  "@peculiar/x509": "^1.14.3",
495
500
  "@trpc/client": "^11.16.0",
496
501
  "@trpc/server": "^11.16.0",
@@ -2,10 +2,10 @@ const require_chunk = require("./chunk-Cek0wNdY.js");
2
2
  let node_crypto = require("node:crypto");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_chunk.__toESM(node_path);
5
+ let _camstack_types_addon = require("@camstack/types/addon");
5
6
  let node_fs = require("node:fs");
6
7
  node_fs = require_chunk.__toESM(node_fs);
7
8
  let node_util = require("node:util");
8
- let _camstack_types_addon = require("@camstack/types/addon");
9
9
  let node_http = require("node:http");
10
10
  let node_zlib = require("node:zlib");
11
11
  let node_perf_hooks = require("node:perf_hooks");
@@ -1,10 +1,10 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import * as path$1 from "node:path";
3
3
  import path from "node:path";
4
+ import { HF_REPO, MODEL_FORMATS, describeCustomActions, hfModelUrl } from "@camstack/types/addon";
4
5
  import * as fs from "node:fs";
5
6
  import { createReadStream, promises } from "node:fs";
6
7
  import { promisify } from "node:util";
7
- import { HF_REPO, MODEL_FORMATS, describeCustomActions, hfModelUrl } from "@camstack/types/addon";
8
8
  import { createServer } from "node:http";
9
9
  import { brotliCompress, constants as constants$1, gzip } from "node:zlib";
10
10
  import { PerformanceObserver } from "node:perf_hooks";