@calltelemetry/ct-lab-mcp 0.8.144 → 0.8.146

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 (52) hide show
  1. package/README.md +13 -0
  2. package/dist/src/appliance/executor.d.ts +1 -1
  3. package/dist/src/appliance/executor.js +1 -1
  4. package/dist/src/credentials/claim-secret.d.ts +116 -0
  5. package/dist/src/credentials/claim-secret.d.ts.map +1 -0
  6. package/dist/src/credentials/claim-secret.js +198 -0
  7. package/dist/src/credentials/claim-secret.js.map +1 -0
  8. package/dist/src/credentials/guest-credential.d.ts +32 -0
  9. package/dist/src/credentials/guest-credential.d.ts.map +1 -0
  10. package/dist/src/credentials/guest-credential.js +74 -0
  11. package/dist/src/credentials/guest-credential.js.map +1 -0
  12. package/dist/src/credentials/remediation.js +1 -1
  13. package/dist/src/credentials/remediation.js.map +1 -1
  14. package/dist/src/credentials/resolver.d.ts +7 -0
  15. package/dist/src/credentials/resolver.d.ts.map +1 -1
  16. package/dist/src/credentials/resolver.js +17 -0
  17. package/dist/src/credentials/resolver.js.map +1 -1
  18. package/dist/src/generated-version.d.ts +1 -1
  19. package/dist/src/generated-version.js +1 -1
  20. package/dist/src/lab-exec/claim-resolve.d.ts +194 -0
  21. package/dist/src/lab-exec/claim-resolve.d.ts.map +1 -1
  22. package/dist/src/lab-exec/claim-resolve.js +10 -0
  23. package/dist/src/lab-exec/claim-resolve.js.map +1 -1
  24. package/dist/src/stig/live.d.ts +16 -1
  25. package/dist/src/stig/live.d.ts.map +1 -1
  26. package/dist/src/stig/live.js +43 -14
  27. package/dist/src/stig/live.js.map +1 -1
  28. package/dist/src/stig/ports.d.ts +1 -1
  29. package/dist/src/stig/ports.d.ts.map +1 -1
  30. package/dist/src/stig/runner.js +1 -1
  31. package/dist/src/stig/runner.js.map +1 -1
  32. package/dist/src/tools/appliance-claim-tools.d.ts +7 -7
  33. package/dist/src/tools/appliance-exec.d.ts +2 -2
  34. package/dist/src/tools/claim-custody.d.ts.map +1 -1
  35. package/dist/src/tools/claim-custody.js +4 -0
  36. package/dist/src/tools/claim-custody.js.map +1 -1
  37. package/dist/src/tools/claim-kubernetes.d.ts +7 -2
  38. package/dist/src/tools/claim-kubernetes.d.ts.map +1 -1
  39. package/dist/src/tools/claim-kubernetes.js +51 -7
  40. package/dist/src/tools/claim-kubernetes.js.map +1 -1
  41. package/dist/src/tools/claimed-import-ova.d.ts +6 -6
  42. package/dist/src/tools/claimed-import-qcow.d.ts +2 -2
  43. package/dist/src/tools/guest-agent.js +1 -1
  44. package/dist/src/tools/guest-agent.js.map +1 -1
  45. package/dist/src/tools/license-tools.d.ts +8 -8
  46. package/dist/src/tools/network-tools.js +1 -1
  47. package/dist/src/tools/network-tools.js.map +1 -1
  48. package/dist/src/tools/provision-tools.d.ts +4 -4
  49. package/dist/src/tools/sideload-runner.d.ts +2 -2
  50. package/dist/src/tools/update-cli.d.ts +4 -4
  51. package/dist/src/tools/verify-demo.d.ts +1 -1
  52. package/package.json +1 -1
