@camstack/types 1.2.327 → 1.2.328
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/base-addon.d.ts +58 -0
- package/dist/addon/secret-aware-context.d.ts +27 -0
- package/dist/addon/secret-paths.d.ts +48 -0
- package/dist/addon/secret-settings-view.d.ts +80 -0
- package/dist/addon.d.ts +1 -0
- package/dist/addon.js +52 -4
- package/dist/addon.mjs +27 -5
- package/dist/capabilities/addons.cap.d.ts +4 -4
- package/dist/capabilities/alerts.cap.d.ts +5 -5
- package/dist/capabilities/control.cap.d.ts +11 -11
- package/dist/capabilities/day-night.cap.d.ts +8 -8
- package/dist/capabilities/device-manager.cap.d.ts +2 -0
- package/dist/capabilities/enum-sensor.cap.d.ts +4 -4
- package/dist/capabilities/index.d.ts +6 -2
- package/dist/capabilities/integrations.cap.d.ts +6 -3
- package/dist/capabilities/keychain-admin.cap.d.ts +312 -0
- package/dist/capabilities/keychain.cap.d.ts +180 -0
- package/dist/capabilities/lawn-mower-control.cap.d.ts +4 -4
- package/dist/capabilities/log-channels.cap.d.ts +1 -1
- package/dist/capabilities/log-destination.cap.d.ts +5 -5
- package/dist/capabilities/notification-rules.cap.d.ts +30 -30
- package/dist/capabilities/osd-manager.cap.d.ts +11 -11
- package/dist/capabilities/osd.cap.d.ts +5 -5
- package/dist/capabilities/pipeline-analytics.cap.d.ts +996 -118
- package/dist/capabilities/pipeline-orchestrator.cap.d.ts +384 -0
- package/dist/capabilities/pipeline-runner.cap.d.ts +145 -2
- package/dist/capabilities/storage.cap.d.ts +1 -1
- package/dist/capabilities/system.cap.d.ts +16 -16
- package/dist/capabilities/webrtc-session.cap.d.ts +3 -3
- package/dist/client.js +32 -1
- package/dist/client.mjs +32 -1
- package/dist/device/device-context.d.ts +11 -1
- package/dist/device/index.d.ts +1 -1
- package/dist/enums/event-category.d.ts +7 -0
- package/dist/enums.js +1 -1
- package/dist/enums.mjs +1 -1
- package/dist/{event-category-CtBAl88K.js → event-category-CHduMGvg.js} +7 -0
- package/dist/{event-category-B_0XEcLF.mjs → event-category-DaIZi00R.mjs} +7 -0
- package/dist/generated/addon-api.d.ts +254 -0
- package/dist/generated/cap-input-defaults.d.ts +1 -1
- package/dist/generated/capability-router-map.d.ts +8 -2
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/method-device-selectors.d.ts +1 -1
- package/dist/generated/system-proxy.d.ts +4 -2
- package/dist/health/wiring-health.d.ts +8 -8
- package/dist/index.d.ts +6 -2
- package/dist/index.js +1736 -101
- package/dist/index.mjs +1625 -98
- package/dist/interfaces/addon.d.ts +25 -0
- package/dist/interfaces/config-ui-secrets.d.ts +12 -0
- package/dist/interfaces/config-ui.d.ts +29 -1
- package/dist/interfaces/event-bus.d.ts +11 -0
- package/dist/interfaces/pipeline-runner-capability.d.ts +18 -0
- package/dist/keychain/index.d.ts +5 -0
- package/dist/keychain/known-secrets.d.ts +31 -0
- package/dist/keychain/resolve-secret-views.d.ts +2 -0
- package/dist/keychain/secret-marker.d.ts +52 -0
- package/dist/keychain/secret-ref.d.ts +62 -0
- package/dist/keychain/secrets-access.d.ts +77 -0
- package/dist/logging/log-channel.d.ts +2 -2
- package/dist/pipeline/auto-tune/plan-schemas.d.ts +123 -0
- package/dist/pipeline/auto-tune/recommend.d.ts +26 -1
- package/dist/pipeline/load-balancing/schemas.d.ts +8 -7
- package/dist/pipeline/node-benchmark/benchmark-mode.d.ts +57 -0
- package/dist/pipeline/node-benchmark/index.d.ts +9 -0
- package/dist/pipeline/node-benchmark/results.d.ts +52 -0
- package/dist/pipeline/node-benchmark/schemas.d.ts +848 -0
- package/dist/pipeline/node-benchmark/step-tree.d.ts +23 -0
- package/dist/{sleep-B0JG7-_F.mjs → sleep-Cr5YOk3m.mjs} +1335 -121
- package/dist/{sleep-az1qiFzK.js → sleep-i3kl-0Na.js} +1575 -121
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import type { CustomActionCaller, CustomActionsSpec } from '../capabilities/cust
|
|
|
4
4
|
import type { PipelineSlot } from '../types/pipeline.js';
|
|
5
5
|
import type { AddonDataPlane } from './addon-data-plane.js';
|
|
6
6
|
import type { AddonPeerBytes } from './addon-peer-bytes.js';
|
|
7
|
+
import type { SecretsAccess } from '../keychain/secrets-access.js';
|
|
7
8
|
import type { CapabilityDeclaration } from './capability.js';
|
|
8
9
|
import type { ConfigUISchemaWithValues, FieldProbeResult } from './config-ui.js';
|
|
9
10
|
import type { IEventBus, ReadinessScope } from './event-bus.js';
|
|
@@ -202,6 +203,14 @@ export interface IKernelServices {
|
|
|
202
203
|
* dispatch from remote dispatch without a direct Moleculer dependency.
|
|
203
204
|
*/
|
|
204
205
|
readonly localNodeId?: string;
|
|
206
|
+
/**
|
|
207
|
+
* The runner id hosting an addon — its `execution.group`, else itself
|
|
208
|
+
* (`resolveRunnerId`). Hub in-process contexts only: the keychain uses it to
|
|
209
|
+
* accept a grouped runner's transport stamp (the group id) for the refs of
|
|
210
|
+
* the addons that runner hosts. Absent elsewhere; callers fall back to
|
|
211
|
+
* identity.
|
|
212
|
+
*/
|
|
213
|
+
readonly runnerIdForAddon?: (addonId: string) => string;
|
|
205
214
|
/**
|
|
206
215
|
* Local storage provider — direct filesystem access with zero serialization.
|
|
207
216
|
* Use for file I/O (write/read with Buffer data) that can't go through tRPC.
|
|
@@ -695,6 +704,17 @@ export interface AddonContext<TConfig = Record<string, unknown>> {
|
|
|
695
704
|
* (tests, tooling), which is why the field is optional.
|
|
696
705
|
*/
|
|
697
706
|
readonly peerBytes?: AddonPeerBytes;
|
|
707
|
+
/**
|
|
708
|
+
* This addon's keychain values (D787). The owner is the addon's own id,
|
|
709
|
+
* closed over by the kernel; nothing passed to it can change that.
|
|
710
|
+
* `BaseAddon` uses it for every secret field of its schema (a marker in the
|
|
711
|
+
* blob, the value here) — reach for it directly only for values that are
|
|
712
|
+
* not settings (a business collection's credentials, a minted token).
|
|
713
|
+
*
|
|
714
|
+
* Absent when the addon is loaded in isolation (tests, tooling) or on a
|
|
715
|
+
* kernel that predates the keychain.
|
|
716
|
+
*/
|
|
717
|
+
readonly secrets?: SecretsAccess;
|
|
698
718
|
/**
|
|
699
719
|
* Runtime accessor for the capability registry — exposed to addons that
|
|
700
720
|
* need to enumerate peers of a collection cap (e.g. `turn-provider`) or
|
|
@@ -829,6 +849,11 @@ export interface ICamstackAddon {
|
|
|
829
849
|
* persisted store. */
|
|
830
850
|
getGlobalSettings?(overlay?: Record<string, unknown>, cap?: string, nodeId?: string): Promise<ConfigUISchemaWithValues | null>;
|
|
831
851
|
updateGlobalSettings?(patch: Record<string, unknown>, nodeId?: string): Promise<void>;
|
|
852
|
+
/**
|
|
853
|
+
* The same write, made by the OPERATOR (D787): the kernel's settings
|
|
854
|
+
* dispatch prefers it when the addon has it.
|
|
855
|
+
*/
|
|
856
|
+
updateGlobalSettingsAsOperator?(patch: Record<string, unknown>, nodeId?: string): Promise<void>;
|
|
832
857
|
/**
|
|
833
858
|
* Integration-level settings — the subset of the global schema the addon
|
|
834
859
|
* declared as its INTEGRATION's configuration (`BaseAddon.
|
|
@@ -35,11 +35,23 @@ import type { ConfigField } from './config-ui.js';
|
|
|
35
35
|
export declare const REDACTED_SECRET = "__camstack_redacted__";
|
|
36
36
|
/** Is this field's stored value a credential? */
|
|
37
37
|
export declare function isSecretConfigField(field: ConfigField): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Is this field's value stored in the KEYCHAIN (D787)? Every secret field is,
|
|
40
|
+
* by its TYPE, unless it opts out with `storage: 'inline'` — never by its name.
|
|
41
|
+
*/
|
|
42
|
+
export declare function isKeychainStoredField(field: ConfigField): boolean;
|
|
38
43
|
/**
|
|
39
44
|
* Every config key in `schema` whose value is a secret, including keys nested
|
|
40
45
|
* inside `group` / `sub-tabs` containers.
|
|
41
46
|
*/
|
|
42
47
|
export declare function collectSecretConfigKeys(schema: unknown): ReadonlySet<string>;
|
|
48
|
+
/**
|
|
49
|
+
* Every top-level config key whose value the KEYCHAIN stores (D787) —
|
|
50
|
+
* {@link collectSecretConfigKeys} minus `storage: 'inline'` and minus JSON
|
|
51
|
+
* blobs (see {@link isKeychainStoredField}). What a form sends as
|
|
52
|
+
* `secretKeys` so the owner's door knows which values to keep out of the blob.
|
|
53
|
+
*/
|
|
54
|
+
export declare function collectKeychainStoredKeys(schema: unknown): ReadonlySet<string>;
|
|
43
55
|
/**
|
|
44
56
|
* True when the value is a readable schema declaring at least one field of any
|
|
45
57
|
* kind. Lets a caller tell "this provider has no secrets" from "this provider's
|
|
@@ -148,6 +148,15 @@ export interface ConfigFieldBase {
|
|
|
148
148
|
* non-admin session. It costs nothing when the field is already a password.
|
|
149
149
|
*/
|
|
150
150
|
readonly secret?: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Where a SECRET field's value is stored (D787). Default for every field
|
|
153
|
+
* {@link isSecretConfigField} selects (`type: 'password'` or `secret: true`):
|
|
154
|
+
* `'keychain'` — the settings blob holds a `{"$secret": ref}` marker and the
|
|
155
|
+
* value lives in the encrypted keychain; the form sees "set / not set",
|
|
156
|
+
* never the value. `'inline'` opts out, for a NON-secret value that is merely
|
|
157
|
+
* drawn masked. Decided by the field's TYPE and this flag — never its name.
|
|
158
|
+
*/
|
|
159
|
+
readonly storage?: 'keychain' | 'inline';
|
|
151
160
|
/** Per-model knob applicability — see {@link ConfigFieldModelApplicability}. */
|
|
152
161
|
readonly modelApplicability?: ConfigFieldModelApplicability;
|
|
153
162
|
/**
|
|
@@ -745,6 +754,14 @@ export interface ConfigEditableArrayField extends ConfigFieldBase {
|
|
|
745
754
|
* collapse to an em-dash. Falls back to `Row N` when not provided.
|
|
746
755
|
*/
|
|
747
756
|
readonly rowTitleTemplate?: string;
|
|
757
|
+
/**
|
|
758
|
+
* The row field that IDENTIFIES a row (default `'id'`). A secret inside a row
|
|
759
|
+
* is stored under a ref addressed by this id — never by index — so a reorder
|
|
760
|
+
* keeps every ref, a removed row's refs are deleted, and a duplicated row's
|
|
761
|
+
* secret is "not set" rather than copied (D787). A row saved without one has
|
|
762
|
+
* an id minted for it.
|
|
763
|
+
*/
|
|
764
|
+
readonly itemIdKey?: string;
|
|
748
765
|
}
|
|
749
766
|
export interface ConfigModelSelectorField extends ConfigFieldBase {
|
|
750
767
|
type: 'model-selector';
|
|
@@ -1096,4 +1113,15 @@ export declare function resolveHydratedFieldValue(schema: unknown, key: string):
|
|
|
1096
1113
|
* the schema default or `null`. This mirrors the old backend behaviour where
|
|
1097
1114
|
* `FormBuilder` rendered `values[key] ?? field.default`.
|
|
1098
1115
|
*/
|
|
1099
|
-
|
|
1116
|
+
/** How `hydrateSchema` treats keychain-stored secret fields (D789). */
|
|
1117
|
+
export interface HydrateSchemaOptions {
|
|
1118
|
+
/**
|
|
1119
|
+
* `true` — the form is the addon's OWN settings form, saved through the
|
|
1120
|
+
* keychain-aware `BaseAddon` path: a pre-keychain inline value is shown as
|
|
1121
|
+
* "set" too, never as text. Omitted — only a keychain MARKER becomes a view;
|
|
1122
|
+
* an inline value hydrates as before, because forms saved through other
|
|
1123
|
+
* paths (a camera driver's settings) need the value they were given.
|
|
1124
|
+
*/
|
|
1125
|
+
readonly inlineSecretsAsViews?: boolean;
|
|
1126
|
+
}
|
|
1127
|
+
export declare function hydrateSchema(schema: ConfigUISchema, values: Record<string, unknown>, options?: HydrateSchemaOptions): ConfigUISchemaWithValues;
|
|
@@ -743,6 +743,16 @@ export interface TopologyNode {
|
|
|
743
743
|
*/
|
|
744
744
|
export type StreamHealthReason = 'stale-timeout' | 'recovered' | 'first-packet' | 'suspended' | 'resumed' | 'broker-destroyed';
|
|
745
745
|
/** All known event categories with their typed payloads */
|
|
746
|
+
/**
|
|
747
|
+
* `keychain.changed` — a value was put or deleted. Never carries the value.
|
|
748
|
+
*/
|
|
749
|
+
export type KeychainChangedPayload = {
|
|
750
|
+
readonly ref: string;
|
|
751
|
+
readonly owner: string;
|
|
752
|
+
/** The version now stored; `null` after a delete. */
|
|
753
|
+
readonly version: number | null;
|
|
754
|
+
readonly change: 'put' | 'delete';
|
|
755
|
+
};
|
|
746
756
|
export interface EventCatalog {
|
|
747
757
|
'system.boot': {
|
|
748
758
|
mode: string;
|
|
@@ -753,6 +763,7 @@ export interface EventCatalog {
|
|
|
753
763
|
'system.restarting': PendingRestartMarkerPayload;
|
|
754
764
|
'system.restart-completed': PendingRestartMarkerPayload;
|
|
755
765
|
'system.tls-cert-changed': TlsCertChangedPayload;
|
|
766
|
+
'keychain.changed': KeychainChangedPayload;
|
|
756
767
|
'update.available': {
|
|
757
768
|
/**
|
|
758
769
|
* `addon` and `server` are npm packages a node can install for itself.
|
|
@@ -305,4 +305,22 @@ export interface IPipelineRunnerProvider {
|
|
|
305
305
|
}): Promise<{
|
|
306
306
|
released: number;
|
|
307
307
|
}>;
|
|
308
|
+
/**
|
|
309
|
+
* Node benchmark measurement on THIS node (D791). See
|
|
310
|
+
* `pipelineRunnerCapability.startNodeBench` for the contract.
|
|
311
|
+
*/
|
|
312
|
+
startNodeBench(input: import('../pipeline/node-benchmark/schemas.js').NodeBenchRunnerInputRaw): Promise<{
|
|
313
|
+
accepted: true;
|
|
314
|
+
runId: string;
|
|
315
|
+
}>;
|
|
316
|
+
/** The run's status; `null` when this runner does not know `runId`. */
|
|
317
|
+
getNodeBenchStatus(input: {
|
|
318
|
+
readonly runId: string;
|
|
319
|
+
}): Promise<import('../pipeline/node-benchmark/schemas.js').NodeBenchRunnerStatus | null>;
|
|
320
|
+
/** Stop the run; what it finished is kept. */
|
|
321
|
+
abortNodeBench(input: {
|
|
322
|
+
readonly runId: string;
|
|
323
|
+
}): Promise<{
|
|
324
|
+
aborted: boolean;
|
|
325
|
+
}>;
|
|
308
326
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { clearKnownSecrets, forgetKnownSecret, knownSecretCount, MIN_SCRUBBED_LENGTH, registerKnownSecret, scrubKnownSecrets, shortSecretRefHash, } from './known-secrets.js';
|
|
2
|
+
export { classifySecretWrite, isSecretClear, isSecretFieldView, isSecretMarker, isSecretMarkerLike, makeSecretMarker, SECRET_CLEAR, type SecretClearInstruction, type SecretFieldView, type SecretMarker, type SecretWriteIntent, toSecretFieldView, } from './secret-marker.js';
|
|
3
|
+
export { arrayItemFieldPath, buildSecretRef, formatSecretScope, globalSecretScope, isSecretRef, normalizeSecretOwner, parseSecretRef, SECRET_REF_SCHEME, type SecretRefParts, type SecretScope, } from './secret-ref.js';
|
|
4
|
+
export { createSecretsAccess, type KeychainTransport, REVALIDATE_AFTER_MS, type SecretsAccess, type SecretsAccessOptions, } from './secrets-access.js';
|
|
5
|
+
export { resolveSecretViewsIn } from './resolve-secret-views.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-process set of secret VALUES this process has resolved, so the log
|
|
3
|
+
* pipeline can scrub them wherever they turn up — a library's error message,
|
|
4
|
+
* an argv a spawn logged, a URL someone interpolated.
|
|
5
|
+
*
|
|
6
|
+
* Key-name redaction cannot catch `Command failed: cloudflared --token eyJ…`:
|
|
7
|
+
* the value is in a message, not under a named key. The only thing that can is
|
|
8
|
+
* knowing the value. `ctx.secrets` registers every value it resolves (and every
|
|
9
|
+
* value it stores), and `scrubKnownSecrets` replaces each occurrence with
|
|
10
|
+
* `‹secret:<hash of its ref>›` — enough to say WHICH secret leaked, nothing of
|
|
11
|
+
* what it is.
|
|
12
|
+
*
|
|
13
|
+
* Process-global on purpose: a forked runner scrubs its own logs before they
|
|
14
|
+
* leave over UDS, and hub-main scrubs what it resolved. Values shorter than
|
|
15
|
+
* {@link MIN_SCRUBBED_LENGTH} are not registered — a 4-digit PIN would turn
|
|
16
|
+
* every timestamp containing it into a redaction.
|
|
17
|
+
*/
|
|
18
|
+
/** Below this length a value is too likely to collide with ordinary text. */
|
|
19
|
+
export declare const MIN_SCRUBBED_LENGTH = 8;
|
|
20
|
+
/** FNV-1a, 32-bit, as 8 hex chars — a label, not a security property. */
|
|
21
|
+
export declare function shortSecretRefHash(ref: string): string;
|
|
22
|
+
/** Remember a value this process holds. A no-op for short or empty values. */
|
|
23
|
+
export declare function registerKnownSecret(value: string, ref: string): void;
|
|
24
|
+
/** Forget a value (it was replaced or deleted). */
|
|
25
|
+
export declare function forgetKnownSecret(value: string): void;
|
|
26
|
+
/** How many values are registered — for tests and the status page. */
|
|
27
|
+
export declare function knownSecretCount(): number;
|
|
28
|
+
/** Test seam: drop every registered value. */
|
|
29
|
+
export declare function clearKnownSecrets(): void;
|
|
30
|
+
/** Replace every registered value inside `text` with its label. */
|
|
31
|
+
export declare function scrubKnownSecrets(text: string): string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** The marker a settings blob stores in place of a secret value. */
|
|
2
|
+
export interface SecretMarker {
|
|
3
|
+
readonly $secret: string;
|
|
4
|
+
readonly v: 1;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* What a read surface hands the UI for a secret field that holds something.
|
|
8
|
+
* `$secret` is `null` when the stored value predates the keychain (it is still
|
|
9
|
+
* plaintext in the blob until the operator's one-off migration moves it): the
|
|
10
|
+
* UI draws "set" either way and the value never leaves the hub.
|
|
11
|
+
*/
|
|
12
|
+
export interface SecretFieldView {
|
|
13
|
+
readonly $secret: string | null;
|
|
14
|
+
readonly v: 1;
|
|
15
|
+
readonly set: true;
|
|
16
|
+
/** The stored value is still inline plaintext (pre-keychain). */
|
|
17
|
+
readonly inline?: true;
|
|
18
|
+
}
|
|
19
|
+
/** The explicit "remove this secret" instruction a form sends. */
|
|
20
|
+
export interface SecretClearInstruction {
|
|
21
|
+
readonly $secretClear: true;
|
|
22
|
+
}
|
|
23
|
+
export declare const SECRET_CLEAR: SecretClearInstruction;
|
|
24
|
+
export declare function makeSecretMarker(ref: string): SecretMarker;
|
|
25
|
+
/** A stored marker: an object whose `$secret` is a non-empty string. */
|
|
26
|
+
export declare function isSecretMarker(value: unknown): value is SecretMarker;
|
|
27
|
+
/** Anything carrying a `$secret` key — a stored marker or a UI view of one. */
|
|
28
|
+
export declare function isSecretMarkerLike(value: unknown): boolean;
|
|
29
|
+
export declare function isSecretClear(value: unknown): value is SecretClearInstruction;
|
|
30
|
+
export declare function isSecretFieldView(value: unknown): value is SecretFieldView;
|
|
31
|
+
/**
|
|
32
|
+
* The UI view of a stored secret field value: a marker becomes "set", inline
|
|
33
|
+
* plaintext becomes "set (inline)", nothing becomes `null`. A value never
|
|
34
|
+
* survives this function.
|
|
35
|
+
*/
|
|
36
|
+
export declare function toSecretFieldView(stored: unknown): SecretFieldView | null;
|
|
37
|
+
/** How an incoming value for a secret path is to be treated. */
|
|
38
|
+
export type SecretWriteIntent = {
|
|
39
|
+
readonly kind: 'keep';
|
|
40
|
+
} | {
|
|
41
|
+
readonly kind: 'clear';
|
|
42
|
+
} | {
|
|
43
|
+
readonly kind: 'set';
|
|
44
|
+
readonly value: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Classify an incoming value for a secret path. Only a non-empty string that is
|
|
48
|
+
* not a redaction token sets a value; only {@link SECRET_CLEAR} clears.
|
|
49
|
+
* Everything else — a marker (ours or anyone's), a UI view, `''`, `null`,
|
|
50
|
+
* `undefined`, a redaction token, a non-string — keeps what is stored.
|
|
51
|
+
*/
|
|
52
|
+
export declare function classifySecretWrite(incoming: unknown): SecretWriteIntent;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
3
|
+
*
|
|
4
|
+
* A ref is DETERMINISTIC from where the value lives — the owner addon, the
|
|
5
|
+
* scope (global, one node, one device, one integration, one row of a business
|
|
6
|
+
* collection) and the field path inside that scope — so saving the same field
|
|
7
|
+
* twice updates the same ref, and the ref itself says which settings blob
|
|
8
|
+
* points at it. It is also the AAD of the value's ciphertext: a ciphertext
|
|
9
|
+
* copied onto another ref does not decrypt.
|
|
10
|
+
*
|
|
11
|
+
* Array items are addressed by ITEM ID, never by index
|
|
12
|
+
* (`providers[authentik].clientSecret`), so reordering a list keeps every ref.
|
|
13
|
+
*
|
|
14
|
+
* Pure and browser-safe: no node builtins, no I/O.
|
|
15
|
+
*/
|
|
16
|
+
/** The URI scheme of every keychain ref. */
|
|
17
|
+
export declare const SECRET_REF_SCHEME = "secret://";
|
|
18
|
+
/** Where, inside its owner, a secret lives. */
|
|
19
|
+
export type SecretScope = {
|
|
20
|
+
readonly kind: 'global';
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: 'node';
|
|
23
|
+
readonly nodeId: string;
|
|
24
|
+
} | {
|
|
25
|
+
readonly kind: 'device';
|
|
26
|
+
readonly deviceId: number;
|
|
27
|
+
} | {
|
|
28
|
+
readonly kind: 'integration';
|
|
29
|
+
readonly integrationId: string;
|
|
30
|
+
} | {
|
|
31
|
+
readonly kind: 'row';
|
|
32
|
+
readonly collection: string;
|
|
33
|
+
readonly rowId: string;
|
|
34
|
+
};
|
|
35
|
+
/** A ref, taken apart. */
|
|
36
|
+
export interface SecretRefParts {
|
|
37
|
+
readonly owner: string;
|
|
38
|
+
readonly scope: SecretScope;
|
|
39
|
+
readonly fieldPath: string;
|
|
40
|
+
}
|
|
41
|
+
/** The scope every addon-wide (not per-node, not per-device) setting lives in. */
|
|
42
|
+
export declare function globalSecretScope(): SecretScope;
|
|
43
|
+
/**
|
|
44
|
+
* An addon id as a ref owner. A hub in-process builtin's context id is
|
|
45
|
+
* `addon:<id>`; the owner of its secrets is `<id>`, exactly as for the same
|
|
46
|
+
* addon running forked.
|
|
47
|
+
*/
|
|
48
|
+
export declare function normalizeSecretOwner(addonId: string): string;
|
|
49
|
+
/** The scope as it appears inside a ref (`global`, `node/<id>`, …). */
|
|
50
|
+
export declare function formatSecretScope(scope: SecretScope): string;
|
|
51
|
+
/**
|
|
52
|
+
* The field path of a value inside an array item, addressed by the item's id:
|
|
53
|
+
* `arrayItemFieldPath('providers', 'authentik', 'clientSecret')` →
|
|
54
|
+
* `providers[authentik].clientSecret`.
|
|
55
|
+
*/
|
|
56
|
+
export declare function arrayItemFieldPath(arrayKey: string, itemId: string, fieldKey: string): string;
|
|
57
|
+
/** Build a ref. Throws on an empty owner or field path — both are bugs. */
|
|
58
|
+
export declare function buildSecretRef(parts: SecretRefParts): string;
|
|
59
|
+
/** True when `value` is shaped like a keychain ref (not that it exists). */
|
|
60
|
+
export declare function isSecretRef(value: unknown): value is string;
|
|
61
|
+
/** Take a ref apart; `null` when it is not a well-formed ref. */
|
|
62
|
+
export declare function parseSecretRef(ref: string): SecretRefParts | null;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ctx.secrets` — an addon's handle on its OWN keychain values.
|
|
3
|
+
*
|
|
4
|
+
* The owner is closed over at construction (the addon's id); nothing a caller
|
|
5
|
+
* passes can change it. How the call travels is the transport's business:
|
|
6
|
+
*
|
|
7
|
+
* - hub in-process builtin: the provider is in the same process, so the
|
|
8
|
+
* transport states the owner explicitly (`callerAddonId`) — the one place a
|
|
9
|
+
* claim is trusted, because no wire is involved;
|
|
10
|
+
* - forked runner / agent addon: the transport sends the bare input and the
|
|
11
|
+
* parent (or the agent, through `$hub-cap-fwd`) stamps who is calling.
|
|
12
|
+
*
|
|
13
|
+
* The cache holds `ref → {value, version}` with no expiry while the version is
|
|
14
|
+
* current. `keychain.changed` drops an entry; an entry older than
|
|
15
|
+
* {@link REVALIDATE_AFTER_MS} is re-fetched on its next use (the safety net
|
|
16
|
+
* for a dropped event, D8). A failed fetch answers the last good value when
|
|
17
|
+
* there is one — a locked keychain never turns a working credential into an
|
|
18
|
+
* empty one — and `unavailable` otherwise, never `missing`.
|
|
19
|
+
*/
|
|
20
|
+
import type { KeychainResolveResult, KeychainState } from '../capabilities/keychain.cap.js';
|
|
21
|
+
import type { IEventBus } from '../interfaces/event-bus.js';
|
|
22
|
+
import { type SecretScope } from './secret-ref.js';
|
|
23
|
+
/** Re-fetch a cached value after this long, even with no change event. */
|
|
24
|
+
export declare const REVALIDATE_AFTER_MS: number;
|
|
25
|
+
/** The four calls a {@link SecretsAccess} makes. `callerAddonId` is set only in-process. */
|
|
26
|
+
export interface KeychainTransport {
|
|
27
|
+
put(input: {
|
|
28
|
+
readonly ref: string;
|
|
29
|
+
readonly value: string;
|
|
30
|
+
}): Promise<{
|
|
31
|
+
readonly ref: string;
|
|
32
|
+
readonly version: number;
|
|
33
|
+
}>;
|
|
34
|
+
resolve(input: {
|
|
35
|
+
readonly refs: readonly string[];
|
|
36
|
+
}): Promise<KeychainResolveResult>;
|
|
37
|
+
delete(input: {
|
|
38
|
+
readonly refs: readonly string[];
|
|
39
|
+
}): Promise<{
|
|
40
|
+
readonly deleted: number;
|
|
41
|
+
}>;
|
|
42
|
+
getState(): Promise<KeychainState>;
|
|
43
|
+
}
|
|
44
|
+
export interface SecretsAccess {
|
|
45
|
+
/** The owner every ref of this handle belongs to. */
|
|
46
|
+
readonly owner: string;
|
|
47
|
+
/** Build this owner's ref for a field. */
|
|
48
|
+
ref(scope: SecretScope, fieldPath: string): string;
|
|
49
|
+
/** Resolve refs in one call; cached values are served without one. */
|
|
50
|
+
resolve(refs: readonly string[]): Promise<KeychainResolveResult>;
|
|
51
|
+
/** Store a value. Throws while the keychain is locked. */
|
|
52
|
+
put(ref: string, value: string): Promise<number>;
|
|
53
|
+
/** Remove refs. Unknown refs are ignored. */
|
|
54
|
+
delete(refs: readonly string[]): Promise<number>;
|
|
55
|
+
/** Whether values can be stored right now. */
|
|
56
|
+
state(): Promise<KeychainState>;
|
|
57
|
+
/**
|
|
58
|
+
* Subscribe to changes of refs this handle has served. The handler runs
|
|
59
|
+
* after the cache dropped the ref. Returns the unsubscribe.
|
|
60
|
+
*/
|
|
61
|
+
onChange(handler: (ref: string) => void): () => void;
|
|
62
|
+
/**
|
|
63
|
+
* In-process SYSTEM builtins only (device-manager, storage-orchestrator):
|
|
64
|
+
* act as a door for another owner's values. `null` everywhere else — a
|
|
65
|
+
* forked addon has no way to state an identity the transport would accept.
|
|
66
|
+
*/
|
|
67
|
+
readonly forOwner: ((owner: string) => SecretsAccess) | null;
|
|
68
|
+
}
|
|
69
|
+
export interface SecretsAccessOptions {
|
|
70
|
+
readonly owner: string;
|
|
71
|
+
readonly transport: KeychainTransport;
|
|
72
|
+
/** Builds the transport for another owner — in-process system builtins only. */
|
|
73
|
+
readonly transportForOwner?: (owner: string) => KeychainTransport;
|
|
74
|
+
readonly eventBus?: Pick<IEventBus, 'subscribe'>;
|
|
75
|
+
readonly now?: () => number;
|
|
76
|
+
}
|
|
77
|
+
export declare function createSecretsAccess(opts: SecretsAccessOptions): SecretsAccess;
|
|
@@ -56,8 +56,8 @@ import type { IScopedLogger, LogExtras } from '../interfaces/logging.js';
|
|
|
56
56
|
* to read it later.
|
|
57
57
|
*/
|
|
58
58
|
export declare const LogChannelLevelSchema: z.ZodEnum<{
|
|
59
|
-
error: "error";
|
|
60
59
|
info: "info";
|
|
60
|
+
error: "error";
|
|
61
61
|
warn: "warn";
|
|
62
62
|
}>;
|
|
63
63
|
/**
|
|
@@ -68,8 +68,8 @@ export declare const LogChannelDescriptorSchema: z.ZodObject<{
|
|
|
68
68
|
name: z.ZodString;
|
|
69
69
|
description: z.ZodString;
|
|
70
70
|
defaultLevel: z.ZodEnum<{
|
|
71
|
-
error: "error";
|
|
72
71
|
info: "info";
|
|
72
|
+
error: "error";
|
|
73
73
|
warn: "warn";
|
|
74
74
|
}>;
|
|
75
75
|
perDevice: z.ZodBoolean;
|