@homeflare/alchemy 0.4.0 → 0.5.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.
Files changed (53) hide show
  1. package/README.md +57 -0
  2. package/dist/openbao/approle-login-form.d.ts +72 -0
  3. package/dist/openbao/approle-login-form.d.ts.map +1 -0
  4. package/dist/openbao/approle-login-result.d.ts +39 -0
  5. package/dist/openbao/approle-login-result.d.ts.map +1 -0
  6. package/dist/openbao/approle-login.d.ts +77 -0
  7. package/dist/openbao/approle-login.d.ts.map +1 -0
  8. package/dist/openbao/bao-http.d.ts +2 -0
  9. package/dist/openbao/bao-http.d.ts.map +1 -1
  10. package/dist/openbao/index.d.ts +7 -0
  11. package/dist/openbao/index.d.ts.map +1 -1
  12. package/dist/openbao/index.js +589 -305
  13. package/dist/openbao/index.js.map +16 -8
  14. package/dist/openbao/mount-path.d.ts +14 -0
  15. package/dist/openbao/mount-path.d.ts.map +1 -0
  16. package/dist/openbao/plugin-form.d.ts +60 -0
  17. package/dist/openbao/plugin-form.d.ts.map +1 -0
  18. package/dist/openbao/plugin-reconcile.d.ts +18 -0
  19. package/dist/openbao/plugin-reconcile.d.ts.map +1 -0
  20. package/dist/openbao/plugin-wire.d.ts +45 -0
  21. package/dist/openbao/plugin-wire.d.ts.map +1 -0
  22. package/dist/openbao/plugin.d.ts +31 -0
  23. package/dist/openbao/plugin.d.ts.map +1 -0
  24. package/dist/openbao/ssh-role-form.d.ts +6 -5
  25. package/dist/openbao/ssh-role-form.d.ts.map +1 -1
  26. package/dist/openbao/ssh-role.d.ts.map +1 -1
  27. package/dist/proxmox/index.d.ts +23 -20
  28. package/dist/proxmox/index.d.ts.map +1 -1
  29. package/dist/proxmox/index.js +19 -1
  30. package/dist/proxmox/index.js.map +1 -1
  31. package/package.json +1 -1
  32. package/src/openbao/approle-login-form.test.ts +64 -0
  33. package/src/openbao/approle-login-form.ts +153 -0
  34. package/src/openbao/approle-login-result.ts +96 -0
  35. package/src/openbao/approle-login.test.ts +200 -0
  36. package/src/openbao/approle-login.ts +156 -0
  37. package/src/openbao/auth-role-form.test.ts +42 -0
  38. package/src/openbao/bao-http.ts +2 -0
  39. package/src/openbao/index.ts +13 -0
  40. package/src/openbao/mount-path.test.ts +23 -0
  41. package/src/openbao/mount-path.ts +25 -0
  42. package/src/openbao/mount-wire.test.ts +13 -0
  43. package/src/openbao/plugin-form.test.ts +86 -0
  44. package/src/openbao/plugin-form.ts +164 -0
  45. package/src/openbao/plugin-reconcile.ts +92 -0
  46. package/src/openbao/plugin-wire.test.ts +146 -0
  47. package/src/openbao/plugin-wire.ts +65 -0
  48. package/src/openbao/plugin.ts +100 -0
  49. package/src/openbao/ssh-role-form.test.ts +105 -0
  50. package/src/openbao/ssh-role-form.ts +18 -16
  51. package/src/openbao/ssh-role.ts +3 -4
  52. package/src/proxmox/index.test.ts +53 -0
  53. package/src/proxmox/index.ts +26 -20
package/README.md CHANGED
@@ -64,6 +64,63 @@ from `rules` is a rule deleted. That is the same shape as the Cloudflare API its
64
64
  ⛔ **Deletion is refused by design.** Removing a lock is removing a retention floor, which
65
65
  is the one operation this resource exists to make hard. It retains on destroy.
66
66
 
