@homeflare/alchemy 0.47.0 → 0.48.0
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/README.md +6 -32
- package/dist/talos/bao-env.d.ts +7 -0
- package/dist/talos/bao-env.d.ts.map +1 -0
- package/dist/talos/cluster-adapter-errors.d.ts +134 -0
- package/dist/talos/cluster-adapter-errors.d.ts.map +1 -0
- package/dist/talos/cluster-adapter.d.ts +115 -0
- package/dist/talos/cluster-adapter.d.ts.map +1 -0
- package/dist/talos/cluster-identity.d.ts +28 -0
- package/dist/talos/cluster-identity.d.ts.map +1 -0
- package/dist/talos/cluster-transport.d.ts +64 -0
- package/dist/talos/cluster-transport.d.ts.map +1 -0
- package/dist/talos/credentials-write.d.ts.map +1 -1
- package/dist/talos/credentials.d.ts +10 -19
- package/dist/talos/credentials.d.ts.map +1 -1
- package/dist/talos/fake-apiserver.d.ts +9 -0
- package/dist/talos/fake-apiserver.d.ts.map +1 -0
- package/dist/talos/index.d.ts +7 -0
- package/dist/talos/index.d.ts.map +1 -1
- package/dist/talos/index.js +669 -167
- package/dist/talos/index.js.map +22 -10
- package/dist/talos/kubeconfig-attrs.d.ts +14 -0
- package/dist/talos/kubeconfig-attrs.d.ts.map +1 -1
- package/dist/talos/kubeconfig-doc.d.ts +23 -0
- package/dist/talos/kubeconfig-doc.d.ts.map +1 -0
- package/dist/talos/kubeconfig.d.ts +0 -12
- package/dist/talos/kubeconfig.d.ts.map +1 -1
- package/dist/talos/literal-connection.d.ts +16 -0
- package/dist/talos/literal-connection.d.ts.map +1 -0
- package/dist/talos/machine-config-read.d.ts +1 -1
- package/dist/talos/machine-config-read.d.ts.map +1 -1
- package/dist/talos/mint-temp-file.d.ts +20 -0
- package/dist/talos/mint-temp-file.d.ts.map +1 -0
- package/dist/talos/node-temp.harness.d.ts +2 -0
- package/dist/talos/node-temp.harness.d.ts.map +1 -0
- package/dist/talos/talos-bootstrap-etcd.d.ts +1 -1
- package/dist/talos/talos-bootstrap-etcd.d.ts.map +1 -1
- package/dist/talos/talos-cluster-health.d.ts +15 -15
- package/dist/talos/talos-cluster-health.d.ts.map +1 -1
- package/dist/talos/talos-cluster-identity.d.ts +65 -0
- package/dist/talos/talos-cluster-identity.d.ts.map +1 -0
- package/dist/talos/talosctl-binary.d.ts +43 -0
- package/dist/talos/talosctl-binary.d.ts.map +1 -0
- package/dist/talos/talosctl.d.ts +12 -15
- package/dist/talos/talosctl.d.ts.map +1 -1
- package/dist/talos/trust-boundary.d.ts +8 -0
- package/dist/talos/trust-boundary.d.ts.map +1 -0
- package/dist/talos/values.d.ts.map +1 -1
- package/dist/talos/yaml-parse.d.ts +3 -0
- package/dist/talos/yaml-parse.d.ts.map +1 -0
- package/dist/verify/fake-engine.d.ts +2 -0
- package/dist/verify/fake-engine.d.ts.map +1 -1
- package/docs/peers.md +32 -5
- package/docs/talos-openbao-adapter.md +44 -0
- package/docs/upstream-conformance-bun.md +61 -24
- package/package.json +116 -2
- package/src/talos/bao-env.ts +16 -0
- package/src/talos/bao-vetting.test.ts +97 -0
- package/src/talos/cluster-adapter-deadline.test.ts +110 -0
- package/src/talos/cluster-adapter-errors.ts +192 -0
- package/src/talos/cluster-adapter-move.test.ts +129 -0
- package/src/talos/cluster-adapter-reconcile.test.ts +165 -0
- package/src/talos/cluster-adapter-refusals.test.ts +102 -0
- package/src/talos/cluster-adapter-rename.test.ts +87 -0
- package/src/talos/cluster-adapter-secrecy.test.ts +243 -0
- package/src/talos/cluster-adapter.fixtures.ts +125 -0
- package/src/talos/cluster-adapter.test.ts +240 -0
- package/src/talos/cluster-adapter.ts +249 -0
- package/src/talos/cluster-identity.ts +63 -0
- package/src/talos/cluster-transport.ts +110 -0
- package/src/talos/credentials-write.ts +27 -5
- package/src/talos/credentials.ts +51 -45
- package/src/talos/fake-apiserver.ts +55 -0
- package/src/talos/fake-process.ts +47 -1
- package/src/talos/index.ts +32 -0
- package/src/talos/kubeconfig-adapter.test.ts +37 -0
- package/src/talos/kubeconfig-attrs.ts +12 -28
- package/src/talos/kubeconfig-doc.ts +74 -0
- package/src/talos/kubeconfig.fixtures.ts +52 -0
- package/src/talos/kubeconfig.test.ts +6 -54
- package/src/talos/kubeconfig.ts +18 -34
- package/src/talos/literal-connection.ts +59 -0
- package/src/talos/mint-temp-file.ts +56 -0
- package/src/talos/node-connect.harness.ts +78 -0
- package/src/talos/node-connect.test.ts +27 -0
- package/src/talos/node-temp.harness.ts +40 -0
- package/src/talos/node-temp.test.ts +26 -0
- package/src/talos/talos-cluster-health-declare.test.ts +54 -0
- package/src/talos/talos-cluster-health-literal.test.ts +69 -0
- package/src/talos/talos-cluster-health.test.ts +104 -18
- package/src/talos/talos-cluster-health.ts +46 -14
- package/src/talos/talos-cluster-identity.test.ts +96 -0
- package/src/talos/talos-cluster-identity.ts +130 -0
- package/src/talos/talosctl-binary.ts +150 -0
- package/src/talos/talosctl-vetting.test.ts +212 -0
- package/src/talos/talosctl.test.ts +110 -0
- package/src/talos/talosctl.ts +101 -15
- package/src/talos/trust-boundary.test.ts +17 -0
- package/src/talos/trust-boundary.ts +45 -0
- package/src/talos/values.ts +5 -12
- package/src/talos/yaml-parse.test.ts +41 -0
- package/src/talos/yaml-parse.ts +28 -0
- package/src/verify/fake-engine.ts +10 -0
- package/dist/talos/fake-process.d.ts +0 -56
- package/dist/talos/fake-process.d.ts.map +0 -1
- package/dist/talos/machine-config-fixtures.d.ts +0 -60
- package/dist/talos/machine-config-fixtures.d.ts.map +0 -1
- package/src/postgres/fixtures/pg-REL_18_6-acl.h.txt +0 -147
- package/src/postgres/fixtures/pg-REL_18_6-aclexplode-func.txt +0 -31
- package/src/postgres/fixtures/pg-REL_18_6-alter-default-privileges-synopsis.txt +0 -82
- package/src/postgres/fixtures/pg-REL_18_6-alter_role.sgml.txt +0 -44
- package/src/postgres/fixtures/pg-REL_18_6-create-database-synopsis.txt +0 -26
- package/src/postgres/fixtures/pg-REL_18_6-create_role.sgml.txt +0 -26
- package/src/postgres/fixtures/pg-REL_18_6-grant-synopsis.txt +0 -95
- package/src/postgres/fixtures/pg-REL_18_6-pg_attribute.h.txt +0 -24
- package/src/postgres/fixtures/pg-REL_18_6-pg_auth_members.h.txt +0 -62
- package/src/postgres/fixtures/pg-REL_18_6-pg_authid.h.txt +0 -70
- package/src/postgres/fixtures/pg-REL_18_6-pg_class.h.txt +0 -33
- package/src/postgres/fixtures/pg-REL_18_6-pg_database.h.txt +0 -67
- package/src/postgres/fixtures/pg-REL_18_6-pg_default_acl.h.txt +0 -81
- package/src/postgres/fixtures/pg-REL_18_6-pg_namespace.h.txt +0 -28
- package/src/postgres/fixtures/pg-REL_18_6-revoke-synopsis.txt +0 -124
- package/src/release/fixtures/openbao/checksums.txt +0 -83
- package/src/release/fixtures/openbao/checksums.txt.gpgsig +0 -0
- package/src/release/fixtures/openbao/darwin-assets.txt +0 -12
- package/src/release/fixtures/openbao/openbao-gpg-pub-20240618.asc +0 -109
- package/src/release/fixtures/victoria/darwin-arm64-assets.txt +0 -22
- package/src/release/fixtures/victoria/victoria-logs-darwin-arm64-v1.52.0_checksums.txt +0 -2
- package/src/release/fixtures/victoria/victoria-metrics-darwin-arm64-v1.151.0_checksums.txt +0 -2
- package/src/release/fixtures/victoria/victoria-traces-darwin-arm64-v0.10.0_checksums.txt +0 -2
- package/src/release/fixtures/victoria/vmutils-darwin-arm64-v1.151.0_checksums.txt +0 -8
package/README.md
CHANGED
|
@@ -2,18 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Custom [Alchemy](https://alchemy.run) providers for gaps the vendor SDK leaves.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@distilled.cloud/cloudflare@1.0.0-rc.13 @distilled.cloud/forgejo@1.0.0-rc.13 \
|
|
9
|
-
@distilled.cloud/discord@1.0.0-rc.13 @distilled.cloud/google-workspace@1.0.0-rc.13 \
|
|
10
|
-
@distilled.cloud/argocd@1.0.0-rc.13 \
|
|
11
|
-
@effect/sql-pg@4.0.1
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
⛔ **Every one of those is required, and you also need an `overrides` block** — see
|
|
15
|
-
[Peers](#peers--and-one-override-you-need) below. The install succeeds without them and
|
|
16
|
-
the import throws.
|
|
5
|
+
⛔ **Every peer on the install line is required, and you also need an `overrides` block**
|
|
6
|
+
— see [docs/peers.md](./docs/peers.md). The install succeeds without them and the import
|
|
7
|
+
throws.
|
|
17
8
|
|
|
18
9
|
★ **Why a custom provider at all.** When Alchemy has no property for something, the
|
|
19
10
|
alternative is a runbook step a human runs once — and a plan can never show a missing
|
|
@@ -390,26 +381,9 @@ subpath reads its credential, and why an empty one fails closed: [docs/credentia
|
|
|
390
381
|
|
|
391
382
|
## Peers — and one override you need
|
|
392
383
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
⛔ **Add this to your `package.json`, or the install works and the import throws:**
|
|
397
|
-
|
|
398
|
-
```json
|
|
399
|
-
{
|
|
400
|
-
"overrides": {
|
|
401
|
-
"effect": "4.0.1",
|
|
402
|
-
"@effect/platform-node": "4.0.1",
|
|
403
|
-
"@effect/platform-node-shared": "4.0.1",
|
|
404
|
-
"@effect/platform-bun": "4.0.1",
|
|
405
|
-
"rolldown": "1.2.8",
|
|
406
|
-
"redis": "6.3.0"
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
Why each peer is required and each pin exists — the measurements, and the error a missing one
|
|
412
|
-
produces: [docs/peers.md](./docs/peers.md).
|
|
384
|
+
The install command, the required `overrides` block, why each pin exists, and the
|
|
385
|
+
`homeflare.consumer` machine-readable source of truth are all in
|
|
386
|
+
[docs/peers.md](./docs/peers.md).
|
|
413
387
|
|
|
414
388
|
## License
|
|
415
389
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⛔ THE CHILD GETS A MINIMAL ENV, NOT `process.env`: `PATH` to find `bao`, the vault address and
|
|
3
|
+
* token, and the optional namespace / CA. `undefined` when BAO_ADDR or BAO_TOKEN is missing —
|
|
4
|
+
* `bao` would then read a cached `~/.vault-token` login and act as whoever last logged in.
|
|
5
|
+
*/
|
|
6
|
+
export declare const baoEnv: () => Record<string, string> | undefined;
|
|
7
|
+
//# sourceMappingURL=bao-env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bao-env.d.ts","sourceRoot":"","sources":["../../src/talos/bao-env.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,MAAM,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAUlD,CAAC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
declare const TalosOpenBaoUnknownCluster_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
2
|
+
readonly _tag: "TalosOpenBaoUnknownCluster";
|
|
3
|
+
} & Readonly<A>;
|
|
4
|
+
/** The connection's uid matches no adapter entry. Uids are public. */
|
|
5
|
+
export declare class TalosOpenBaoUnknownCluster extends TalosOpenBaoUnknownCluster_base<{
|
|
6
|
+
readonly uid: string;
|
|
7
|
+
readonly known: readonly string[];
|
|
8
|
+
}> {
|
|
9
|
+
get message(): string;
|
|
10
|
+
}
|
|
11
|
+
declare const TalosOpenBaoAmbiguousUid_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
12
|
+
readonly _tag: "TalosOpenBaoAmbiguousUid";
|
|
13
|
+
} & Readonly<A>;
|
|
14
|
+
/** Two adapter entries pin the same uid, so which vault key to read is ambiguous. */
|
|
15
|
+
export declare class TalosOpenBaoAmbiguousUid extends TalosOpenBaoAmbiguousUid_base<{
|
|
16
|
+
readonly uid: string;
|
|
17
|
+
readonly aliases: readonly string[];
|
|
18
|
+
}> {
|
|
19
|
+
get message(): string;
|
|
20
|
+
}
|
|
21
|
+
declare const TalosOpenBaoLegacyAuth_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
22
|
+
readonly _tag: "TalosOpenBaoLegacyAuth";
|
|
23
|
+
} & Readonly<A>;
|
|
24
|
+
/** The saved auth block still carries the alias an earlier build of this branch persisted. */
|
|
25
|
+
export declare class TalosOpenBaoLegacyAuth extends TalosOpenBaoLegacyAuth_base<{
|
|
26
|
+
readonly _?: never;
|
|
27
|
+
}> {
|
|
28
|
+
get message(): string;
|
|
29
|
+
}
|
|
30
|
+
declare const TalosClusterMoved_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
31
|
+
readonly _tag: "TalosClusterMoved";
|
|
32
|
+
} & Readonly<A>;
|
|
33
|
+
/** `Talos.ClusterIdentity` read a different uid than the one it published. Never an update. */
|
|
34
|
+
export declare class TalosClusterMoved extends TalosClusterMoved_base<{
|
|
35
|
+
readonly cluster: string;
|
|
36
|
+
readonly saved: string;
|
|
37
|
+
readonly live: string;
|
|
38
|
+
}> {
|
|
39
|
+
get message(): string;
|
|
40
|
+
}
|
|
41
|
+
declare const TalosVaultKeyMissing_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
42
|
+
readonly _tag: "TalosVaultKeyMissing";
|
|
43
|
+
} & Readonly<A>;
|
|
44
|
+
/** `bao kv get` reported the key unwritten. The message names `mount/key` and no document bytes. */
|
|
45
|
+
export declare class TalosVaultKeyMissing extends TalosVaultKeyMissing_base<{
|
|
46
|
+
readonly mount: string;
|
|
47
|
+
readonly key: string;
|
|
48
|
+
}> {
|
|
49
|
+
get message(): string;
|
|
50
|
+
}
|
|
51
|
+
declare const TalosKubeconfigUnreadable_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
52
|
+
readonly _tag: "TalosKubeconfigUnreadable";
|
|
53
|
+
} & Readonly<A>;
|
|
54
|
+
/** Vault bytes exist but are not a kubeconfig for the pinned context. The document is not echoed. */
|
|
55
|
+
export declare class TalosKubeconfigUnreadable extends TalosKubeconfigUnreadable_base<{
|
|
56
|
+
readonly mount: string;
|
|
57
|
+
readonly key: string;
|
|
58
|
+
readonly context: string;
|
|
59
|
+
}> {
|
|
60
|
+
get message(): string;
|
|
61
|
+
}
|
|
62
|
+
declare const TalosOpenBaoAuthKind_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
63
|
+
readonly _tag: "TalosOpenBaoAuthKind";
|
|
64
|
+
} & Readonly<A>;
|
|
65
|
+
/** The layer was asked to connect a different auth kind. */
|
|
66
|
+
export declare class TalosOpenBaoAuthKind extends TalosOpenBaoAuthKind_base<{
|
|
67
|
+
readonly kind: string;
|
|
68
|
+
}> {
|
|
69
|
+
get message(): string;
|
|
70
|
+
}
|
|
71
|
+
declare const TalosClusterIdentityMissing_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
72
|
+
readonly _tag: "TalosClusterIdentityMissing";
|
|
73
|
+
} & Readonly<A>;
|
|
74
|
+
/** The saved auth block carries no `uid`, so nothing says WHICH physical cluster it meant. */
|
|
75
|
+
export declare class TalosClusterIdentityMissing extends TalosClusterIdentityMissing_base<{
|
|
76
|
+
readonly _?: never;
|
|
77
|
+
}> {
|
|
78
|
+
get message(): string;
|
|
79
|
+
}
|
|
80
|
+
declare const TalosClusterIdentityMismatch_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
81
|
+
readonly _tag: "TalosClusterIdentityMismatch";
|
|
82
|
+
} & Readonly<A>;
|
|
83
|
+
/** The cluster that answered is not the one the saved auth block named. Uids are public. */
|
|
84
|
+
export declare class TalosClusterIdentityMismatch extends TalosClusterIdentityMismatch_base<{
|
|
85
|
+
readonly cluster: string;
|
|
86
|
+
readonly expected: string;
|
|
87
|
+
readonly got: string;
|
|
88
|
+
}> {
|
|
89
|
+
get message(): string;
|
|
90
|
+
}
|
|
91
|
+
declare const TalosClusterIdentityUnreadable_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
92
|
+
readonly _tag: "TalosClusterIdentityUnreadable";
|
|
93
|
+
} & Readonly<A>;
|
|
94
|
+
/** `GET /api/v1/namespaces/kube-system` answered without a usable `metadata.uid`. */
|
|
95
|
+
export declare class TalosClusterIdentityUnreadable extends TalosClusterIdentityUnreadable_base<{
|
|
96
|
+
readonly cluster: string;
|
|
97
|
+
}> {
|
|
98
|
+
get message(): string;
|
|
99
|
+
}
|
|
100
|
+
declare const TalosClusterIdentityTimeout_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
101
|
+
readonly _tag: "TalosClusterIdentityTimeout";
|
|
102
|
+
} & Readonly<A>;
|
|
103
|
+
/** The kube-system GET got no answer in time: a silent apiserver must not hang connect or plan. */
|
|
104
|
+
export declare class TalosClusterIdentityTimeout extends TalosClusterIdentityTimeout_base<{
|
|
105
|
+
readonly cluster: string;
|
|
106
|
+
readonly seconds: number;
|
|
107
|
+
}> {
|
|
108
|
+
get message(): string;
|
|
109
|
+
}
|
|
110
|
+
declare const TalosOpenBaoConnectTimeout_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
111
|
+
readonly _tag: "TalosOpenBaoConnectTimeout";
|
|
112
|
+
} & Readonly<A>;
|
|
113
|
+
/**
|
|
114
|
+
* The whole connect path (vault read, parse, kube-system uid read) did not finish in time. One
|
|
115
|
+
* deadline covers `bao` and the apiserver together: a hung `bao` or a silent apiserver must not
|
|
116
|
+
* hang connect or a plan. The `bao` child is killed when the deadline interrupts the read.
|
|
117
|
+
*/
|
|
118
|
+
export declare class TalosOpenBaoConnectTimeout extends TalosOpenBaoConnectTimeout_base<{
|
|
119
|
+
readonly cluster: string;
|
|
120
|
+
readonly seconds: number;
|
|
121
|
+
}> {
|
|
122
|
+
get message(): string;
|
|
123
|
+
}
|
|
124
|
+
declare const TalosUidNotLiteral_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
125
|
+
readonly _tag: "TalosUidNotLiteral";
|
|
126
|
+
} & Readonly<A>;
|
|
127
|
+
/** A connection was built from a uid that is not a plain string known at plan time. */
|
|
128
|
+
export declare class TalosUidNotLiteral extends TalosUidNotLiteral_base<{
|
|
129
|
+
readonly _?: never;
|
|
130
|
+
}> {
|
|
131
|
+
get message(): string;
|
|
132
|
+
}
|
|
133
|
+
export {};
|
|
134
|
+
//# sourceMappingURL=cluster-adapter-errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cluster-adapter-errors.d.ts","sourceRoot":"","sources":["../../src/talos/cluster-adapter-errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Kubernetes.ClusterAdapter` kind `talos-openbao`.
|
|
3
|
+
*
|
|
4
|
+
* Reads `mount/key` from OpenBao at connect time (`credentials.ts`'s `readKvValue`, which
|
|
5
|
+
* shells to `bao` with BAO_ADDR/BAO_TOKEN) and returns a `ClusterTransport`.
|
|
6
|
+
* ⛔ NOTHING IS WRITTEN TO DISK. ⛔ THE PERSISTED CONNECTION IS `{ kind, uid }` ONLY.
|
|
7
|
+
* alchemy `Kubernetes/Connection.ts` (v2.0.0-beta.79, lines 12-14) stores the Connection on
|
|
8
|
+
* every workload's attributes, so the upstream `client-cert` kind would put the admin PEM in
|
|
9
|
+
* the shared state store. The stock `kubeconfig` kind is also refused: an empty path falls
|
|
10
|
+
* back to `$KUBECONFIG`.
|
|
11
|
+
* ⛔ mount/key/context LIVE IN THE ADAPTER'S CONFIGURATION, NOT IN THE AUTH BLOCK. Upstream
|
|
12
|
+
* compares clusters by the auth block (`internal/workload.ts:91-96` `connectionIdentity`) and
|
|
13
|
+
* HelmChart/Manifest answer `replace` when it changes (`HelmChart.ts:248-258`,
|
|
14
|
+
* `Manifest.ts:194-209`); a replace creates first, then cleanup deletes the SAME-named objects.
|
|
15
|
+
* Renaming a vault key would silently delete Cilium, External Secrets and Gatekeeper.
|
|
16
|
+
* ⛔ THE IDENTITY RULE: THE AUTH BLOCK NAMES THE PHYSICAL CLUSTER. `uid` is the kube-system
|
|
17
|
+
* Namespace `metadata.uid` (public, stable across CA rotation). read/update/delete all
|
|
18
|
+
* reconnect from the SAVED auth block (`Manifest.ts:214` and `:267`, `HelmChart.ts:322` and
|
|
19
|
+
* `:335`, via `connectionOfOutput`, `internal/workload.ts:104-108`). A cluster NAME resolved
|
|
20
|
+
* through today's MUTABLE config would let a repointed `c1` PATCH one cluster and DELETE on
|
|
21
|
+
* another (reproduced through ManifestProvider). So the auth block carries the uid and NOTHING
|
|
22
|
+
* else: the config entry is looked up BY uid (no probing walk), `connect` fails closed BEFORE
|
|
23
|
+
* any vault read when the uid is absent or the row is legacy (`auth.cluster`), then GETs
|
|
24
|
+
* kube-system and compares before returning a transport. Missing, unreadable, unknown or
|
|
25
|
+
* different uid is a typed error — never `ClusterNotFoundError` (except a `retired` entry's
|
|
26
|
+
* absent key), which upstream turns into a silent no-op. Cost: one extra GET per connect.
|
|
27
|
+
* ⛔ BOOTSTRAP-THEN-PIN. A brand-new cluster has no uid until it exists, and an Output uid is
|
|
28
|
+
* forbidden (`talosOpenBaoConnection`). So: (1) the first deploy creates the cluster and
|
|
29
|
+
* `Talos.ClusterIdentity` (which reads and outputs the uid) with NO workloads; (2) the operator
|
|
30
|
+
* pins that printed uid literal in this adapter's config; (3) workloads deploy afterwards with
|
|
31
|
+
* `talosOpenBaoConnection(<that literal>)`. A uid that is not pinned is refused at connect.
|
|
32
|
+
* ⚠️ A REAL MOVE IS NOT AN AUTOMATIC REPLACE. `Talos.ClusterIdentity` refuses a changed uid
|
|
33
|
+
* (`TalosClusterMoved`); a move is a NEW identity resource per physical cluster, so the old
|
|
34
|
+
* rows keep their old uid and their cleanup reaches only the old cluster.
|
|
35
|
+
* ⛔ ONE 10 s DEADLINE covers the vault read AND the uid GET (`withConnectDeadline`): a hung `bao`
|
|
36
|
+
* or silent apiserver fails with `TalosOpenBaoConnectTimeout` and the `bao` child is killed.
|
|
37
|
+
* ⚠️ The in-flight HTTPS GET is NOT aborted: upstream `readObject` takes no signal
|
|
38
|
+
* (`internal/client.ts:75`), so the socket lives until the OS gives up. Left for the upstream ask.
|
|
39
|
+
*
|
|
40
|
+
* USAGE RULES (upstream behaviour this adapter cannot fix; full text in
|
|
41
|
+
* `docs/talos-openbao-adapter.md`):
|
|
42
|
+
* 1. Move a workload to another cluster ONLY by new logical IDs (destroy + create), never by
|
|
43
|
+
* changing its connection in place.
|
|
44
|
+
* 2. Manage namespaces as separate `Manifest`s, never HelmChart `createNamespace`.
|
|
45
|
+
* 3. Never rename a HelmChart `releaseName` in place.
|
|
46
|
+
*
|
|
47
|
+
* Walked against alchemy 2.0.0-beta.79 `ClusterAdapter.ts` / `BuiltinAdapters.ts` and the OpenBao
|
|
48
|
+
* v2.6.2 absence string in `isVaultKeyAbsent`. Fake `bao` and fake apiserver only.
|
|
49
|
+
*/
|
|
50
|
+
import { type ClusterAdapterService, ClusterNotFoundError, type ClusterTransport } from 'alchemy/Kubernetes/ClusterAdapter';
|
|
51
|
+
import type { Connection } from 'alchemy/Kubernetes/Connection';
|
|
52
|
+
import type * as Duration from 'effect/Duration';
|
|
53
|
+
import * as Effect from 'effect/Effect';
|
|
54
|
+
import * as Layer from 'effect/Layer';
|
|
55
|
+
import type * as ChildProcessSpawner from 'effect/process/ChildProcessSpawner';
|
|
56
|
+
import { type TalosClusterIdentityMismatch, TalosClusterIdentityMissing, type TalosClusterIdentityTimeout, type TalosClusterIdentityUnreadable, type TalosKubeconfigUnreadable, TalosOpenBaoAmbiguousUid, TalosOpenBaoAuthKind, type TalosOpenBaoConnectTimeout, TalosOpenBaoLegacyAuth, TalosOpenBaoUnknownCluster, type TalosVaultKeyMissing } from './cluster-adapter-errors.ts';
|
|
57
|
+
import { type TalosOpenBaoCluster } from './cluster-transport.ts';
|
|
58
|
+
export { TalosClusterIdentityMismatch, TalosClusterIdentityMissing, TalosClusterIdentityTimeout, TalosClusterIdentityUnreadable, TalosClusterMoved, TalosKubeconfigUnreadable, TalosOpenBaoAmbiguousUid, TalosOpenBaoAuthKind, TalosOpenBaoConnectTimeout, TalosOpenBaoLegacyAuth, TalosOpenBaoUnknownCluster, TalosUidNotLiteral, TalosVaultKeyMissing, } from './cluster-adapter-errors.ts';
|
|
59
|
+
export { type TalosOpenBaoCluster, talosOpenBaoCluster } from './cluster-transport.ts';
|
|
60
|
+
declare module 'alchemy/Kubernetes/Connection' {
|
|
61
|
+
interface AuthRegistry {
|
|
62
|
+
/**
|
|
63
|
+
* Admin kubeconfig in OpenBao KV, read when a workload connects. Contributed by
|
|
64
|
+
* {@link TalosOpenBaoAdapter}, which holds the mount, key and context.
|
|
65
|
+
*/
|
|
66
|
+
'talos-openbao': {
|
|
67
|
+
/**
|
|
68
|
+
* The cluster's kube-system `metadata.uid`: the ONLY identity, a LITERAL string known at
|
|
69
|
+
* plan time (never an Output, see {@link talosOpenBaoConnection}). A row saved before it
|
|
70
|
+
* existed has none at runtime and is refused at connect.
|
|
71
|
+
*/
|
|
72
|
+
uid: string;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* ⛔ KEYED BY ALIAS, BUT THE ALIAS IS NEVER PERSISTED OR LOOKED UP BY. `connect` finds the entry
|
|
78
|
+
* whose pinned `uid` equals the saved auth block's, so renaming an alias changes nothing upstream
|
|
79
|
+
* hashes. Unknown uid is {@link TalosOpenBaoUnknownCluster}: refused, not another cluster's key.
|
|
80
|
+
*/
|
|
81
|
+
export type TalosOpenBaoConfig = Readonly<Record<string, TalosOpenBaoCluster>>;
|
|
82
|
+
/** A `Connection` narrowed to this kind, so a PEM-bearing connection cannot be stored by type. */
|
|
83
|
+
export type TalosOpenBaoConnection = Connection & {
|
|
84
|
+
readonly auth: {
|
|
85
|
+
readonly kind: 'talos-openbao';
|
|
86
|
+
readonly uid: string;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Serializable connection: kind and uid. No alias, vault path, endpoint, CA or cert.
|
|
91
|
+
* ⛔ THE UID MUST BE A LITERAL. An Output (a resource attribute such as `Talos.ClusterIdentity`'s)
|
|
92
|
+
* is unresolved at plan time, so `isResolved(news)` is false, upstream plans an UPDATE (Plan.ts)
|
|
93
|
+
* and reconcile replays the OLD cluster's `previousObjects` as deletes on the NEW cluster
|
|
94
|
+
* (reproduced: DELETE on B). Take the uid from the pinned adapter entry: a changed literal is a
|
|
95
|
+
* changed auth block, which upstream answers with `replace`. Anything else throws
|
|
96
|
+
* {@link TalosUidNotLiteral} while the stack is being declared.
|
|
97
|
+
*/
|
|
98
|
+
export declare const talosOpenBaoConnection: (uid: string) => TalosOpenBaoConnection;
|
|
99
|
+
/**
|
|
100
|
+
* Resolve one `talos-openbao` connection to a transport. Requires `ChildProcessSpawner`
|
|
101
|
+
* because the vault read shells to `bao`, and reaches the apiserver once for the identity check.
|
|
102
|
+
*
|
|
103
|
+
* ⚠️ A key the vault reports absent is `ClusterNotFoundError` ONLY for a cluster configured
|
|
104
|
+
* `retired: true` (upstream `read`/`delete` treat that as "everything in-cluster is already
|
|
105
|
+
* gone", so tearing down a destroyed cluster is not stuck). Otherwise it is the typed
|
|
106
|
+
* `TalosVaultKeyMissing`. Neither message names document bytes.
|
|
107
|
+
*/
|
|
108
|
+
export declare const connectTalosOpenBao: (configs: TalosOpenBaoConfig, connection: Connection, timeout?: Duration.Duration) => Effect.Effect<ClusterTransport, TalosOpenBaoAuthKind | TalosOpenBaoUnknownCluster | TalosOpenBaoAmbiguousUid | TalosOpenBaoLegacyAuth | TalosClusterIdentityMissing | TalosClusterIdentityMismatch | TalosClusterIdentityUnreadable | TalosClusterIdentityTimeout | TalosOpenBaoConnectTimeout | TalosVaultKeyMissing | ClusterNotFoundError | TalosKubeconfigUnreadable | Error, ChildProcessSpawner.ChildProcessSpawner>;
|
|
109
|
+
/**
|
|
110
|
+
* Register kind `talos-openbao`. Merge with `Kubernetes.providers()`:
|
|
111
|
+
* `Layer.mergeAll(Kubernetes.providers(), TalosOpenBaoAdapter({ c1: talosOpenBaoCluster(kc1) }))`
|
|
112
|
+
* where `kc1` is the props object handed to `Talos.Kubeconfig`.
|
|
113
|
+
*/
|
|
114
|
+
export declare const TalosOpenBaoAdapter: (config: TalosOpenBaoConfig) => Layer.Layer<ClusterAdapterService, never, ChildProcessSpawner.ChildProcessSpawner>;
|
|
115
|
+
//# sourceMappingURL=cluster-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cluster-adapter.d.ts","sourceRoot":"","sources":["../../src/talos/cluster-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,EAEL,KAAK,qBAAqB,EAC1B,oBAAoB,EACpB,KAAK,gBAAgB,EACtB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,KAAK,mBAAmB,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EACL,KAAK,4BAA4B,EACjC,2BAA2B,EAC3B,KAAK,2BAA2B,EAChC,KAAK,8BAA8B,EACnC,KAAK,yBAAyB,EAC9B,wBAAwB,EACxB,oBAAoB,EACpB,KAAK,0BAA0B,EAC/B,sBAAsB,EACtB,0BAA0B,EAE1B,KAAK,oBAAoB,EAC1B,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAEL,KAAK,mBAAmB,EAGzB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,4BAA4B,EAC5B,2BAA2B,EAC3B,2BAA2B,EAC3B,8BAA8B,EAC9B,iBAAiB,EACjB,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,0BAA0B,EAC1B,sBAAsB,EACtB,0BAA0B,EAC1B,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAEvF,OAAO,QAAQ,+BAA+B,CAAC;IAC7C,UAAU,YAAY;QACpB;;;WAGG;QACH,eAAe,EAAE;YACf;;;;eAIG;YACH,GAAG,EAAE,MAAM,CAAC;SACb,CAAC;KACH;CACF;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC;AAE/E,kGAAkG;AAClG,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG;IAChD,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CACzE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,QAAS,MAAM,KAAG,sBAGpD,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,YACrB,kBAAkB,cACf,UAAU,YACb,QAAQ,CAAC,QAAQ,KACzB,MAAM,CAAC,MAAM,CACd,gBAAgB,EACd,oBAAoB,GACpB,0BAA0B,GAC1B,wBAAwB,GACxB,sBAAsB,GACtB,2BAA2B,GAC3B,4BAA4B,GAC5B,8BAA8B,GAC9B,2BAA2B,GAC3B,0BAA0B,GAC1B,oBAAoB,GACpB,oBAAoB,GACpB,yBAAyB,GACzB,KAAK,EACP,mBAAmB,CAAC,mBAAmB,CAyDrC,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,WACtB,kBAAkB,KACzB,KAAK,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,EAAE,mBAAmB,CAAC,mBAAmB,CAYjF,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which PHYSICAL cluster answers — the kube-system Namespace's `metadata.uid`.
|
|
3
|
+
*
|
|
4
|
+
* ★ WHY THE UID. It is public, assigned once at cluster creation and stable across CA and
|
|
5
|
+
* admin-cert rotation (a CA fingerprint is not: rotation would read as "a different cluster").
|
|
6
|
+
* Every cluster has the namespace, and the admin kubeconfig can read it.
|
|
7
|
+
* ★ The GET goes through alchemy's own `readObject` (`Kubernetes/internal/client.ts`, reached
|
|
8
|
+
* through the package's `./*` export) so TLS, CA and client-cert handling stay upstream's.
|
|
9
|
+
*/
|
|
10
|
+
import type { ClusterTransport } from 'alchemy/Kubernetes/ClusterAdapter';
|
|
11
|
+
import * as Duration from 'effect/Duration';
|
|
12
|
+
import * as Effect from 'effect/Effect';
|
|
13
|
+
import { TalosClusterIdentityMismatch, TalosClusterIdentityTimeout, TalosClusterIdentityUnreadable } from './cluster-adapter-errors.ts';
|
|
14
|
+
/**
|
|
15
|
+
* ⛔ upstream `readObject` (`internal/client.ts`) sets no request timeout, so a silent apiserver
|
|
16
|
+
* would hang connect and every plan. Bounded here; the failure is typed and closed.
|
|
17
|
+
* ⛔ SHORTER THAN `CONNECT_TIMEOUT` (10 s, cluster-transport.ts): the outer deadline wraps the vault
|
|
18
|
+
* read AND this uid GET together, so an equal inner deadline could never fire first and
|
|
19
|
+
* `TalosClusterIdentityTimeout` was unreachable through `connectTalosOpenBao` (red team, PR 355).
|
|
20
|
+
* A silent apiserver now fails with the identity timeout while a slow `bao` still gets the full
|
|
21
|
+
* outer budget.
|
|
22
|
+
*/
|
|
23
|
+
export declare const UID_READ_TIMEOUT: Duration.Duration;
|
|
24
|
+
/** The cluster's kube-system uid. Request failures propagate: nothing here fails open. */
|
|
25
|
+
export declare const readClusterUid: (cluster: string, transport: ClusterTransport, timeout?: Duration.Duration) => Effect.Effect<string, Error | import("alchemy/Kubernetes/internal/client").KubernetesApiError | TalosClusterIdentityTimeout | TalosClusterIdentityUnreadable, never>;
|
|
26
|
+
/** Succeeds only when the answering cluster is the one `expected` names. */
|
|
27
|
+
export declare const assertClusterUid: (cluster: string, expected: string, transport: ClusterTransport) => Effect.Effect<void, Error | import("alchemy/Kubernetes/internal/client").KubernetesApiError | TalosClusterIdentityMismatch | TalosClusterIdentityTimeout | TalosClusterIdentityUnreadable, never>;
|
|
28
|
+
//# sourceMappingURL=cluster-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cluster-identity.d.ts","sourceRoot":"","sources":["../../src/talos/cluster-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,4BAA4B,EAC5B,2BAA2B,EAC3B,8BAA8B,EAC/B,MAAM,6BAA6B,CAAC;AAIrC;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,mBAAsB,CAAC;AAEpD,0FAA0F;AAC1F,eAAO,MAAM,cAAc,YAChB,MAAM,aACJ,gBAAgB,YAClB,QAAQ,CAAC,QAAQ,yKAgBzB,CAAC;AAEJ,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,YAAa,MAAM,YAAY,MAAM,aAAa,gBAAgB,sMAO5F,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as Duration from 'effect/Duration';
|
|
2
|
+
import * as Effect from 'effect/Effect';
|
|
3
|
+
import { TalosKubeconfigUnreadable, TalosOpenBaoConnectTimeout, TalosVaultKeyMissing } from './cluster-adapter-errors.ts';
|
|
4
|
+
import type { KubeconfigProps } from './kubeconfig.ts';
|
|
5
|
+
/** Where one cluster's admin kubeconfig lives in the vault. Configuration; never persisted. */
|
|
6
|
+
export type TalosVaultLocation = {
|
|
7
|
+
/** OpenBao KV mount, e.g. `talos-c1`. */
|
|
8
|
+
readonly mount: string;
|
|
9
|
+
/** Path under the mount. `Talos.Kubeconfig` writes `kubeconfig` unless `kubeconfigKey` is set. */
|
|
10
|
+
readonly key: string;
|
|
11
|
+
/** Context name inside the kubeconfig. Not secret. */
|
|
12
|
+
readonly context: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* One adapter entry: a vault location PINNED to the physical cluster it must answer as.
|
|
16
|
+
* ⛔ `uid` is the kube-system `metadata.uid`. The persisted auth block carries only this, and
|
|
17
|
+
* connect looks the entry up BY it, so renaming the alias, vault key, mount or context changes
|
|
18
|
+
* nothing upstream hashes (`internal/workload.ts:91-96` `connectionIdentity`). Connect still
|
|
19
|
+
* proves the answering cluster has this uid before it returns a transport.
|
|
20
|
+
*/
|
|
21
|
+
export type TalosOpenBaoCluster = TalosVaultLocation & {
|
|
22
|
+
readonly uid: string;
|
|
23
|
+
/**
|
|
24
|
+
* ⛔ ONLY `true` MAKES THIS UID MEAN "THE CLUSTER IS GONE": connect answers `ClusterNotFoundError`
|
|
25
|
+
* (upstream read/delete treat it as "everything in-cluster is already gone") WITHOUT a vault
|
|
26
|
+
* read, so destroying a dead cluster's rows completes. Otherwise an absent key is the loud
|
|
27
|
+
* `TalosVaultKeyMissing`: a typo in mount/key must not make `destroy` a silent no-op.
|
|
28
|
+
*/
|
|
29
|
+
readonly retired?: boolean;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* ★ ONE SOURCE OF TRUTH FOR mount/key/context: build the location from the SAME props the
|
|
33
|
+
* `Talos.Kubeconfig` resource writes with (`target.mount`, `kubeconfigKey`, `context`), so the
|
|
34
|
+
* writer and the reader cannot disagree.
|
|
35
|
+
*/
|
|
36
|
+
export declare const talosVaultLocation: (props: Pick<KubeconfigProps, 'context' | 'kubeconfigKey' | 'target'>) => TalosVaultLocation;
|
|
37
|
+
/** An adapter entry: {@link talosVaultLocation} plus the uid it is pinned to. */
|
|
38
|
+
export declare const talosOpenBaoCluster: (props: Pick<KubeconfigProps, 'context' | 'kubeconfigKey' | 'target'>, pin: {
|
|
39
|
+
readonly uid: string;
|
|
40
|
+
readonly retired?: boolean;
|
|
41
|
+
}) => TalosOpenBaoCluster;
|
|
42
|
+
/**
|
|
43
|
+
* ⛔ ONE DEADLINE FOR THE WHOLE CONNECT PATH. `bao kv get` has no timeout of its own, and the
|
|
44
|
+
* uid GET's bound (`cluster-identity.ts`) only starts after the vault read returns, so a hung
|
|
45
|
+
* `bao` hung connect and every plan. Interrupting the effect closes the spawn scope, which
|
|
46
|
+
* signals the child's process group (`NodeChildProcessSpawner` `terminateProcessGroup`).
|
|
47
|
+
*/
|
|
48
|
+
export declare const CONNECT_TIMEOUT: Duration.Duration;
|
|
49
|
+
export declare const withConnectDeadline: <A, E, R>(cluster: string, effect: Effect.Effect<A, E, R>, timeout?: Duration.Duration) => Effect.Effect<A, E | TalosOpenBaoConnectTimeout, R>;
|
|
50
|
+
/**
|
|
51
|
+
* Vault read and parse, in memory. ⛔ No identity check here: callers decide what to trust.
|
|
52
|
+
* ⚠️ Only a document that does not parse is `TalosKubeconfigUnreadable`; a parser or runtime
|
|
53
|
+
* failure (a missing global, a broken import) is a defect and propagates as one.
|
|
54
|
+
*/
|
|
55
|
+
export declare const openTransport: (config: TalosVaultLocation) => Effect.Effect<{
|
|
56
|
+
endpoint: string;
|
|
57
|
+
certificateAuthorityData?: string;
|
|
58
|
+
clientCert?: {
|
|
59
|
+
certificate: string;
|
|
60
|
+
key: string;
|
|
61
|
+
};
|
|
62
|
+
headers: Effect.Effect<{}, never, never>;
|
|
63
|
+
}, Error | TalosKubeconfigUnreadable | TalosVaultKeyMissing, import("effect/process/ChildProcessSpawner").ChildProcessSpawner>;
|
|
64
|
+
//# sourceMappingURL=cluster-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cluster-transport.d.ts","sourceRoot":"","sources":["../../src/talos/cluster-transport.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,yBAAyB,EACzB,0BAA0B,EAC1B,oBAAoB,EACrB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,+FAA+F;AAC/F,MAAM,MAAM,kBAAkB,GAAG;IAC/B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kGAAkG;IAClG,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,GAAG;IACrD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,UACtB,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,eAAe,GAAG,QAAQ,CAAC,KACnE,kBAID,CAAC;AAEH,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,UACvB,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,eAAe,GAAG,QAAQ,CAAC,OAC/D;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,KACxD,mBAID,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,mBAAuB,CAAC;AAEpD,eAAO,MAAM,mBAAmB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,WAChC,MAAM,UACP,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,YACrB,QAAQ,CAAC,QAAQ,wDAUzB,CAAC;AAEJ;;;;GAIG;AACH,eAAO,MAAM,aAAa,WAAY,kBAAkB;;;;;;;;8HAapD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials-write.d.ts","sourceRoot":"","sources":["../../src/talos/credentials-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"credentials-write.d.ts","sourceRoot":"","sources":["../../src/talos/credentials-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAQ3C,OAAO,KAAK,mBAAmB,MAAM,oCAAoC,CAAC;AAI1E;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,UACpB,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAS1D,CAAC;AAEL;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,UAChB,MAAM,OACR,MAAM,SACJ,MAAM,SACN,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,mBAAmB,CAAC,mBAAmB,CA+CjE,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as Effect from 'effect/Effect';
|
|
2
2
|
import type * as Scope from 'effect/Scope';
|
|
3
3
|
import * as ChildProcessSpawner from 'effect/process/ChildProcessSpawner';
|
|
4
|
+
export { mintKvTempFile } from './mint-temp-file.ts';
|
|
4
5
|
/** Where credentials come from. HomeFlare-specific mount names live in the stack, not here. */
|
|
5
6
|
export type TalosTarget = {
|
|
6
7
|
/** OpenBao KV mount holding Talos material, e.g. `talos-c1`. */
|
|
@@ -35,22 +36,12 @@ export declare const DEFAULT_KUBECONFIG_KEY = "kubeconfig";
|
|
|
35
36
|
*/
|
|
36
37
|
export declare const readKvValue: (mount: string, key: string, fields: readonly string[]) => Effect.Effect<string, Error, ChildProcessSpawner.ChildProcessSpawner>;
|
|
37
38
|
/**
|
|
38
|
-
*
|
|
39
|
-
* `
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* admin client certificate or a machine config's bootstrap token. `Bun.write` takes no mode, so
|
|
43
|
-
* it lands at the process umask — world-readable on this estate — for as long as the resource
|
|
44
|
-
* runs. The name is unguessable, and "unguessable" is not a permission.
|
|
45
|
-
* ⚠️ `wx` IS THE OTHER HALF. `O_EXCL` means this cannot be made to write through a path an
|
|
46
|
-
* attacker pre-created (the classic /tmp symlink race), and with a UUIDv7 name a collision is a
|
|
47
|
-
* genuine error rather than something to paper over.
|
|
48
|
-
* ⚠️ A HARD KILL (SIGKILL, power loss) skips the finalizer and leaves the file behind. 0600 is
|
|
49
|
-
* what makes that survivable rather than a disclosure.
|
|
39
|
+
* ★ MEASURED AGAINST OPENBAO v2.6.2 (C1 fix, LAND red team, 2026-09-26 — isolated in-memory
|
|
40
|
+
* dev server): `bao kv get` on an unwritten KV-v2 key exits 2 with stderr exactly
|
|
41
|
+
* `No value found at <mount>/data/<key>`. `readKvValue`'s Error embeds that text. It is the
|
|
42
|
+
* only signal treated as "not written yet"; a permission denial or a transport failure is not.
|
|
50
43
|
*/
|
|
51
|
-
export declare const
|
|
52
|
-
readonly path: string;
|
|
53
|
-
}, Error, Scope.Scope>;
|
|
44
|
+
export declare const isVaultKeyAbsent: (error: unknown) => boolean;
|
|
54
45
|
/**
|
|
55
46
|
* Mint one talosconfig file for `target`.
|
|
56
47
|
*
|
|
@@ -61,10 +52,10 @@ export declare const mintKvTempFile: (raw: string, label: string) => Effect.Effe
|
|
|
61
52
|
export declare const mintTalosconfig: (target: TalosTarget) => Effect.Effect<TalosCredential, Error, ChildProcessSpawner.ChildProcessSpawner | Scope.Scope>;
|
|
62
53
|
/**
|
|
63
54
|
* Mint one temp kubeconfig file from the vault copy `Talos.Kubeconfig` writes at bring-up
|
|
64
|
-
* (K-talos-first-boot, 2026-09-26) — the
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
55
|
+
* (K-talos-first-boot, 2026-09-26) — the file-shaped counterpart to {@link mintTalosconfig}.
|
|
56
|
+
* `Kubernetes.ClusterAdapter` `talos-openbao` does not use it: connect reads the bytes with
|
|
57
|
+
* {@link readKvValue} and keeps them in memory. This remains for a caller that must hand a
|
|
58
|
+
* path to a process.
|
|
68
59
|
*/
|
|
69
60
|
export declare const mintKubeconfig: (target: TalosTarget, key?: string) => Effect.Effect<{
|
|
70
61
|
readonly path: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/talos/credentials.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAG3C,OAAO,KAAK,mBAAmB,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/talos/credentials.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAG3C,OAAO,KAAK,mBAAmB,MAAM,oCAAoC,CAAC;AAK1E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,+FAA+F;AAC/F,MAAM,MAAM,WAAW,GAAG;IACxB,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,gFAAgF;IAChF,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC,CAAC;AAIF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAEnD;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,UACf,MAAM,OACR,MAAM,UACH,SAAS,MAAM,EAAE,KACxB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,mBAAmB,CAAC,mBAAmB,CAgFnE,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,UAAW,OAAO,KAAG,OACkB,CAAC;AAErE;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,WAClB,WAAW,KAClB,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,EAAE,mBAAmB,CAAC,mBAAmB,GAAG,KAAK,CAAC,KAAK,CAO3F,CAAC;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,WACjB,WAAW,mBAElB,MAAM,CAAC,MAAM,CACd;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACzB,KAAK,EACL,mBAAmB,CAAC,mBAAmB,GAAG,KAAK,CAAC,KAAK,CAIpD,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `uids` maps an apiserver hostname to the kube-system uid it reports; `hang` lists hostnames that
|
|
3
|
+
* accept the request and never answer.
|
|
4
|
+
*/
|
|
5
|
+
export declare const fakeApiServer: (uids: Readonly<Record<string, string>>, hang?: readonly string[]) => {
|
|
6
|
+
restore: () => void | Promise<void>;
|
|
7
|
+
seen: string[];
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=fake-apiserver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fake-apiserver.d.ts","sourceRoot":"","sources":["../../src/talos/fake-apiserver.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,eAAO,MAAM,aAAa,SAClB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,SAChC,SAAS,MAAM,EAAE;;;CA8BxB,CAAC"}
|
package/dist/talos/index.d.ts
CHANGED
|
@@ -18,13 +18,20 @@
|
|
|
18
18
|
* (talos-bootstrap.ts's own header) and kubeconfig landing in OpenBao, written once at bring-up
|
|
19
19
|
* (kubeconfig.ts's own header) — plus `Talos.ClusterHealth`'s error handling no longer conflates a
|
|
20
20
|
* vault/transport failure with "cluster not healthy yet" (talos-cluster-health.ts's own header).
|
|
21
|
+
* ★ `talos-openbao` (K-A5) is how `Kubernetes.*` reaches the cluster: `TalosOpenBaoAdapter`,
|
|
22
|
+
* `talosOpenBaoConnection(<pinned uid literal>)` (or `cluster: health`) as the persisted
|
|
23
|
+
* `{ kind, uid }` connection — `Talos.Kubeconfig` and `Talos.ClusterIdentity` persist none — and
|
|
24
|
+
* `HF_TALOSCTL` to pin the talosctl binary.
|
|
21
25
|
* ★ Anything unlisted is still reachable by path if you genuinely need it — that is a
|
|
22
26
|
* deliberate, visible act rather than an accident of barrelling.
|
|
23
27
|
*/
|
|
28
|
+
export { TalosClusterIdentityMismatch, TalosClusterIdentityMissing, TalosClusterIdentityTimeout, TalosClusterIdentityUnreadable, TalosClusterMoved, TalosKubeconfigUnreadable, TalosOpenBaoAdapter, TalosOpenBaoAmbiguousUid, TalosOpenBaoLegacyAuth, TalosOpenBaoAuthKind, TalosOpenBaoConnectTimeout, type TalosOpenBaoCluster, type TalosOpenBaoConfig, TalosOpenBaoUnknownCluster, TalosUidNotLiteral, TalosVaultKeyMissing, connectTalosOpenBao, talosOpenBaoCluster, talosOpenBaoConnection, } from './cluster-adapter.ts';
|
|
24
29
|
export type { TalosCredential, TalosTarget } from './credentials.ts';
|
|
25
30
|
export { type KubeconfigAttributes, type KubeconfigProps, TalosKubeconfig, TalosKubeconfigProvider, } from './kubeconfig.ts';
|
|
26
31
|
export type { ApplyMode, WithTarget } from './resource.ts';
|
|
27
32
|
export { type BootstrapAttributes, type BootstrapProps, TalosBootstrap, TalosBootstrapProvider, } from './talos-bootstrap.ts';
|
|
33
|
+
export { type ClusterIdentityAttributes, type ClusterIdentityProps, TalosClusterIdentity, TalosClusterIdentityProvider, } from './talos-cluster-identity.ts';
|
|
28
34
|
export { type ClusterHealthAttributes, type ClusterHealthProps, TalosClusterHealth, TalosClusterHealthProvider, } from './talos-cluster-health.ts';
|
|
29
35
|
export { type MachineConfigAttributes, type MachineConfigProps, TalosMachineConfig, TalosMachineConfigProvider, } from './talos-machine-config.ts';
|
|
36
|
+
export { DEFAULT_TALOSCTL_BINARY, TALOSCTL_BINARY_ENV } from './talosctl.ts';
|
|
30
37
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/talos/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/talos/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EACL,4BAA4B,EAC5B,2BAA2B,EAC3B,2BAA2B,EAC3B,8BAA8B,EAC9B,iBAAiB,EACjB,yBAAyB,EACzB,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,oBAAoB,EACpB,0BAA0B,EAC1B,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,eAAe,EACf,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,cAAc,EACd,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
|