package/README.md CHANGED
@@ -911,3 +911,16 @@ issuing one.
911
911
  MIT © 2026 [Call Telemetry](https://calltelemetry.com) / Contributors
912
912
 
913
913
  See [resource admission and runtime activation](docs/runbooks/resource-admission.md) for compatibility fields and release verification.
914
+
915
+
916
+ ## Per-claim credential Secret (UAT-1663)
917
+
918
+ Dynamic guest credentials no longer go to Doppler. Each ApplianceClaim has one Kubernetes Secret
919
+ `claim-<claim name>-credentials` in `ct-lab` (label `calltelemetry.com/credential-kind: claim-guest`, ownerReference to the
920
+ claim so Kubernetes garbage-collects it; claim release/destroy also deletes it explicitly and idempotently). Allowed keys:
921
+ `credential.json`, `claim-entry.json`, `admin-password`, `ssh-password`, `ssh-private-key`, `LAB_RHEL8_RECOVERY`,
922
+ `LAB_RHEL8_PROVISIONING`, `LAB_RHEL8_OBSERVER`, `LAB_RHEL8_REGISTRATION`. `src/credentials/claim-secret.ts` is the only writer
923
+ (exact-name GET/POST/PATCH/DELETE through the hardened claim transport; no Secret list; a Secret without the credential-kind
924
+ label is never overwritten). `CredentialResolver.resolveGuestCredential({ claimName | vmUuid })` reads Secret-first and falls
925
+ back to the Doppler `LAB_VM_CREDENTIALS_<UUID>` entry only as a read-only result marked `legacy: true` for not-yet-migrated
926
+ guests. Doppler keeps static secrets only; `test/credentials/no-doppler-claim-writes.test.ts` fails if src/ can write to it.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Remote Appliance SSH Executor using pure-JS ssh2 Client
3
3
  *
4
- * Connects to Call Telemetry appliances on Port 2222 using Doppler-resolved credentials.
4
+ * Connects to Call Telemetry appliances on Port 2222 using credentials from the claim Secret claim-<name>-credentials (legacy Doppler read-only fallback).
5
5
  * Handles connect & execution timeouts, and guarantees output sanitization & secret redaction.
6
6
  */
7
7
  import { ApplianceExecParams, ApplianceExecResult, IApplianceExecutor } from "./types.js";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Remote Appliance SSH Executor using pure-JS ssh2 Client
3
3
  *
4
- * Connects to Call Telemetry appliances on Port 2222 using Doppler-resolved credentials.
4
+ * Connects to Call Telemetry appliances on Port 2222 using credentials from the claim Secret claim-<name>-credentials (legacy Doppler read-only fallback).
5
5
  * Handles connect & execution timeouts, and guarantees output sanitization & secret redaction.
6
6
  */
7
7
  import { logger } from "../utils/logger.js";
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Per-claim credential Secret (contract ct-lab-claim-credentials/v1).
3
+ *
4
+ * Dynamic guest credentials live in ONE Kubernetes Secret per ApplianceClaim in `ct-lab`, named
5
+ * `claim-<claim name>-credentials`, owned (ownerReference) by the claim so Kubernetes garbage-collects it.
6
+ * Doppler holds only static secrets; it is read here only as an explicitly marked legacy fallback by the resolver.
7
+ *
8
+ * Never logs or returns a value in an error; names and key names only.
9
+ */
10
+ import { CLAIM_SECRET_DATA_KEYS, type ClaimApi } from "../tools/claim-kubernetes.js";
11
+ export { CLAIM_SECRET_DATA_KEYS };
12
+ export type ClaimSecretKey = typeof CLAIM_SECRET_DATA_KEYS[number];
13
+ export type ClaimSecretWriter = "ct-appliance-operator" | "ct-qualification-controller" | "ct-lab-mcp" | "lab-inventory";
14
+ export declare const CLAIM_SECRET_NAMESPACE = "ct-lab";
15
+ export declare const CREDENTIAL_KIND_LABEL = "calltelemetry.com/credential-kind";
16
+ export declare const CREDENTIAL_KIND_VALUE = "claim-guest";
17
+ export declare const MANAGED_BY_LABEL = "app.kubernetes.io/managed-by";
18
+ export declare const CLAIM_LABEL = "calltelemetry.com/claim";
19
+ export declare const VM_UUID_LABEL = "calltelemetry.com/vm-uuid";
20
+ export declare const CLAIM_NAME_ANNOTATION = "calltelemetry.com/claim-name";
21
+ export declare const CLAIM_UID_ANNOTATION = "calltelemetry.com/claim-uid";
22
+ export declare const CREDENTIAL_KEY_ANNOTATION = "calltelemetry.com/credential-key";
23
+ export type ClaimSecretData = Partial<Record<ClaimSecretKey, string>>;
24
+ export interface ClaimSecretReadResult {
25
+ name: string;
26
+ data: ClaimSecretData;
27
+ keys: ClaimSecretKey[];
28
+ }
29
+ /** `claim-<name>-credentials`. Literal; a result over 253 characters or an invalid claim name is refused. */
30
+ export declare function claimCredentialSecretName(claimName: string): string;
31
+ /** Legacy Doppler key name for a guest: LAB_VM_CREDENTIALS_<UUID upper, - -> _>. */
32
+ export declare function legacyCredentialKey(vmUuid: string): string;
33
+ export declare function normalizeUuid(vmUuid: string): string;
34
+ /** Throws on an unknown key or a non-string/empty value. Never echoes a value. */
35
+ export declare function assertAllowedKeys(data: Record<string, unknown>): asserts data is ClaimSecretData;
36
+ export interface BuildSecretInput {
37
+ claimName: string;
38
+ data: ClaimSecretData;
39
+ managedBy?: ClaimSecretWriter;
40
+ claimUid?: string;
41
+ vmUuid?: string;
42
+ }
43
+ /** The full Secret for first creation. ownerReference only when the claim exists (claimUid given). */
44
+ export declare function buildClaimCredentialSecret(input: BuildSecretInput): {
45
+ apiVersion: string;
46
+ kind: string;
47
+ type: string;
48
+ metadata: {
49
+ ownerReferences?: {
50
+ apiVersion: string;
51
+ kind: string;
52
+ name: string;
53
+ uid: string;
54
+ controller: boolean;
55
+ blockOwnerDeletion: boolean;
56
+ }[] | undefined;
57
+ name: string;
58
+ namespace: string;
59
+ labels: Record<string, string>;
60
+ annotations: Record<string, string>;
61
+ };
62
+ data: {
63
+ [k: string]: string;
64
+ };
65
+ };
66
+ /** JSON merge-patch carrying only the keys the writer owns (other keys are untouched) plus identity metadata. */
67
+ export declare function buildClaimCredentialPatch(input: BuildSecretInput & {
68
+ adoptOwner?: boolean;
69
+ }): {
70
+ metadata: {
71
+ ownerReferences?: {
72
+ apiVersion: string;
73
+ kind: string;
74
+ name: string;
75
+ uid: string;
76
+ controller: boolean;
77
+ blockOwnerDeletion: boolean;
78
+ }[] | undefined;
79
+ labels: Record<string, string>;
80
+ annotations: Record<string, string>;
81
+ };
82
+ data: {
83
+ [k: string]: string;
84
+ };
85
+ };
86
+ export interface WriteResult {
87
+ action: "created" | "patched";
88
+ name: string;
89
+ keys: ClaimSecretKey[];
90
+ owned: boolean;
91
+ }
92
+ /**
93
+ * Create the claim's Secret or merge-patch the writer's keys into it. Refuses a Secret that lacks the
94
+ * `credential-kind: claim-guest` label. Without a live claim the Secret is created un-owned (the operator adopts it).
95
+ */
96
+ export declare function writeClaimCredentialSecret(input: {
97
+ claimName: string;
98
+ data: ClaimSecretData;
99
+ managedBy?: ClaimSecretWriter;
100
+ vmUuid?: string;
101
+ }, api?: ClaimApi): Promise<WriteResult>;
102
+ /** Read a claim's credential Secret (null when absent or when it is not a claim-guest Secret). Values are returned to the caller only. */
103
+ export declare function readClaimCredentialSecret(claimName: string, api?: ClaimApi): Promise<ClaimSecretReadResult | null>;
104
+ export interface DeleteResult {
105
+ name: string;
106
+ deleted: boolean;
107
+ error?: string;
108
+ }
109
+ /** Idempotent explicit delete (404 is fine). Never throws: GC by ownerReference is the backstop. */
110
+ export declare function deleteClaimCredentialSecret(claimName: string, api?: ClaimApi): Promise<DeleteResult>;
111
+ /** uuid -> claim name via the existing claim list (never a Secret list). Matches claim label/annotation/status vm uuid. */
112
+ export declare function findClaimNameByVmUuid(vmUuid: string, api?: ClaimApi): Promise<string | null>;
113
+ export declare function readClaimCredentialByVmUuid(vmUuid: string, api?: ClaimApi): Promise<(ClaimSecretReadResult & {
114
+ claimName: string;
115
+ }) | null>;
116
+ //# sourceMappingURL=claim-secret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim-secret.d.ts","sourceRoot":"","sources":["../../../src/credentials/claim-secret.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Per-claim credential Secret (contract ct-lab-claim-credentials/v1).
3
+ *
4
+ * Dynamic guest credentials live in ONE Kubernetes Secret per ApplianceClaim in `ct-lab`, named
5
+ * `claim-<claim name>-credentials`, owned (ownerReference) by the claim so Kubernetes garbage-collects it.
6
+ * Doppler holds only static secrets; it is read here only as an explicitly marked legacy fallback by the resolver.
7
+ *
8
+ * Never logs or returns a value in an error; names and key names only.
9
+ */
10
+ import { CLAIM_SECRETS_COLLECTION, CLAIM_SECRET_DATA_KEYS, CLAIM_SECRET_NAME, ClaimApiError, createClaimApi, } from "../tools/claim-kubernetes.js";
11
+ export { CLAIM_SECRET_DATA_KEYS };
12
+ export const CLAIM_SECRET_NAMESPACE = "ct-lab";
13
+ export const CREDENTIAL_KIND_LABEL = "calltelemetry.com/credential-kind";
14
+ export const CREDENTIAL_KIND_VALUE = "claim-guest";
15
+ export const MANAGED_BY_LABEL = "app.kubernetes.io/managed-by";
16
+ export const CLAIM_LABEL = "calltelemetry.com/claim";
17
+ export const VM_UUID_LABEL = "calltelemetry.com/vm-uuid";
18
+ export const CLAIM_NAME_ANNOTATION = "calltelemetry.com/claim-name";
19
+ export const CLAIM_UID_ANNOTATION = "calltelemetry.com/claim-uid";
20
+ export const CREDENTIAL_KEY_ANNOTATION = "calltelemetry.com/credential-key";
21
+ const CLAIMS_PATH = "/apis/calltelemetry.com/v1alpha1/namespaces/ct-lab/applianceclaims";
22
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
23
+ const LABEL_VALUE_RE = /^(?:[A-Za-z0-9](?:[A-Za-z0-9_.-]{0,61}[A-Za-z0-9])?)?$/;
24
+ /** `claim-<name>-credentials`. Literal; a result over 253 characters or an invalid claim name is refused. */
25
+ export function claimCredentialSecretName(claimName) {
26
+ const name = `claim-${claimName}-credentials`;
27
+ if (name.length > 253 || !CLAIM_SECRET_NAME.test(name) || !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/.test(claimName))
28
+ throw new Error("claim credential Secret name refused");
29
+ return name;
30
+ }
31
+ const secretPath = (claimName) => `${CLAIM_SECRETS_COLLECTION}/${claimCredentialSecretName(claimName)}`;
32
+ /** Legacy Doppler key name for a guest: LAB_VM_CREDENTIALS_<UUID upper, - -> _>. */
33
+ export function legacyCredentialKey(vmUuid) {
34
+ const id = normalizeUuid(vmUuid);
35
+ return `LAB_VM_CREDENTIALS_${id.toUpperCase().replace(/-/g, "_")}`;
36
+ }
37
+ export function normalizeUuid(vmUuid) {
38
+ const id = vmUuid.trim().toLowerCase();
39
+ if (!UUID_RE.test(id))
40
+ throw new Error("vm uuid must be a hyphenated SMBIOS UUID");
41
+ return id;
42
+ }
43
+ /** Throws on an unknown key or a non-string/empty value. Never echoes a value. */
44
+ export function assertAllowedKeys(data) {
45
+ for (const [key, value] of Object.entries(data)) {
46
+ if (!CLAIM_SECRET_DATA_KEYS.includes(key))
47
+ throw new Error(`claim credential Secret key refused: ${key}`);
48
+ if (typeof value !== "string" || value.length === 0)
49
+ throw new Error(`claim credential Secret value for ${key} must be a non-empty string`);
50
+ }
51
+ }
52
+ const b64 = (v) => Buffer.from(v, "utf8").toString("base64");
53
+ function ownerReference(claimName, claim) {
54
+ return { apiVersion: "calltelemetry.com/v1alpha1", kind: "ApplianceClaim", name: claimName, uid: claim.uid, controller: false, blockOwnerDeletion: false };
55
+ }
56
+ function metadataFor(claimName, managedBy, claim, vmUuid) {
57
+ const labels = { [CREDENTIAL_KIND_LABEL]: CREDENTIAL_KIND_VALUE, [MANAGED_BY_LABEL]: managedBy };
58
+ if (claimName.length <= 63 && LABEL_VALUE_RE.test(claimName))
59
+ labels[CLAIM_LABEL] = claimName;
60
+ const annotations = { [CLAIM_NAME_ANNOTATION]: claimName };
61
+ if (claim)
62
+ annotations[CLAIM_UID_ANNOTATION] = claim.uid;
63
+ if (vmUuid) {
64
+ const id = normalizeUuid(vmUuid);
65
+ labels[VM_UUID_LABEL] = id;
66
+ annotations[CREDENTIAL_KEY_ANNOTATION] = legacyCredentialKey(id);
67
+ }
68
+ return { labels, annotations };
69
+ }
70
+ /** The full Secret for first creation. ownerReference only when the claim exists (claimUid given). */
71
+ export function buildClaimCredentialSecret(input) {
72
+ assertAllowedKeys(input.data);
73
+ const claim = input.claimUid ? { uid: input.claimUid } : null;
74
+ const { labels, annotations } = metadataFor(input.claimName, input.managedBy ?? "ct-lab-mcp", claim, input.vmUuid);
75
+ return {
76
+ apiVersion: "v1", kind: "Secret", type: "Opaque",
77
+ metadata: {
78
+ name: claimCredentialSecretName(input.claimName), namespace: CLAIM_SECRET_NAMESPACE, labels, annotations,
79
+ ...(claim ? { ownerReferences: [ownerReference(input.claimName, claim)] } : {}),
80
+ },
81
+ data: Object.fromEntries(Object.entries(input.data).map(([k, v]) => [k, b64(v)])),
82
+ };
83
+ }
84
+ /** JSON merge-patch carrying only the keys the writer owns (other keys are untouched) plus identity metadata. */
85
+ export function buildClaimCredentialPatch(input) {
86
+ assertAllowedKeys(input.data);
87
+ const claim = input.claimUid ? { uid: input.claimUid } : null;
88
+ const { labels, annotations } = metadataFor(input.claimName, input.managedBy ?? "ct-lab-mcp", claim, input.vmUuid);
89
+ return {
90
+ metadata: { labels, annotations, ...(claim && input.adoptOwner ? { ownerReferences: [ownerReference(input.claimName, claim)] } : {}) },
91
+ data: Object.fromEntries(Object.entries(input.data).map(([k, v]) => [k, b64(v)])),
92
+ };
93
+ }
94
+ function isGuestSecret(secret) { return secret?.metadata?.labels?.[CREDENTIAL_KIND_LABEL] === CREDENTIAL_KIND_VALUE; }
95
+ async function liveClaim(api, claimName) {
96
+ const claim = await api.request(`${CLAIMS_PATH}/${claimName}`, "GET");
97
+ return typeof claim?.metadata?.uid === "string" && claim.metadata.uid ? { uid: claim.metadata.uid } : null;
98
+ }
99
+ /**
100
+ * Create the claim's Secret or merge-patch the writer's keys into it. Refuses a Secret that lacks the
101
+ * `credential-kind: claim-guest` label. Without a live claim the Secret is created un-owned (the operator adopts it).
102
+ */
103
+ export async function writeClaimCredentialSecret(input, api = createClaimApi()) {
104
+ assertAllowedKeys(input.data);
105
+ const name = claimCredentialSecretName(input.claimName);
106
+ const keys = Object.keys(input.data);
107
+ const claim = await liveClaim(api, input.claimName);
108
+ const common = { ...input, ...(claim ? { claimUid: claim.uid } : {}) };
109
+ const patch = async (existing) => {
110
+ if (!isGuestSecret(existing))
111
+ throw new Error(`refusing to overwrite Secret ${name}: it lacks label ${CREDENTIAL_KIND_LABEL}=${CREDENTIAL_KIND_VALUE}`);
112
+ const hasOwner = Array.isArray(existing.metadata?.ownerReferences) && existing.metadata.ownerReferences.some((o) => o?.kind === "ApplianceClaim" && o?.uid === claim?.uid);
113
+ await api.request(secretPath(input.claimName), "PATCH", buildClaimCredentialPatch({ ...common, adoptOwner: Boolean(claim) && !hasOwner }));
114
+ return { action: "patched", name, keys, owned: Boolean(claim) };
115
+ };
116
+ const existing = await api.request(secretPath(input.claimName), "GET");
117
+ if (existing)
118
+ return patch(existing);
119
+ try {
120
+ await api.request(CLAIM_SECRETS_COLLECTION, "POST", buildClaimCredentialSecret(common));
121
+ return { action: "created", name, keys, owned: Boolean(claim) };
122
+ }
123
+ catch (error) {
124
+ if (error instanceof ClaimApiError && error.status === 409) {
125
+ const raced = await api.request(secretPath(input.claimName), "GET");
126
+ if (raced)
127
+ return patch(raced);
128
+ }
129
+ throw error;
130
+ }
131
+ }
132
+ /** Decode the allowed keys of a Secret; unknown keys are ignored. */
133
+ function decode(secret) {
134
+ const out = {};
135
+ const data = secret?.data;
136
+ if (!data || typeof data !== "object")
137
+ return out;
138
+ for (const key of CLAIM_SECRET_DATA_KEYS) {
139
+ const raw = data[key];
140
+ if (typeof raw === "string" && raw.length > 0)
141
+ out[key] = Buffer.from(raw, "base64").toString("utf8");
142
+ }
143
+ return out;
144
+ }
145
+ /** Read a claim's credential Secret (null when absent or when it is not a claim-guest Secret). Values are returned to the caller only. */
146
+ export async function readClaimCredentialSecret(claimName, api = createClaimApi()) {
147
+ const secret = await api.request(secretPath(claimName), "GET");
148
+ if (!secret || !isGuestSecret(secret))
149
+ return null;
150
+ const data = decode(secret);
151
+ return { name: claimCredentialSecretName(claimName), data, keys: Object.keys(data) };
152
+ }
153
+ /** Idempotent explicit delete (404 is fine). Never throws: GC by ownerReference is the backstop. */
154
+ export async function deleteClaimCredentialSecret(claimName, api = createClaimApi()) {
155
+ let name;
156
+ try {
157
+ name = claimCredentialSecretName(claimName);
158
+ }
159
+ catch (e) {
160
+ return { name: `claim-${claimName}-credentials`, deleted: false, error: String(e.message) };
161
+ }
162
+ try {
163
+ await api.request(secretPath(claimName), "DELETE", { apiVersion: "v1", kind: "DeleteOptions" });
164
+ return { name, deleted: true };
165
+ }
166
+ catch (error) {
167
+ if (error instanceof ClaimApiError && error.status === 404)
168
+ return { name, deleted: false };
169
+ // Only the status/name; remote text is never echoed.
170
+ return { name, deleted: false, error: error instanceof ClaimApiError ? `delete failed (${error.status ?? "unknown"})` : "delete failed" };
171
+ }
172
+ }
173
+ /** uuid -> claim name via the existing claim list (never a Secret list). Matches claim label/annotation/status vm uuid. */
174
+ export async function findClaimNameByVmUuid(vmUuid, api = createClaimApi()) {
175
+ const id = normalizeUuid(vmUuid);
176
+ let path = `${CLAIMS_PATH}?limit=500`;
177
+ for (let page = 0; page < 20; page++) {
178
+ const list = await api.request(path, "GET");
179
+ for (const item of list?.items ?? []) {
180
+ const candidates = [item?.metadata?.labels?.[VM_UUID_LABEL], item?.metadata?.annotations?.[VM_UUID_LABEL], item?.status?.vmUuid, item?.status?.smbiosUuid, item?.spec?.vmUuid];
181
+ if (candidates.some(c => typeof c === "string" && c.toLowerCase() === id) && typeof item.metadata?.name === "string")
182
+ return item.metadata.name;
183
+ }
184
+ const next = list?.metadata?.continue;
185
+ if (!next || !/^[A-Za-z0-9%._~-]+$/.test(next))
186
+ return null;
187
+ path = `${CLAIMS_PATH}?limit=500&continue=${next}`;
188
+ }
189
+ return null;
190
+ }
191
+ export async function readClaimCredentialByVmUuid(vmUuid, api = createClaimApi()) {
192
+ const claimName = await findClaimNameByVmUuid(vmUuid, api);
193
+ if (!claimName)
194
+ return null;
195
+ const read = await readClaimCredentialSecret(claimName, api);
196
+ return read ? { ...read, claimName } : null;
197
+ }
198
+ //# sourceMappingURL=claim-secret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim-secret.js","sourceRoot":"","sources":["../../../src/credentials/claim-secret.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EACL,wBAAwB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,GACnG,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAIlC,MAAM,CAAC,MAAM,sBAAsB,GAAG,QAAQ,CAAC;AAC/C,MAAM,CAAC,MAAM,qBAAqB,GAAG,mCAAmC,CAAC;AACzE,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AACnD,MAAM,CAAC,MAAM,gBAAgB,GAAG,8BAA8B,CAAC;AAC/D,MAAM,CAAC,MAAM,WAAW,GAAG,yBAAyB,CAAC;AACrD,MAAM,CAAC,MAAM,aAAa,GAAG,2BAA2B,CAAC;AACzD,MAAM,CAAC,MAAM,qBAAqB,GAAG,8BAA8B,CAAC;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,6BAA6B,CAAC;AAClE,MAAM,CAAC,MAAM,yBAAyB,GAAG,kCAAkC,CAAC;AAC5E,MAAM,WAAW,GAAG,oEAAoE,CAAC;AACzF,MAAM,OAAO,GAAG,gEAAgE,CAAC;AACjF,MAAM,cAAc,GAAG,wDAAwD,CAAC;AAKhF,6GAA6G;AAC7G,MAAM,UAAU,yBAAyB,CAAC,SAAiB;IACzD,MAAM,IAAI,GAAG,SAAS,SAAS,cAAc,CAAC;IAC9C,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,oCAAoC,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IACzK,OAAO,IAAI,CAAC;AACd,CAAC;AACD,MAAM,UAAU,GAAG,CAAC,SAAiB,EAAE,EAAE,CAAC,GAAG,wBAAwB,IAAI,yBAAyB,CAAC,SAAS,CAAC,EAAE,CAAC;AAEhH,oFAAoF;AACpF,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,sBAAsB,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;AACrE,CAAC;AACD,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACvC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IACnF,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,iBAAiB,CAAC,IAA6B;IAC7D,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,CAAE,sBAA4C,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,GAAG,EAAE,CAAC,CAAC;QACjI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,GAAG,6BAA6B,CAAC,CAAC;IAC9I,CAAC;AACH,CAAC;AACD,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAGrE,SAAS,cAAc,CAAC,SAAiB,EAAE,KAAoB;IAC7D,OAAO,EAAE,UAAU,EAAE,4BAA4B,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC;AAC7J,CAAC;AACD,SAAS,WAAW,CAAC,SAAiB,EAAE,SAA4B,EAAE,KAA2B,EAAE,MAAe;IAChH,MAAM,MAAM,GAA2B,EAAE,CAAC,qBAAqB,CAAC,EAAE,qBAAqB,EAAE,CAAC,gBAAgB,CAAC,EAAE,SAAS,EAAE,CAAC;IACzH,IAAI,SAAS,CAAC,MAAM,IAAI,EAAE,IAAI,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,MAAM,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC;IAC9F,MAAM,WAAW,GAA2B,EAAE,CAAC,qBAAqB,CAAC,EAAE,SAAS,EAAE,CAAC;IACnF,IAAI,KAAK;QAAE,WAAW,CAAC,oBAAoB,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC;IACzD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC;QAC3B,WAAW,CAAC,yBAAyB,CAAC,GAAG,mBAAmB,CAAC,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AACjC,CAAC;AAGD,sGAAsG;AACtG,MAAM,UAAU,0BAA0B,CAAC,KAAuB;IAChE,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,YAAY,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACnH,OAAO;QACL,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ;QAChD,QAAQ,EAAE;YACR,IAAI,EAAE,yBAAyB,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,EAAE,WAAW;YACxG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChF;QACD,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAW,CAAC,CAAC,CAAC,CAAC;KAC5F,CAAC;AACJ,CAAC;AACD,iHAAiH;AACjH,MAAM,UAAU,yBAAyB,CAAC,KAAkD;IAC1F,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,YAAY,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACnH,OAAO;QACL,QAAQ,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;QACtI,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAW,CAAC,CAAC,CAAC,CAAC;KAC5F,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,MAAW,IAAa,OAAO,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,qBAAqB,CAAC,KAAK,qBAAqB,CAAC,CAAC,CAAC;AAEpI,KAAK,UAAU,SAAS,CAAC,GAAa,EAAE,SAAiB;IACvD,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,WAAW,IAAI,SAAS,EAAE,EAAE,KAAK,CAAC,CAAC;IACtE,OAAO,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7G,CAAC;AAGD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,KAAmG,EACnG,MAAgB,cAAc,EAAE;IAEhC,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,yBAAyB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAqB,CAAC;IACzD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IACvE,MAAM,KAAK,GAAG,KAAK,EAAE,QAAa,EAAwB,EAAE;QAC1D,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,oBAAoB,qBAAqB,IAAI,qBAAqB,EAAE,CAAC,CAAC;QACxJ,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,gBAAgB,IAAI,CAAC,EAAE,GAAG,KAAK,KAAK,EAAE,GAAG,CAAC,CAAC;QAChL,MAAM,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,yBAAyB,CAAC,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC3I,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IAClE,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC;IACvE,IAAI,QAAQ;QAAE,OAAO,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,OAAO,CAAC,wBAAwB,EAAE,MAAM,EAAE,0BAA0B,CAAC,MAAM,CAAC,CAAC,CAAC;QACxF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IAClE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,aAAa,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC3D,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC;YACpE,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,SAAS,MAAM,CAAC,MAAW;IACzB,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,MAAM,EAAE,IAAI,CAAC;IAC1B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IAClD,KAAK,MAAM,GAAG,IAAI,sBAAsB,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;QACtB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AACD,0IAA0I;AAC1I,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,SAAiB,EAAE,MAAgB,cAAc,EAAE;IACjG,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/D,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE,IAAI,EAAE,yBAAyB,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAqB,EAAE,CAAC;AAC3G,CAAC;AAGD,oGAAoG;AACpG,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,SAAiB,EAAE,MAAgB,cAAc,EAAE;IACnG,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QAAC,IAAI,GAAG,yBAAyB,CAAC,SAAS,CAAC,CAAC;IAAC,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAAC,OAAO,EAAE,IAAI,EAAE,SAAS,SAAS,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAAC,CAAC;IACpK,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;QAChG,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACjC,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,IAAI,KAAK,YAAY,aAAa,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC5F,qDAAqD;QACrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,YAAY,aAAa,CAAC,CAAC,CAAC,kBAAkB,KAAK,CAAC,MAAM,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC;IAC5I,CAAC;AACH,CAAC;AAED,2HAA2H;AAC3H,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAAc,EAAE,MAAgB,cAAc,EAAE;IAC1F,MAAM,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,IAAI,GAAG,GAAG,WAAW,YAAY,CAAC;IACtC,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC5C,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC;YACrC,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAC/K,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC,IAAI,OAAO,IAAI,CAAC,QAAQ,EAAE,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAClJ,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC;QACtC,IAAI,CAAC,IAAI,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5D,IAAI,GAAG,GAAG,WAAW,uBAAuB,IAAI,EAAE,CAAC;IACrD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,MAAc,EAAE,MAAgB,cAAc,EAAE;IAChG,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3D,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,yBAAyB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC7D,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Guest credential resolution, Secret-first.
3
+ *
4
+ * 1. The claim's Secret `claim-<name>-credentials` (by claim name, or by SMBIOS uuid via the claim list).
5
+ * 2. LEGACY, read-only: the Doppler config entry LAB_VM_CREDENTIALS_<UUID>, only when no Secret exists
6
+ * (not-yet-migrated entries). The result is explicitly marked `legacy: true`; nothing is ever written back.
7
+ */
8
+ import type { ClaimApi } from "../tools/claim-kubernetes.js";
9
+ import { type ClaimSecretData } from "./claim-secret.js";
10
+ import type { VmCredentialConfig } from "./types.js";
11
+ export interface GuestCredentialQuery {
12
+ claimName?: string;
13
+ vmUuid?: string;
14
+ }
15
+ export interface GuestCredentialResolution {
16
+ source: "claim-secret" | "legacy-doppler";
17
+ /** True for the Doppler fallback: the entry has not been migrated to a claim Secret. */
18
+ legacy: boolean;
19
+ claimName?: string;
20
+ vmCredentials: VmCredentialConfig;
21
+ }
22
+ export interface GuestCredentialDeps {
23
+ api?: ClaimApi;
24
+ /** Reads one legacy Doppler secret by key; undefined when absent or Doppler is not configured. */
25
+ legacyRead?: (key: string) => Promise<string | undefined>;
26
+ }
27
+ /** Map `credential.json` (v1: {ssh:{...}} or flat) and the plain keys onto a VmCredentialConfig. */
28
+ export declare function vmCredentialsFromSecretData(data: ClaimSecretData): VmCredentialConfig | null;
29
+ /** Default legacy reader: the whole Doppler config via the config-scoped token, picking one key. Read-only. */
30
+ export declare function dopplerLegacyReader(env?: Record<string, string | undefined>): (key: string) => Promise<string | undefined>;
31
+ export declare function resolveGuestCredential(query: GuestCredentialQuery, deps?: GuestCredentialDeps): Promise<GuestCredentialResolution | null>;
32
+ //# sourceMappingURL=guest-credential.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guest-credential.d.ts","sourceRoot":"","sources":["../../../src/credentials/guest-credential.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,EACiF,KAAK,eAAe,EAC3G,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,oBAAoB;IAAG,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AAC7E,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,cAAc,GAAG,gBAAgB,CAAC;IAC1C,wFAAwF;IACxF,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,kBAAkB,CAAC;CACnC;AACD,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,kGAAkG;IAClG,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC3D;AAED,oGAAoG;AACpG,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,eAAe,GAAG,kBAAkB,GAAG,IAAI,CAc5F;AAED,+GAA+G;AAC/G,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAOvI;AAED,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,oBAAoB,EAAE,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAyBnJ"}
@@ -0,0 +1,74 @@
1
+ import { DopplerClient } from "./doppler.js";
2
+ import { findClaimNameByVmUuid, legacyCredentialKey, normalizeUuid, readClaimCredentialSecret, } from "./claim-secret.js";
3
+ /** Map `credential.json` (v1: {ssh:{...}} or flat) and the plain keys onto a VmCredentialConfig. */
4
+ export function vmCredentialsFromSecretData(data) {
5
+ let parsed;
6
+ if (data["credential.json"]) {
7
+ try {
8
+ parsed = JSON.parse(data["credential.json"]);
9
+ }
10
+ catch {
11
+ parsed = undefined;
12
+ }
13
+ }
14
+ const ssh = parsed?.ssh ?? parsed;
15
+ const password = ssh?.password ?? ssh?.pass ?? data["ssh-password"] ?? data["admin-password"];
16
+ const privateKey = ssh?.privateKey ?? ssh?.private_key ?? data["ssh-private-key"];
17
+ if (!password && !privateKey)
18
+ return null;
19
+ return {
20
+ username: ssh?.username ?? ssh?.user ?? "calltelemetry",
21
+ password: password || undefined,
22
+ privateKey: privateKey || undefined,
23
+ passphrase: ssh?.passphrase,
24
+ port: ssh?.port ? Number(ssh.port) : 2222,
25
+ };
26
+ }
27
+ /** Default legacy reader: the whole Doppler config via the config-scoped token, picking one key. Read-only. */
28
+ export function dopplerLegacyReader(env = process.env) {
29
+ return async (key) => {
30
+ const token = env.DOPPLER_TOKEN;
31
+ if (!token)
32
+ return undefined;
33
+ const secrets = await new DopplerClient({ token, project: env.DOPPLER_PROJECT, config: env.DOPPLER_CONFIG }).downloadSecrets();
34
+ return secrets[key];
35
+ };
36
+ }
37
+ export async function resolveGuestCredential(query, deps = {}) {
38
+ if (!query.claimName && !query.vmUuid)
39
+ throw new Error("claimName or vmUuid is required");
40
+ let api = deps.api;
41
+ if (!api) {
42
+ const { createClaimApi } = await import("../tools/claim-kubernetes.js");
43
+ api = createClaimApi();
44
+ }
45
+ let claimName = query.claimName;
46
+ let secretError;
47
+ try {
48
+ if (!claimName && query.vmUuid)
49
+ claimName = (await findClaimNameByVmUuid(query.vmUuid, api)) ?? undefined;
50
+ if (claimName) {
51
+ const secret = await readClaimCredentialSecret(claimName, api);
52
+ const vmCredentials = secret ? vmCredentialsFromSecretData(secret.data) : null;
53
+ if (vmCredentials)
54
+ return { source: "claim-secret", legacy: false, claimName, vmCredentials };
55
+ }
56
+ }
57
+ catch (error) {
58
+ secretError = error;
59
+ }
60
+ // LEGACY read-only fallback (static / not-yet-migrated entries). Needs a uuid to know the key.
61
+ if (query.vmUuid) {
62
+ const read = deps.legacyRead ?? dopplerLegacyReader();
63
+ const raw = await read(legacyCredentialKey(normalizeUuid(query.vmUuid)));
64
+ if (raw) {
65
+ const vmCredentials = vmCredentialsFromSecretData({ "credential.json": raw });
66
+ if (vmCredentials)
67
+ return { source: "legacy-doppler", legacy: true, ...(claimName ? { claimName } : {}), vmCredentials };
68
+ }
69
+ }
70
+ if (secretError)
71
+ throw secretError;
72
+ return null;
73
+ }
74
+ //# sourceMappingURL=guest-credential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guest-credential.js","sourceRoot":"","sources":["../../../src/credentials/guest-credential.ts"],"names":[],"mappings":"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"}
@@ -46,7 +46,7 @@ const CANONICAL_ALTERNATIVE = "Reads that do not need this aggregate bundle: lis
46
46
  "canonical lab-inventory CLI), the ApplianceClaim tools, and `lab-inventory` itself. Legacy tools such as " +
47
47
  "list_slots stay blocked with CREDENTIALS_UNAVAILABLE until the bundle resolves; do not inject unrelated hosts' " +
48
48
  "credentials just to unblock one VM. A shared appliance login is not required: guests are dynamic, and their " +
49
- "per-VM `LAB_VM_CREDENTIALS_<UUID>` secrets belong to the canonical CLI.";
49
+ "per-guest credentials live in the claim Kubernetes Secret `claim-<name>-credentials` in ct-lab (legacy `LAB_VM_CREDENTIALS_<UUID>` Doppler entries are read-only fallbacks) and belong to the canonical CLI.";
50
50
  const VERIFY_STEP = "Reload the server (manage_mcp_server reload, or restart the harness) and call credential_status again: the " +
51
51
  "tier should read direct_env or doppler_api and the legacy aggregate source resolved. A host listed here as " +
52
52
  "reachable is the only evidence the credentials work; mock mode (CT_LAB_MCP_ALLOW_MOCK=1) is for offline tests " +
@@ -1 +1 @@
1
- {"version":3,"file":"remediation.js","sourceRoot":"","sources":["../../../src/credentials/remediation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"remediation.js","sourceRoot":"","sources":["../../../src/credentials/remediation.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@
6
6
  * Tier 3: In-Memory Mock Fallback
7
7
  */
8
8
  import { ProxmoxHostConfig, EsxiHostConfig, ResolutionOptions, CredentialResolutionResult, CanonicalCredentialEnv } from "./types.js";
9
+ import { type GuestCredentialDeps, type GuestCredentialQuery, type GuestCredentialResolution } from "./guest-credential.js";
9
10
  export declare const DEFAULT_DOPPLER_TIMEOUT_MS = 8000;
10
11
  export declare const MIN_DOPPLER_TIMEOUT_MS = 250;
11
12
  export declare const MAX_DOPPLER_TIMEOUT_MS = 30000;
@@ -53,6 +54,12 @@ export declare class CredentialResolver {
53
54
  * Extract Appliance / VM credentials (LAB_VM_CREDENTIALS_* or LAB_VM_CREDENTIALS JSON/slug).
54
55
  */
55
56
  private extractVmCredentials;
57
+ /**
58
+ * Per-guest credential, Secret-first: the claim's Secret `claim-<name>-credentials` (by claim name, or by SMBIOS uuid
59
+ * through the claim list); only when none exists, a read-only Doppler LAB_VM_CREDENTIALS_<UUID> entry marked
60
+ * `legacy: true`. Dynamic claim credentials are never written to Doppler.
61
+ */
62
+ resolveGuestCredential(query: GuestCredentialQuery, deps?: GuestCredentialDeps): Promise<GuestCredentialResolution | null>;
56
63
  private createMockResult;
57
64
  private createMockProxmoxHost;
58
65
  private createMockVmCredentials;
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/credentials/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,iBAAiB,EAEjB,cAAc,EAEd,iBAAiB,EACjB,0BAA0B,EAE1B,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAcpB,eAAO,MAAM,0BAA0B,OAAQ,CAAC;AAChD,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAMvF;AAkCD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,iBAAiB,GAAG,cAAc,EACxC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,sBAAsB,CAiCxB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA6B;IACtE,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAwB;IAClE,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAmB;IAE9D;;;;;;OAMG;IACH,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,eAAe;IASvB;;;;;;;;;OASG;IACG,OAAO,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAuMnF;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAgHhC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAM7B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA+F5B,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,qBAAqB;IAuB7B,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,kBAAkB;CAwB3B;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/credentials/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,iBAAiB,EAEjB,cAAc,EAEd,iBAAiB,EACjB,0BAA0B,EAE1B,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAA0B,KAAK,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,KAAK,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAapJ,eAAO,MAAM,0BAA0B,OAAQ,CAAC;AAChD,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAMvF;AAkCD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,iBAAiB,GAAG,cAAc,EACxC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,sBAAsB,CAiCxB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA6B;IACtE,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAwB;IAClE,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAmB;IAE9D;;;;;;OAMG;IACH,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,eAAe;IASvB;;;;;;;;;OASG;IACG,OAAO,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAuMnF;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAgHhC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAM7B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA+F5B;;;;OAIG;IACG,sBAAsB,CAAC,KAAK,EAAE,oBAAoB,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;IAUhI,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,qBAAqB;IAuB7B,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,kBAAkB;CAwB3B;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
@@ -6,6 +6,7 @@
6
6
  * Tier 3: In-Memory Mock Fallback
7
7
  */
8
8
  import { DopplerClient } from "./doppler.js";
9
+ import { resolveGuestCredential } from "./guest-credential.js";
9
10
  import { registerSecret } from "../utils/redactor.js";
10
11
  import { logger } from "../utils/logger.js";
11
12
  import { CredentialResolutionError } from "./errors.js";
@@ -468,6 +469,22 @@ export class CredentialResolver {
468
469
  }
469
470
  return null;
470
471
  }
472
+ /**
473
+ * Per-guest credential, Secret-first: the claim's Secret `claim-<name>-credentials` (by claim name, or by SMBIOS uuid
474
+ * through the claim list); only when none exists, a read-only Doppler LAB_VM_CREDENTIALS_<UUID> entry marked
475
+ * `legacy: true`. Dynamic claim credentials are never written to Doppler.
476
+ */
477
+ async resolveGuestCredential(query, deps) {
478
+ const result = await resolveGuestCredential(query, deps);
479
+ if (result) {
480
+ registerSecret(result.vmCredentials.password ?? "");
481
+ if (result.vmCredentials.privateKey)
482
+ registerSecret(result.vmCredentials.privateKey);
483
+ if (result.legacy)
484
+ logger.warn(`Guest credential for ${result.claimName ?? "uuid lookup"} came from the legacy Doppler entry; it is not migrated to a claim Secret.`);
485
+ }
486
+ return result;
487
+ }
471
488
  createMockResult() {
472
489
  const proxmoxHosts = [
473
490
  this.createMockProxmoxHost("proxmox-lab", this.defaultLabProtectedVmids),