67
+ ## OpenBao — `@homeflare/alchemy/openbao`
68
+
69
+ Vault objects as resources — `BaoMount`, `BaoAuthMethod`, `BaoPolicy`, `BaoAuthRole`,
70
+ `BaoPkiRole`, `BaoSshRole`, `BaoCloudflareRole`, `BaoProxmoxRole`, `BaoPlugin` — plus an AppRole
71
+ login for scripts. Every call resolves `BAO_ADDR` / `BAO_AGENT_ADDR` / `BAO_NAMESPACE` /
72
+ `BAO_TOKEN` (or the `VAULT_*` twins) the way the `bao` CLI does. A stack provides each
73
+ `Bao*Provider()` it uses, plus `FetchHttpClient.layer`.
74
+
75
+ ⛔ **Metadata only.** Alchemy stores props and attributes unencrypted, so no secret, CA key or
76
+ plugin `env` is declarable. Every family defaults to `retain` on destroy.
77
+
78
+ ### appRoleLogin / revokeSelf
79
+
80
+ ```ts
81
+ import { BaoLoginError, appRoleLogin, revokeSelf } from '@homeflare/alchemy/openbao';
82
+
83
+ const login = await appRoleLogin({ roleId, secretId }); // mount defaults to `approle`
84
+ try {
85
+ use(login.clientToken, login.accessor, login.policies, login.leaseDurationSeconds);
86
+ } finally {
87
+ await revokeSelf(login.clientToken);
88
+ }
89
+ ```
90
+
91
+ - ⛔ The login never sends `BAO_TOKEN`, and a `BaoLoginError` never contains the credential.
92
+ OpenBao can echo a secret_id back in an error, so every error string is redacted.
93
+ - ⚠️ `clientToken` is a getter over a private field: `console.log(login)` prints `[Getter]` at
94
+ most (Bun) or nothing (Node), and JSON, a spread and `structuredClone` carry no token.
95
+ - ⚠️ A value with leading or trailing whitespace is refused, not trimmed. `Bun.file().text()`
96
+ keeps a file's trailing newline.
97
+ - `reason` is `input`, `refused`, `unreachable` or `response`. Pass `env` to use something other
98
+ than `process.env`. `appRoleLoginEffect` / `revokeSelfEffect` are the same calls as Effects.
99
+
100
+ ### BaoPlugin
101
+
102
+ ```ts
103
+ import { BaoPlugin } from '@homeflare/alchemy/openbao';
104
+ import * as Effect from 'effect/Effect';
105
+
106
+ export const plugins = Effect.gen(function* () {
107
+ yield* BaoPlugin('plugin-example', {
108
+ name: 'openbao-plugin-secrets-example',
109
+ type: 'secret', // 'secret' | 'auth' | 'database'
110
+ command: 'openbao-plugin-secrets-example', // a bare file name in plugin_directory
111
+ sha256: '<hex sha256 of that file>',
112
+ version: 'v0.1.2', // canonical semver
113
+ });
114
+ });
115
+ ```
116
+
117
+ - ⛔ It registers a binary that is already in `plugin_directory`. It does not copy the binary.
118
+ - ⚠️ **If the binary reports its own version, declare exactly that one.** OpenBao refuses any
119
+ other ("plugin version mismatch"), and files an unversioned registration under the reported
120
+ one, so reconcile then refuses because the read-back finds nothing.
121
+ - ⚠️ A new `sha256` does not restart running mounts. Reload with `sys/plugins/reload/backend`.
122
+ - The deploying token needs `sudo` on `sys/plugins/catalog/*`.
123
+
67
124
  ## Credentials
68
125
 
69
126
  `CLOUDFLARE_API_TOKEN` is read from the environment at call time, never at module scope.
