@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.
- package/README.md +57 -0
- package/dist/openbao/approle-login-form.d.ts +72 -0
- package/dist/openbao/approle-login-form.d.ts.map +1 -0
- package/dist/openbao/approle-login-result.d.ts +39 -0
- package/dist/openbao/approle-login-result.d.ts.map +1 -0
- package/dist/openbao/approle-login.d.ts +77 -0
- package/dist/openbao/approle-login.d.ts.map +1 -0
- package/dist/openbao/bao-http.d.ts +2 -0
- package/dist/openbao/bao-http.d.ts.map +1 -1
- package/dist/openbao/index.d.ts +7 -0
- package/dist/openbao/index.d.ts.map +1 -1
- package/dist/openbao/index.js +589 -305
- package/dist/openbao/index.js.map +16 -8
- package/dist/openbao/mount-path.d.ts +14 -0
- package/dist/openbao/mount-path.d.ts.map +1 -0
- package/dist/openbao/plugin-form.d.ts +60 -0
- package/dist/openbao/plugin-form.d.ts.map +1 -0
- package/dist/openbao/plugin-reconcile.d.ts +18 -0
- package/dist/openbao/plugin-reconcile.d.ts.map +1 -0
- package/dist/openbao/plugin-wire.d.ts +45 -0
- package/dist/openbao/plugin-wire.d.ts.map +1 -0
- package/dist/openbao/plugin.d.ts +31 -0
- package/dist/openbao/plugin.d.ts.map +1 -0
- package/dist/openbao/ssh-role-form.d.ts +6 -5
- package/dist/openbao/ssh-role-form.d.ts.map +1 -1
- package/dist/openbao/ssh-role.d.ts.map +1 -1
- package/dist/proxmox/index.d.ts +23 -20
- package/dist/proxmox/index.d.ts.map +1 -1
- package/dist/proxmox/index.js +19 -1
- package/dist/proxmox/index.js.map +1 -1
- package/package.json +1 -1
- package/src/openbao/approle-login-form.test.ts +64 -0
- package/src/openbao/approle-login-form.ts +153 -0
- package/src/openbao/approle-login-result.ts +96 -0
- package/src/openbao/approle-login.test.ts +200 -0
- package/src/openbao/approle-login.ts +156 -0
- package/src/openbao/auth-role-form.test.ts +42 -0
- package/src/openbao/bao-http.ts +2 -0
- package/src/openbao/index.ts +13 -0
- package/src/openbao/mount-path.test.ts +23 -0
- package/src/openbao/mount-path.ts +25 -0
- package/src/openbao/mount-wire.test.ts +13 -0
- package/src/openbao/plugin-form.test.ts +86 -0
- package/src/openbao/plugin-form.ts +164 -0
- package/src/openbao/plugin-reconcile.ts +92 -0
- package/src/openbao/plugin-wire.test.ts +146 -0
- package/src/openbao/plugin-wire.ts +65 -0
- package/src/openbao/plugin.ts +100 -0
- package/src/openbao/ssh-role-form.test.ts +105 -0
- package/src/openbao/ssh-role-form.ts +18 -16
- package/src/openbao/ssh-role.ts +3 -4
- package/src/proxmox/index.test.ts +53 -0
- 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
|
|
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"}
|
package/dist/openbao/index.d.ts
CHANGED
|
@@ -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
|
|
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"}
|