@camstack/types 1.2.326 → 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 +2 -0
- package/dist/addon.js +54 -4
- package/dist/addon.mjs +27 -5
- package/dist/capabilities/addon-routes.cap.d.ts +2 -2
- 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 +9 -0
- package/dist/capabilities/enum-sensor.cap.d.ts +4 -4
- package/dist/capabilities/index.d.ts +7 -3
- 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/local-network.cap.d.ts +2 -2
- package/dist/capabilities/log-channels.cap.d.ts +1 -1
- package/dist/capabilities/log-destination.cap.d.ts +5 -5
- package/dist/capabilities/login-method.cap.d.ts +3 -0
- package/dist/capabilities/mesh-network.cap.d.ts +3 -3
- package/dist/capabilities/network-access.cap.d.ts +7 -7
- 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 +1127 -104
- package/dist/capabilities/pipeline-orchestrator.cap.d.ts +384 -0
- package/dist/capabilities/pipeline-runner.cap.d.ts +145 -2
- package/dist/capabilities/sso-bridge.cap.d.ts +6 -0
- package/dist/capabilities/storage.cap.d.ts +1 -1
- package/dist/capabilities/system.cap.d.ts +21 -21
- package/dist/capabilities/user-management.cap.d.ts +53 -1
- package/dist/capabilities/webrtc-session.cap.d.ts +3 -3
- package/dist/client.js +38 -1
- package/dist/client.mjs +38 -1
- package/dist/device/device-context.d.ts +11 -1
- package/dist/device/display-icon-vocabulary.d.ts +7 -0
- package/dist/device/index.d.ts +2 -1
- package/dist/device/zod-secret.d.ts +35 -0
- 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 +376 -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 +5 -3
- package/dist/health/wiring-health.d.ts +8 -8
- package/dist/index.d.ts +8 -3
- package/dist/index.js +1970 -110
- package/dist/index.mjs +1849 -107
- 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 +60 -3
- 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-BNeXenvr.mjs → sleep-Cr5YOk3m.mjs} +1337 -33
- package/dist/{sleep-DPar9AHl.js → sleep-i3kl-0Na.js} +1628 -66
- 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
|
|
@@ -80,7 +80,7 @@ export interface ConfigSection {
|
|
|
80
80
|
readonly immediate?: boolean;
|
|
81
81
|
readonly fields: readonly ConfigField[];
|
|
82
82
|
}
|
|
83
|
-
export type ConfigField = ConfigTextField | ConfigNumberField | ConfigBooleanField | ConfigSelectField | ConfigMultiSelectField | ConfigTimezoneField | ConfigDateTimeField | ConfigColorField | ConfigPasswordField | ConfigTextAreaField | ConfigSliderField | ConfigTagsField | ConfigScheduleField | ConfigGroupField | ConfigSubTabsField | ConfigSeparatorField | ConfigInfoField | ConfigQrCodeField | ConfigObjectArrayField | ConfigEditableArrayField | ConfigModelSelectorField | ConfigStorageLocationField | ConfigProbeField | ConfigButtonField | ConfigNodeSelectField | ConfigNodeMultiSelectField | ConfigDeviceSelectField | ConfigDeviceMultiSelectField | ConfigNotificationTargetSelectField | ConfigNotificationTargetMultiSelectField | ConfigWidgetField | ConfigAddonActionSelectField | ConfigAddonActionButtonField | ConfigDeviceActionButtonField;
|
|
83
|
+
export type ConfigField = ConfigTextField | ConfigNumberField | ConfigBooleanField | ConfigSelectField | ConfigMultiSelectField | ConfigTimezoneField | ConfigDateTimeField | ConfigColorField | ConfigIconField | ConfigPasswordField | ConfigTextAreaField | ConfigSliderField | ConfigTagsField | ConfigScheduleField | ConfigGroupField | ConfigSubTabsField | ConfigSeparatorField | ConfigInfoField | ConfigQrCodeField | ConfigObjectArrayField | ConfigEditableArrayField | ConfigModelSelectorField | ConfigStorageLocationField | ConfigProbeField | ConfigButtonField | ConfigNodeSelectField | ConfigNodeMultiSelectField | ConfigDeviceSelectField | ConfigDeviceMultiSelectField | ConfigNotificationTargetSelectField | ConfigNotificationTargetMultiSelectField | ConfigWidgetField | ConfigAddonActionSelectField | ConfigAddonActionButtonField | ConfigDeviceActionButtonField;
|
|
84
84
|
/**
|
|
85
85
|
* Per-MODEL applicability of a pipeline-step setting (D214).
|
|
86
86
|
*
|
|
@@ -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
|
/**
|
|
@@ -389,6 +398,22 @@ export interface ConfigColorField extends ConfigFieldBase {
|
|
|
389
398
|
type: 'color';
|
|
390
399
|
presets?: string[];
|
|
391
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* An icon chosen from the shared display-icon vocabulary (D780) —
|
|
403
|
+
* `DISPLAY_ICON_VOCABULARY` in `device/display-icon-vocabulary.ts`, the same
|
|
404
|
+
* keys the device and room pickers store. The form renders the shared
|
|
405
|
+
* `<IconPicker>`; the stored value is a vocabulary KEY, never a free-text
|
|
406
|
+
* lucide name. `default` must itself be a vocabulary key: clearing the picker
|
|
407
|
+
* writes it back, and a stored value outside the vocabulary (a pre-D780
|
|
408
|
+
* free-text name) is drawn as the default — readers resolve it with
|
|
409
|
+
* `resolveDisplayIconKey(value, default)`.
|
|
410
|
+
*
|
|
411
|
+
* COMPILE-TIME ONLY on the wire (`z.custom<ConfigField>()`): a form that
|
|
412
|
+
* predates this type renders it through its default branch.
|
|
413
|
+
*/
|
|
414
|
+
export interface ConfigIconField extends ConfigFieldBase {
|
|
415
|
+
readonly type: 'icon';
|
|
416
|
+
}
|
|
392
417
|
/** Select a single cluster node from the live topology. Options are
|
|
393
418
|
* populated dynamically by the UI via useClusterNodes — no server-side
|
|
394
419
|
* option list needed. */
|
|
@@ -700,7 +725,7 @@ export interface ConfigOption {
|
|
|
700
725
|
*
|
|
701
726
|
* Constraints inside `itemFields`:
|
|
702
727
|
* - Only scalar field types are supported (text/number/select/
|
|
703
|
-
* multiselect/password/boolean/textarea/color/tags/slider). Nested
|
|
728
|
+
* multiselect/password/boolean/textarea/color/icon/tags/slider). Nested
|
|
704
729
|
* groups, sub-tabs, probes, button actions, and other arrays are
|
|
705
730
|
* intentionally rejected — they conflate row-state with section-
|
|
706
731
|
* state and the FormBuilder's flat onChange contract can't carry
|
|
@@ -729,6 +754,14 @@ export interface ConfigEditableArrayField extends ConfigFieldBase {
|
|
|
729
754
|
* collapse to an em-dash. Falls back to `Row N` when not provided.
|
|
730
755
|
*/
|
|
731
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;
|
|
732
765
|
}
|
|
733
766
|
export interface ConfigModelSelectorField extends ConfigFieldBase {
|
|
734
767
|
type: 'model-selector';
|
|
@@ -876,6 +909,17 @@ export interface ConfigAddonActionButtonField extends ConfigFieldBase {
|
|
|
876
909
|
* No-op on a form with no device.
|
|
877
910
|
*/
|
|
878
911
|
includeDeviceId?: boolean;
|
|
912
|
+
/**
|
|
913
|
+
* Ask the operator for one value when the button is pressed and send it as
|
|
914
|
+
* `input[param]`. For a one-shot argument that must not live in the saved
|
|
915
|
+
* settings (ids to delete, a code to send). Cancelling runs nothing.
|
|
916
|
+
*/
|
|
917
|
+
prompt?: {
|
|
918
|
+
readonly param: string;
|
|
919
|
+
readonly title?: string;
|
|
920
|
+
readonly message?: string;
|
|
921
|
+
readonly placeholder?: string;
|
|
922
|
+
};
|
|
879
923
|
/** Toast/inline success message template. `{key}` placeholders are
|
|
880
924
|
* resolved against the action result. */
|
|
881
925
|
successMessage?: string;
|
|
@@ -943,6 +987,8 @@ export type ConfigFieldWithValue = (ConfigTextField & {
|
|
|
943
987
|
readonly value: unknown;
|
|
944
988
|
}) | (ConfigColorField & {
|
|
945
989
|
readonly value: unknown;
|
|
990
|
+
}) | (ConfigIconField & {
|
|
991
|
+
readonly value: unknown;
|
|
946
992
|
}) | (ConfigPasswordField & {
|
|
947
993
|
readonly value: unknown;
|
|
948
994
|
}) | (ConfigTextAreaField & {
|
|
@@ -1067,4 +1113,15 @@ export declare function resolveHydratedFieldValue(schema: unknown, key: string):
|
|
|
1067
1113
|
* the schema default or `null`. This mirrors the old backend behaviour where
|
|
1068
1114
|
* `FormBuilder` rendered `values[key] ?? field.default`.
|
|
1069
1115
|
*/
|
|
1070
|
-
|
|
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;
|