@camstack/system 1.2.114 → 1.2.116
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.
- package/dist/addon-runner.js +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.d.ts +1 -1
- package/dist/builtins/alerts/alerts.addon.js +6 -1
- package/dist/builtins/alerts/alerts.addon.mjs +6 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +3 -3
- package/dist/builtins/local-network/local-hostname.d.ts +15 -0
- package/dist/builtins/local-network/local-network.addon.d.ts +31 -7
- package/dist/builtins/local-network/local-network.addon.js +177 -28
- package/dist/builtins/local-network/local-network.addon.mjs +177 -28
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{dist-CJ8RRSvQ.mjs → dist-BpzbLPel.mjs} +149 -6
- package/dist/{dist-C1Aea3c6.js → dist-Cri60Jd0.js} +149 -6
- package/dist/http/lan-http-bind.d.ts +19 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +30 -82
- package/dist/index.mjs +11 -83
- package/dist/lan-http-bind-BDJm4d-K.mjs +716 -0
- package/dist/lan-http-bind-CZ4EBeD_.js +848 -0
- package/dist/{retired-settings-keys-B52KsNz8.js → retired-settings-keys-BVAxT5XL.js} +1 -1
- package/dist/{retired-settings-keys-C1efPfyW.mjs → retired-settings-keys-SrxMH3OT.mjs} +1 -1
- package/dist/tls/cert-evaluation.d.ts +41 -0
- package/dist/tls/cert-identity.d.ts +37 -0
- package/dist/tls/cert-manager.d.ts +71 -6
- package/dist/tls/cert-openssl.d.ts +23 -0
- package/dist/tls/index.d.ts +5 -1
- package/dist/tls/tls-status.d.ts +16 -0
- package/dist/tls/uploaded-cert.d.ts +14 -0
- package/package.json +1 -1
- package/dist/{manifest-python-deps-MA_BZGBz.js → manifest-python-deps-CwBbX4Ut.js} +1 -1
- package/dist/{manifest-python-deps-Cb1ANt3i.mjs → manifest-python-deps-SGdbr9D5.mjs} +1 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { CertIdentity } from './cert-identity.js';
|
|
2
|
+
/** OID `id-kp-serverAuth`. Apple's trustd requires it on the leaf. */
|
|
3
|
+
export declare const SERVER_AUTH_OID = "1.3.6.1.5.5.7.3.1";
|
|
4
|
+
/**
|
|
5
|
+
* Apple's `SecPolicyCheckOtherTrustValidityPeriod` ceiling is 825 days;
|
|
6
|
+
* the CA/Browser Forum's recommended maximum is 397. We sit at 397.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MAX_LEAF_VALIDITY_DAYS = 397;
|
|
9
|
+
/** Reissue this long before the leaf dies, so nobody serves an expired one. */
|
|
10
|
+
export declare const LEAF_RENEWAL_WINDOW_DAYS = 30;
|
|
11
|
+
/** A local root outlives many leaves — it is the anchor an operator trusts. */
|
|
12
|
+
export declare const CA_VALIDITY_DAYS = 3650;
|
|
13
|
+
/**
|
|
14
|
+
* The CA must outlive any leaf it signs by a margin, or a leaf issued
|
|
15
|
+
* today expires after its own issuer.
|
|
16
|
+
*/
|
|
17
|
+
export declare const CA_RENEWAL_WINDOW_DAYS: number;
|
|
18
|
+
/**
|
|
19
|
+
* Why a reissue is happening. Reported on `EnsureTlsResult` and logged
|
|
20
|
+
* verbatim — an operator who trusted the old certificate by hand learns
|
|
21
|
+
* from this string, not from a browser error.
|
|
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';
|
|
24
|
+
export interface CertEvaluationInput {
|
|
25
|
+
/** Contents of `camstack.crt` — leaf first, then the issuing CA. */
|
|
26
|
+
readonly chainPem: string;
|
|
27
|
+
/** Contents of `camstack.key` — the leaf's private key. */
|
|
28
|
+
readonly keyPem: string;
|
|
29
|
+
/** Contents of `camstack-ca.crt` — the local root. */
|
|
30
|
+
readonly caPem: string;
|
|
31
|
+
readonly identity: CertIdentity;
|
|
32
|
+
readonly now: Date;
|
|
33
|
+
}
|
|
34
|
+
export declare function reasonRequiresNewCa(reason: CertRegenerationReason): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* `null` when the material on disk is fit to keep serving.
|
|
37
|
+
*
|
|
38
|
+
* Order matters only for the quality of the reported reason; any
|
|
39
|
+
* non-null answer produces the same action.
|
|
40
|
+
*/
|
|
41
|
+
export declare function evaluateExistingCert(input: CertEvaluationInput): CertRegenerationReason | null;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Names/addresses that go INTO the certificate, and the subset whose
|
|
3
|
+
* absence forces a reissue.
|
|
4
|
+
*
|
|
5
|
+
* The two are deliberately different sizes. Everything plausible is
|
|
6
|
+
* certified — costs nothing. Only a **stable** identity gates
|
|
7
|
+
* regeneration, because a gate that fires on a rotating address rewrites
|
|
8
|
+
* the certificate on every boot, which is a worse bug than the one the
|
|
9
|
+
* gate exists to fix.
|
|
10
|
+
*/
|
|
11
|
+
export interface CertIdentity {
|
|
12
|
+
readonly dnsNames: readonly string[];
|
|
13
|
+
readonly ipAddresses: readonly string[];
|
|
14
|
+
readonly requiredDnsNames: readonly string[];
|
|
15
|
+
readonly requiredIpAddresses: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
export interface CertIdentityOptions {
|
|
18
|
+
readonly commonName: string;
|
|
19
|
+
readonly extraSans: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
export declare function isIpv4(value: string): boolean;
|
|
22
|
+
export declare function isIpv6(value: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* `fe80::/10` and `169.254.0.0/16`. An IPv6 link-local address needs a
|
|
25
|
+
* zone index to be usable and a SAN cannot carry one, so such an entry
|
|
26
|
+
* can never match anything — it is noise that makes the certificate look
|
|
27
|
+
* broader than it is.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isLinkLocal(value: string): boolean;
|
|
30
|
+
/** `fc00::/7` — the IPv6 range that behaves like RFC 1918 does for v4. */
|
|
31
|
+
export declare function isUniqueLocalIpv6(value: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Snapshot the machine's identity. Called once per `ensureTlsCert`; the
|
|
34
|
+
* result is both the SAN list for a new leaf and the yardstick the
|
|
35
|
+
* existing leaf is measured against.
|
|
36
|
+
*/
|
|
37
|
+
export declare function collectCertIdentity(options: CertIdentityOptions): CertIdentity;
|
|
@@ -1,26 +1,91 @@
|
|
|
1
|
+
import { CertRegenerationReason, evaluateExistingCert, MAX_LEAF_VALIDITY_DAYS } from './cert-evaluation.js';
|
|
2
|
+
import { CertIdentity, collectCertIdentity } from './cert-identity.js';
|
|
1
3
|
export interface TlsCertPair {
|
|
2
4
|
readonly cert: Buffer;
|
|
3
5
|
readonly key: Buffer;
|
|
4
6
|
}
|
|
5
7
|
export interface EnsureTlsResult {
|
|
8
|
+
/** Full chain — leaf then CA. This is what the listener serves. */
|
|
6
9
|
readonly certPath: string;
|
|
10
|
+
/** The leaf's private key. */
|
|
7
11
|
readonly keyPath: string;
|
|
12
|
+
/** The local root. **This** is the file an operator installs as trusted. */
|
|
13
|
+
readonly caCertPath: string;
|
|
14
|
+
readonly caKeyPath: string;
|
|
8
15
|
readonly generated: boolean;
|
|
16
|
+
/** Why it was reissued — `null` when the existing material was kept. */
|
|
17
|
+
readonly reason: CertRegenerationReason | null;
|
|
18
|
+
/** True when the ROOT rotated, i.e. trust granted by hand is now void. */
|
|
19
|
+
readonly caRotated: boolean;
|
|
20
|
+
readonly fingerprintSha256: string;
|
|
21
|
+
readonly caFingerprintSha256: string;
|
|
22
|
+
/** Fingerprint of the leaf this one replaced — `null` on a fresh install. */
|
|
23
|
+
readonly previousFingerprintSha256: string | null;
|
|
24
|
+
/** ISO timestamp at which the leaf stops being valid. */
|
|
25
|
+
readonly validTo: string;
|
|
26
|
+
readonly sans: readonly string[];
|
|
27
|
+
/**
|
|
28
|
+
* Set when a reissue was needed and FAILED, and the previous material is
|
|
29
|
+
* still being served. Never null-and-degraded silently: this is the line
|
|
30
|
+
* that says the certificate is not what the rule wants it to be.
|
|
31
|
+
*/
|
|
32
|
+
readonly reissueError: string | null;
|
|
9
33
|
}
|
|
10
34
|
export interface CertOptions {
|
|
11
|
-
/** Common Name for the
|
|
35
|
+
/** Common Name for the leaf (default: 'camstack.local') */
|
|
12
36
|
readonly commonName?: string;
|
|
13
|
-
/**
|
|
37
|
+
/** Leaf validity in days. Clamped to `MAX_LEAF_VALIDITY_DAYS`. */
|
|
14
38
|
readonly validDays?: number;
|
|
15
|
-
/** Additional SANs (DNS names or IPs) */
|
|
39
|
+
/** Additional SANs (DNS names or IPs). Required coverage by definition. */
|
|
16
40
|
readonly extraSans?: readonly string[];
|
|
17
41
|
}
|
|
18
42
|
/**
|
|
19
|
-
* Ensure a
|
|
20
|
-
*
|
|
43
|
+
* Ensure the hub has a usable TLS identity under `<dataDir>/tls`, made of
|
|
44
|
+
* a long-lived local **root** (`camstack-ca.crt`) and a short **leaf**
|
|
45
|
+
* chained to it (`camstack.crt` = leaf + root, `camstack.key`).
|
|
46
|
+
*
|
|
47
|
+
* ## The regeneration rule (D227)
|
|
48
|
+
*
|
|
49
|
+
* Reissue happens when, and only when, the material on disk **cannot do
|
|
50
|
+
* its job**:
|
|
51
|
+
*
|
|
52
|
+
* - it is absent, unreadable, or the key does not match the certificate;
|
|
53
|
+
* - the root is absent (every pre-2026-08 install: one self-signed
|
|
54
|
+
* `CA:TRUE` leaf and no root) or is inside its renewal window;
|
|
55
|
+
* - the leaf is inside its 30-day renewal window;
|
|
56
|
+
* - the leaf lacks `extendedKeyUsage = serverAuth` — Apple's trustd
|
|
57
|
+
* rejects an app-supplied anchor without it;
|
|
58
|
+
* - the leaf is not actually issued by the root on disk;
|
|
59
|
+
* - the leaf's validity span exceeds {@link MAX_LEAF_VALIDITY_DAYS};
|
|
60
|
+
* - **a name or address the hub must answer for is missing from the SAN
|
|
61
|
+
* list** — a DHCP move, a new interface or a new hostname.
|
|
62
|
+
*
|
|
63
|
+
* And explicitly NOT when: the certificate names an address the machine
|
|
64
|
+
* no longer has (a superset still works — the check is one-directional),
|
|
65
|
+
* a volatile interface (docker/VPN/mesh) or a global-scope IPv6 address
|
|
66
|
+
* changed (SLAAC privacy addresses rotate daily), or a new build shipped.
|
|
67
|
+
*
|
|
68
|
+
* Every trigger is derived from the files plus the machine, so once a
|
|
69
|
+
* reissue clears them it clears them for good: the next boot returns
|
|
70
|
+
* `generated: false`. A certificate that moved on every boot would
|
|
71
|
+
* invalidate every trust an operator granted by hand — worse than the
|
|
72
|
+
* bugs this rule fixes.
|
|
73
|
+
*
|
|
74
|
+
* **The root is rotated only for root-level reasons.** SAN drift rotates
|
|
75
|
+
* the leaf alone, so trust granted to `camstack-ca.crt` survives every
|
|
76
|
+
* address change the hub will ever see.
|
|
21
77
|
*/
|
|
22
78
|
export declare function ensureTlsCert(dataDir: string, options?: CertOptions): Promise<EnsureTlsResult>;
|
|
23
79
|
/**
|
|
24
|
-
*
|
|
80
|
+
* Operator "Regenerate": issue a new leaf under the existing local CA.
|
|
81
|
+
* Never rotates the root — trust granted to `camstack-ca.crt` survives.
|
|
82
|
+
*/
|
|
83
|
+
export declare function reissueTlsLeaf(dataDir: string, options?: CertOptions): Promise<EnsureTlsResult>;
|
|
84
|
+
/**
|
|
85
|
+
* Load TLS cert+key from files. Returns Buffers suitable for Node.js TLS
|
|
86
|
+
* options. `certPath` is a full chain, so the listener presents the local
|
|
87
|
+
* root alongside the leaf.
|
|
25
88
|
*/
|
|
26
89
|
export declare function loadTlsCert(certPath: string, keyPath: string): TlsCertPair;
|
|
90
|
+
export type { CertIdentity, CertRegenerationReason };
|
|
91
|
+
export { collectCertIdentity, evaluateExistingCert, MAX_LEAF_VALIDITY_DAYS };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CertIdentity } from './cert-identity.js';
|
|
2
|
+
/** Subject of the local root. Distinct from the leaf's CN by construction. */
|
|
3
|
+
export declare const CA_COMMON_NAME = "CamStack Local CA";
|
|
4
|
+
export interface CaMaterialPaths {
|
|
5
|
+
readonly caCertPath: string;
|
|
6
|
+
readonly caKeyPath: string;
|
|
7
|
+
}
|
|
8
|
+
export interface LeafMaterialPaths {
|
|
9
|
+
readonly certPath: string;
|
|
10
|
+
readonly keyPath: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A local root: 10 years, `pathlen:0` so it can sign leaves and nothing
|
|
14
|
+
* else, and **no EKU** — Apple rejects an anchor asserting
|
|
15
|
+
* `anyExtendedKeyUsage`, and a root with no EKU constrains nothing.
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateCa(tmpDir: string): Promise<CaMaterialPaths>;
|
|
18
|
+
/**
|
|
19
|
+
* A server leaf signed by the local root: `CA:FALSE`, and
|
|
20
|
+
* `extendedKeyUsage=serverAuth` — not `anyExtendedKeyUsage`, which
|
|
21
|
+
* Apple's `testAppTrustRoot_AnyEKU_AfterJul2019` asserts must fail.
|
|
22
|
+
*/
|
|
23
|
+
export declare function issueLeaf(tmpDir: string, ca: CaMaterialPaths, identity: CertIdentity, commonName: string, validDays: number): Promise<LeafMaterialPaths>;
|
package/dist/tls/index.d.ts
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
export {
|
|
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 CertRegenerationReason, collectCertIdentity, type EnsureTlsResult, ensureTlsCert, reissueTlsLeaf, evaluateExistingCert, loadTlsCert, MAX_LEAF_VALIDITY_DAYS, type TlsCertPair, } from './cert-manager.js';
|
|
3
|
+
export { CA_COMMON_NAME } from './cert-openssl.js';
|
|
4
|
+
export { validateUploadedTls, type UploadedTlsValidation } from './uploaded-cert.js';
|
|
5
|
+
export { type TlsAccessStatus, readTlsAccessStatus, writeTlsMode, readTlsMode, readExtraSans, writeExtraSans, } from './tls-status.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type TlsMode = 'generated' | 'uploaded' | 'disabled';
|
|
2
|
+
export interface TlsAccessStatus {
|
|
3
|
+
readonly mode: TlsMode;
|
|
4
|
+
readonly leafFingerprintSha256: string | null;
|
|
5
|
+
readonly caFingerprintSha256: string | null;
|
|
6
|
+
readonly validTo: string | null;
|
|
7
|
+
readonly sans: readonly string[];
|
|
8
|
+
readonly caCertPem: string | null;
|
|
9
|
+
readonly reissueError: string | null;
|
|
10
|
+
readonly restartRequired: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function readTlsMode(dataDir: string): TlsMode;
|
|
13
|
+
export declare function writeTlsMode(dataDir: string, mode: TlsMode): void;
|
|
14
|
+
export declare function readExtraSans(dataDir: string): readonly string[];
|
|
15
|
+
export declare function writeExtraSans(dataDir: string, sans: readonly string[]): void;
|
|
16
|
+
export declare function readTlsAccessStatus(dataDir: string, restartRequired?: boolean): TlsAccessStatus;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type UploadedTlsValidation = {
|
|
2
|
+
readonly ok: true;
|
|
3
|
+
readonly fingerprintSha256: string;
|
|
4
|
+
readonly validTo: string;
|
|
5
|
+
} | {
|
|
6
|
+
readonly ok: false;
|
|
7
|
+
readonly error: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Operator-supplied material must parse, match its key, and carry
|
|
11
|
+
* `extendedKeyUsage = serverAuth` — the same leaf defects D227 refused
|
|
12
|
+
* to keep generating.
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateUploadedTls(certPem: string, keyPem: string): UploadedTlsValidation;
|
package/package.json
CHANGED
|
@@ -17,8 +17,8 @@ node_v8 = require_chunk.__toESM(node_v8);
|
|
|
17
17
|
let node_vm = require("node:vm");
|
|
18
18
|
node_vm = require_chunk.__toESM(node_vm);
|
|
19
19
|
let _camstack_types_addon = require("@camstack/types/addon");
|
|
20
|
-
let _trpc_client = require("@trpc/client");
|
|
21
20
|
let node_net = require("node:net");
|
|
21
|
+
let _trpc_client = require("@trpc/client");
|
|
22
22
|
//#region src/kernel/heap-watch.ts
|
|
23
23
|
/**
|
|
24
24
|
* heap-watch — hub-main's own memory, on the record, and returned to the OS.
|
|
@@ -13,8 +13,8 @@ import { createServer } from "node:http";
|
|
|
13
13
|
import * as v8 from "node:v8";
|
|
14
14
|
import * as vm from "node:vm";
|
|
15
15
|
import { BOOT_RECOVERY_BACKOFF_MS, DATAPLANE_SECRET_HEADER, DeviceType, DisposerChain, EventCategory, ReadinessRegistry, asJsonObject, asString, createDeviceProxy, deviceOpsCapability, emitReadiness, errMsg, expandCapMethods, readNodePin, scopeKey, sleep, withCapInputDefaults } from "@camstack/types/addon";
|
|
16
|
-
import { TRPCClientError, createTRPCClient } from "@trpc/client";
|
|
17
16
|
import { connect, createServer as createServer$1 } from "node:net";
|
|
17
|
+
import { TRPCClientError, createTRPCClient } from "@trpc/client";
|
|
18
18
|
//#region src/kernel/heap-watch.ts
|
|
19
19
|
/**
|
|
20
20
|
* heap-watch — hub-main's own memory, on the record, and returned to the OS.
|