@@ -0,0 +1,72 @@
1
+ /** Why a login or a revoke failed. */
2
+ export type BaoLoginFailure =
3
+ /** Refused before any request was sent: an empty or padded value, or a mount that is not a path. */
4
+ 'input'
5
+ /** OpenBao (or something at BAO_ADDR) answered with a non-2xx status. */
6
+ | 'refused'
7
+ /** No answer at all — transport, timeout, or an address nothing listens on. */
8
+ | 'unreachable'
9
+ /** A 2xx that is not a usable answer: not JSON, no auth block, or MFA still required. */
10
+ | 'response';
11
+ /**
12
+ * The failure a transport-level BaoError stands for.
13
+ *
14
+ * ⚠️ A 2xx CAN FAIL TOO, AND IT IS NOT A REFUSAL. bao-status.ts settle fails a success whose body is
15
+ * not a JSON object — the shape of a BAO_ADDR that points at some other web server. The first
16
+ * version mapped every non-zero status to `refused`, so that read as `refused 200`: a server that
17
+ * said yes, reported as one that said no.
18
+ */
19
+ export declare const failureOf: (status: number) => BaoLoginFailure;
20
+ /**
21
+ * A failed login or revoke.
22
+ *
23
+ * ⛔ IT NEVER CARRIES A CREDENTIAL. It is built from the operation name, a status and a list of
24
+ * strings that have been through `redact` — never from the request, a header, or a `cause`, for
25
+ * the reason bao-status.ts gives for BaoError.
26
+ */
27
+ export declare class BaoLoginError extends Error {
28
+ readonly reason: BaoLoginFailure;
29
+ /** The HTTP status, or 0 when none applies (input, unreachable, an unusable auth block). */
30
+ readonly status: number;
31
+ readonly errors: readonly string[];
32
+ constructor(reason: BaoLoginFailure,
33
+ /** The HTTP status, or 0 when none applies (input, unreachable, an unusable auth block). */
34
+ status: number, errors: readonly string[], operation: string);
35
+ }
36
+ /**
37
+ * Why a value cannot be sent, or undefined when it can.
38
+ *
39
+ * ⚠️ PADDING IS REFUSED, NOT TRIMMED. `await Bun.file(path).text()` keeps a secret_id file's trailing
40
+ * newline, and OpenBao answers that with "invalid role or secret ID" — the same words it uses for
41
+ * a revoked or expired credential, so the real cause is invisible from the error. Trimming quietly
42
+ * would send a different value from the one the caller holds; refusing names the problem.
43
+ * ⚠️ `unknown`, NOT `string`: a plain script passes `process.env.X`, which is `undefined` when unset.
44
+ */
45
+ export declare const refusal: (label: string, value: unknown) => string | undefined;
46
+ /** The auth mount without slashes; `approle` when the caller did not say. */
47
+ export declare const loginMount: (mount: string | undefined) => string;
48
+ /**
49
+ * Why `mount` cannot name an auth mount, or undefined when it can.
50
+ *
51
+ * ⛔ THE MOUNT IS SPLICED INTO A URL, AND THE URL PARSER RESOLVES `..` AND ENDS THE PATH AT `?` OR
52
+ * `#`. `new URL('…/v1/auth/../sys/x/login')` is `…/v1/sys/x/login` — the role_id and secret_id
53
+ * would be sent to a path the caller never named. Refused rather than escaped, like padding.
54
+ */
55
+ export declare const mountProblem: (mount: string | undefined) => string | undefined;
56
+ /** `auth/<mount>/login` — what api/auth/approle/approle.go:145 writes. */
57
+ export declare const loginPath: (mount: string | undefined) => string;
58
+ /**
59
+ * Every string in `errors` with each credential replaced by `[redacted]`.
60
+ *
61
+ * ⛔ OPENBAO CAN ECHO THE SECRET_ID BACK, SO THIS IS NOT DEFENCE IN DEPTH — IT IS THE DEFENCE.
62
+ * openbao v2.6.2 builtin/credential/approle/path_login.go:291 answers a limited-use secret_id
63
+ * whose storage entry vanished between two reads with `invalid secret_id "<the secret_id>"`.
64
+ * BaoError keeps OpenBao's `errors` verbatim (that is its job), so every string that leaves the
65
+ * login or revoke passes through here first.
66
+ * ⚠️ THAT ECHO IS `%q`, SO THE RAW VALUE IS NOT ENOUGH. A custom secret_id holding `"` or `\` comes
67
+ * back escaped (`\"`, `\\`) and would slip past a raw match; the Go-quoted form is redacted too.
68
+ * ⚠️ LONGEST FIRST. Were one value a substring of another, redacting the shorter first would break
69
+ * the longer one's match and leave the rest of it in the message.
70
+ */
71
+ export declare const redact: (errors: readonly string[], secrets: readonly string[]) => readonly string[];
72
+ //# sourceMappingURL=approle-login-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approle-login-form.d.ts","sourceRoot":"","sources":["../../src/openbao/approle-login-form.ts"],"names":[],"mappings":"AAcA,sCAAsC;AACtC,MAAM,MAAM,eAAe;AACzB,oGAAoG;AAClG,OAAO;AACT,yEAAyE;GACvE,SAAS;AACX,+EAA+E;GAC7E,aAAa;AACf,yFAAyF;GACvF,UAAU,CAAC;AAEf;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,WAAY,MAAM,KAAG,eAC4C,CAAC;AAExF;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAEpC,QAAQ,CAAC,MAAM,EAAE,eAAe;IAChC,4FAA4F;IAC5F,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE;IAJpC,YACW,MAAM,EAAE,eAAe;IAChC,4FAA4F;IACnF,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,SAAS,MAAM,EAAE,EAClC,SAAS,EAAE,MAAM,EAKlB;CACF;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,UAAW,MAAM,SAAS,OAAO,KAAG,MAAM,GAAG,SAOhE,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,UAAU,UAAW,MAAM,GAAG,SAAS,KAAG,MAAyC,CAAC;AAEjG;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,UAAW,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SAWjE,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,SAAS,UAAW,MAAM,GAAG,SAAS,KAAG,MAA2C,CAAC;AA8BlG;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,MAAM,WACT,SAAS,MAAM,EAAE,WAChB,SAAS,MAAM,EAAE,KACzB,SAAS,MAAM,EAOjB,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * What a successful AppRole login hands back, and how it is read out of a 2xx body. Split from
3
+ * approle-login-form.ts, which decides what goes IN; this decides what comes OUT and how the token
4
+ * stays out of every log line that prints the result.
5
+ *
6
+ * ★ READ FROM openbao v2.6.2 api/secret.go:304-318 SecretAuth — `client_token`, `accessor`,
7
+ * `policies`, `lease_duration` and `mfa_requirement`. (api/*.go cited here diffed identical
8
+ * between v2.6.0 and v2.6.2.)
9
+ */
10
+ /** What a successful login hands back. */
11
+ export interface AppRoleLogin {
12
+ /**
13
+ * The token. ⛔ A GETTER OVER A PRIVATE FIELD, NEVER AN OWN PROPERTY — see LoginResult below.
14
+ * `console.log(login)` prints `clientToken: [Getter]` at most, and `JSON.stringify(login)`,
15
+ * `{ ...login }` and `structuredClone(login)` carry no token at all. Read it by name.
16
+ */
17
+ readonly clientToken: string;
18
+ /** The token's accessor — safe to log, and what revoke-accessor and audit lines name. */
19
+ readonly accessor: string;
20
+ /** Every policy on the token: the role's token_policies plus identity policies. */
21
+ readonly policies: readonly string[];
22
+ /** The token TTL in seconds, from the role's `token_ttl`. */
23
+ readonly leaseDurationSeconds: number;
24
+ }
25
+ /**
26
+ * The login in a 2xx body, or the reason there is none.
27
+ *
28
+ * ⚠️ AN EMPTY `client_token` WITH A `mfa_requirement` IS AN UNFINISHED LOGIN, NOT A BROKEN ONE. The
29
+ * auth block carries that field for a login that still owes an MFA step (api/secret.go:317). A
30
+ * script cannot complete that step, so it is named rather than reported as a missing field.
31
+ * REASONED FROM THE CLIENT TYPE — no MFA-enforced login was run.
32
+ * ⛔ THE BODY IS NEVER QUOTED. It holds the token on success and is not OpenBao's on a wrong address.
33
+ */
34
+ export declare const loginOf: (body: Record<string, unknown> | undefined) => {
35
+ readonly login: AppRoleLogin;
36
+ } | {
37
+ readonly problem: string;
38
+ };
39
+ //# sourceMappingURL=approle-login-result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approle-login-result.d.ts","sourceRoot":"","sources":["../../src/openbao/approle-login-result.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,0CAA0C;AAC1C,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,6DAA6D;IAC7D,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;CACvC;AAmCD;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,SACZ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,KACxC;IAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAyB/D,CAAC"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * AppRole login and self-revoke for a plain Bun script — the one part of the openbao subpath that is
3
+ * not a Resource.
4
+ *
5
+ * ★ WHY IT LIVES HERE. Wrapper scripts log in with a per-host AppRole and revoke the token on exit.
6
+ * Each one hand-rolling a `fetch` is how two of them end up disagreeing about BAO_ADDR versus
7
+ * BAO_AGENT_ADDR, or dropping the namespace header. This rides the transport every Bao.* family
8
+ * already uses: bao-address.ts resolution (BAO_ADDR, BAO_AGENT_ADDR, BAO_NAMESPACE, the VAULT_*
9
+ * twins, unix sockets), bao-http.ts's in-flight gate, timeout, transport retry and token
10
+ * redaction on spans, and bao-status.ts's status-first classification of the answer.
11
+ * ★ ONE IMPLEMENTATION, TWO SHAPES. `appRoleLoginEffect` and `revokeSelfEffect` are the
12
+ * implementation; `appRoleLogin` and `revokeSelf` run them on the fetch client for a caller with
13
+ * no Effect runtime. Both fail with BaoLoginError and nothing else.
14
+ *
15
+ * ⚠️ SHARING A secret_id ACROSS HOSTS THROUGH A CACHING PROXY SHARES THE TOKEN. Two hosts sending
16
+ * the same AppRole login through a caching OpenBao Proxy get the SAME cached token back, so two
17
+ * hosts become one identity in every audit line (recorded as a trap in the 2026-09-21 vault
18
+ * consolidation plan; not re-measured here). Give each host its own secret_id.
19
+ */
20
+ import * as Effect from 'effect/Effect';
21
+ import type * as HttpClient from 'effect/unstable/http/HttpClient';
22
+ import type { BaoEnvironment } from './bao-address.ts';
23
+ import { BaoLoginError } from './approle-login-form.ts';
24
+ import { type AppRoleLogin } from './approle-login-result.ts';
25
+ export type { BaoLoginFailure } from './approle-login-form.ts';
26
+ export type { AppRoleLogin } from './approle-login-result.ts';
27
+ export { BaoLoginError };
28
+ export interface AppRoleLoginInput {
29
+ /** The role_id. Not a secret on its own, but half of one — it is redacted from errors too. */
30
+ readonly roleId: string;
31
+ /** ⛔ The secret_id. Never logged, never in an error — see `redact`. */
32
+ readonly secretId: string;
33
+ /** The AppRole auth mount, e.g. `approle` (the default) — no `.`/`..` segments, `?` or `#`. */
34
+ readonly mount?: string;
35
+ }
36
+ /**
37
+ * Log in with a role_id and secret_id, under BaoEnv (process.env unless a caller provides one).
38
+ *
39
+ * ⛔ THE LOGIN SENDS NO TOKEN, WHATEVER THE ENVIRONMENT HOLDS. BAO_TOKEN is overridden with a
40
+ * PRESENT-BUT-EMPTY value for this one call, which bao-address.ts readBaoVariable treats as
41
+ * winning over VAULT_TOKEN, so headersFor omits X-Vault-Token entirely. A script that already
42
+ * had an operator token exported must not send it along with a machine login.
43
+ * ⚠️ A DROPPED TRANSPORT IS RETRIED TWICE (bao-http.ts retryTransport, status 0 only). If the first
44
+ * request reached OpenBao and only the answer was lost, the retry is a second login: one extra
45
+ * token that lives until its TTL, and — for a secret_id with `secret_id_num_uses` — one extra use.
46
+ * A single-use secret_id can therefore come back "invalid" on a flaky path.
47
+ * ★ `PUT`, THE METHOD api/logical.go:325 WriteWithContext SENDS, which is what the official Go helper
48
+ * calls (api/auth/approle/approle.go:146). The docs show POST; both are the update operation.
49
+ */
50
+ export declare const appRoleLoginEffect: (input: AppRoleLoginInput) => Effect.Effect<AppRoleLogin, BaoLoginError, HttpClient.HttpClient>;
51
+ /**
52
+ * Revoke `token` itself — what a wrapper runs on exit, so a login does not outlive its job.
53
+ *
54
+ * ★ `PUT auth/token/revoke-self`, READ FROM openbao v2.6.2 api/auth_token.go:318-330: the token
55
+ * revoked is the one in X-Vault-Token, and the body is empty. `token` rides in that header for
56
+ * this call only (BAO_TOKEN is overridden), whatever the environment holds.
57
+ * ⚠️ AN ALREADY-REVOKED OR EXPIRED TOKEN IS A FAILURE, NOT A NO-OP. OpenBao refuses a token it no
58
+ * longer knows (permission denied — not measured here), and a refusal from the wrong server looks
59
+ * the same; "I could not tell" stays an error. A wrapper that only wants best-effort cleanup on
60
+ * exit can catch BaoLoginError with reason `refused`.
61
+ * ⚠️ NEVER THROUGH AN AGENT OR PROXY IN `use_auto_auth_token = "force"` MODE. Force overwrites the
62
+ * request's token with the agent's own (bao-address.ts headersFor cites apiproxy.mdx), so this
63
+ * would revoke the AGENT's token. The estate's LAN proxy runs with no auto-auth at all.
64
+ */
65
+ export declare const revokeSelfEffect: (token: string) => Effect.Effect<void, BaoLoginError, HttpClient.HttpClient>;
66
+ /**
67
+ * `appRoleLoginEffect` for a plain script. `env` defaults to `process.env`, read at call time.
68
+ *
69
+ * const login = await appRoleLogin({ roleId, secretId });
70
+ * try { … login.clientToken … } finally { await revokeSelf(login.clientToken); }
71
+ */
72
+ export declare const appRoleLogin: (input: AppRoleLoginInput & {
73
+ readonly env?: BaoEnvironment;
74
+ }) => Promise<AppRoleLogin>;
75
+ /** `revokeSelfEffect` for a plain script. `env` defaults to `process.env`, read at call time. */
76
+ export declare const revokeSelf: (token: string, env?: BaoEnvironment) => Promise<void>;
77
+ //# sourceMappingURL=approle-login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approle-login.d.ts","sourceRoot":"","sources":["../../src/openbao/approle-login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EACL,aAAa,EAMd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,KAAK,YAAY,EAAW,MAAM,2BAA2B,CAAC;AAEvE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,YAAY,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,CAAC;AAEzB,MAAM,WAAW,iBAAiB;IAChC,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+FAA+F;IAC/F,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAeD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,UACtB,iBAAiB,KACvB,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,aAAa,EAAE,UAAU,CAAC,UAAU,CAsB/D,CAAC;AAEL;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,UACpB,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,CAAC,UAAU,CAWvD,CAAC;AAeL;;;;;GAKG;AACH,eAAO,MAAM,YAAY,UAChB,iBAAiB,GAAG;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,cAAc,CAAA;CAAE,KAC3D,OAAO,CAAC,YAAY,CAAkD,CAAC;AAE1E,iGAAiG;AACjG,eAAO,MAAM,UAAU,UAAW,MAAM,QAAQ,cAAc,KAAG,OAAO,CAAC,IAAI,CACtC,CAAC"}
@@ -64,6 +64,8 @@ export declare const baoRead: (path: string) => Effect.Effect<Record<string, unk
64
64
  * ⚠️ ROLE BODIES STAY ALL-STRING ON PURPOSE. `bao write k=v` put every value in the JSON body as a
65
65
  * string — MEASURED with `-output-curl-string`, see ssh-role-form.ts — and the form files still
66
66
  * build exactly that body, so the server parses the same request it was always sent.
67
+ * ⛔ EXCEPT A MAP FIELD: sdk v2.6.2 refuses a string for a TypeMap with a 400, so Bao.SshRole
68
+ * sends its two maps as objects (the ⛔ on mapValue in ssh-role-form.ts, measured 2026-09-21).
67
69
  */
68
70
  export declare const baoWrite: (method: 'POST' | 'PUT', path: string, body: Readonly<Record<string, unknown>>) => Effect.Effect<void, BaoError, HttpClient.HttpClient>;
69
71
  /** A delete: idempotent — already gone (404) is success — and refused (403, 5xx) is a failure. */
@@ -1 +1 @@
1
- {"version":3,"file":"bao-http.d.ts","sourceRoot":"","sources":["../../src/openbao/bao-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,SAAS,MAAM,kBAAkB,CAAC;AAG9C,OAAO,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAG9D,OAAO,EAAE,QAAQ,EAAE,KAAK,SAAS,EAAU,MAAM,iBAAiB,CAAC;AAEnE;;;GAGG;AACH,eAAO,MAAM,MAAM,iEAEjB,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,aAAa,IAAI,CAAC;AAE/B,eAAO,MAAM,OAAO,wCAElB,CAAC;AAiEH,mGAAmG;AACnG,eAAO,MAAM,OAAO,WACV,SAAS,UACT,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,QACnC,MAAM,SACL,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,wFAoCtC,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,OAAO,SAAU,MAAM,wFAOhC,CAAC;AAEL;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,WACX,MAAM,GAAG,KAAK,QAChB,MAAM,QACN,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,yDACe,CAAC;AAEzD,kGAAkG;AAClG,eAAO,MAAM,SAAS,SAAU,MAAM,yDAAqD,CAAC"}
1
+ {"version":3,"file":"bao-http.d.ts","sourceRoot":"","sources":["../../src/openbao/bao-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,SAAS,MAAM,kBAAkB,CAAC;AAG9C,OAAO,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAG9D,OAAO,EAAE,QAAQ,EAAE,KAAK,SAAS,EAAU,MAAM,iBAAiB,CAAC;AAEnE;;;GAGG;AACH,eAAO,MAAM,MAAM,iEAEjB,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,aAAa,IAAI,CAAC;AAE/B,eAAO,MAAM,OAAO,wCAElB,CAAC;AAiEH,mGAAmG;AACnG,eAAO,MAAM,OAAO,WACV,SAAS,UACT,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,QACnC,MAAM,SACL,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,wFAoCtC,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,OAAO,SAAU,MAAM,wFAOhC,CAAC;AAEL;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,WACX,MAAM,GAAG,KAAK,QAChB,MAAM,QACN,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,yDACe,CAAC;AAEzD,kGAAkG;AAClG,eAAO,MAAM,SAAS,SAAU,MAAM,yDAAqD,CAAC"}
@@ -7,7 +7,12 @@
7
7
  * every helper as API and make the next refactor a breaking change.
8
8
  * ★ Anything unlisted is still reachable by path if you genuinely need it — that is a
9
9
  * deliberate, visible act rather than an accident of barrelling.
10
+ * ★ THE APPROLE LOGIN IS HERE FOR SCRIPTS, NOT STACKS (approle-login.ts). A wrapper that logs in
11
+ * and revokes on exit needs it, and giving it the families' own transport is what keeps a script
12
+ * and a stack from disagreeing about which server BAO_ADDR means.
10
13
  */
14
+ export type { AppRoleLogin, AppRoleLoginInput, BaoLoginFailure } from './approle-login.ts';
15
+ export { BaoLoginError, appRoleLogin, appRoleLoginEffect, revokeSelf, revokeSelfEffect, } from './approle-login.ts';
11
16
  export { BaoAuthMethod, BaoAuthMethodProvider } from './auth-method.ts';
12
17
  export { BaoAuthRole, BaoAuthRoleProvider } from './auth-role.ts';
13
18
  export { BaoCloudflareRole, BaoCloudflareRoleProvider } from './cloudflare-role.ts';
@@ -18,6 +23,8 @@ export type { RolesConfig, SurfaceSpec } from './cloudflare-roles-config.ts';
18
23
  export { parseRolesConfig } from './cloudflare-roles-config.ts';
19
24
  export { BaoMount, BaoMountProvider } from './mount.ts';
20
25
  export { BaoPkiRole, BaoPkiRoleProvider } from './pki-role.ts';
26
+ export type { BaoPluginProps, BaoPluginType } from './plugin.ts';
27
+ export { BaoPlugin, BaoPluginProvider } from './plugin.ts';
21
28
  export { BaoPolicy, BaoPolicyProvider } from './policy.ts';
22
29
  export { BaoProxmoxRole, BaoProxmoxRoleProvider } from './proxmox-role.ts';
23
30
  export { BaoSshRole, BaoSshRoleProvider } from './ssh-role.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/openbao/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,6BAA6B,GAC9B,MAAM,mCAAmC,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACnF,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/openbao/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC3F,OAAO,EACL,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,UAAU,EACV,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,6BAA6B,GAC9B,MAAM,mCAAmC,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACnF,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}