@homeflare/alchemy 0.4.1 → 0.6.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 +26 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/launchd/fake-runner.d.ts +36 -0
- package/dist/launchd/fake-runner.d.ts.map +1 -0
- package/dist/launchd/host-effect.d.ts +19 -0
- package/dist/launchd/host-effect.d.ts.map +1 -0
- package/dist/launchd/host-file-form.d.ts +24 -0
- package/dist/launchd/host-file-form.d.ts.map +1 -0
- package/dist/launchd/host-file-lifecycle.d.ts +29 -0
- package/dist/launchd/host-file-lifecycle.d.ts.map +1 -0
- package/dist/launchd/host-file.d.ts +25 -0
- package/dist/launchd/host-file.d.ts.map +1 -0
- package/dist/launchd/host-lookup.d.ts +27 -0
- package/dist/launchd/host-lookup.d.ts.map +1 -0
- package/dist/launchd/index.d.ts +21 -0
- package/dist/launchd/index.d.ts.map +1 -0
- package/dist/launchd/index.js +969 -0
- package/dist/launchd/index.js.map +25 -0
- package/dist/launchd/job-form.d.ts +90 -0
- package/dist/launchd/job-form.d.ts.map +1 -0
- package/dist/launchd/job-lifecycle.d.ts +34 -0
- package/dist/launchd/job-lifecycle.d.ts.map +1 -0
- package/dist/launchd/job-preflight.d.ts +50 -0
- package/dist/launchd/job-preflight.d.ts.map +1 -0
- package/dist/launchd/job-validate.d.ts +25 -0
- package/dist/launchd/job-validate.d.ts.map +1 -0
- package/dist/launchd/job.d.ts +34 -0
- package/dist/launchd/job.d.ts.map +1 -0
- package/dist/launchd/launchctl.d.ts +62 -0
- package/dist/launchd/launchctl.d.ts.map +1 -0
- package/dist/launchd/local-runner.d.ts +7 -0
- package/dist/launchd/local-runner.d.ts.map +1 -0
- package/dist/launchd/plist.d.ts +41 -0
- package/dist/launchd/plist.d.ts.map +1 -0
- package/dist/launchd/providers.d.ts +15 -0
- package/dist/launchd/providers.d.ts.map +1 -0
- package/dist/launchd/runner.d.ts +91 -0
- package/dist/launchd/runner.d.ts.map +1 -0
- package/dist/launchd/secret-tripwire.d.ts +23 -0
- package/dist/launchd/secret-tripwire.d.ts.map +1 -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/auth-method-form.d.ts +7 -0
- package/dist/openbao/auth-method-form.d.ts.map +1 -1
- package/dist/openbao/auth-method-reconcile.d.ts +15 -0
- package/dist/openbao/auth-method-reconcile.d.ts.map +1 -0
- package/dist/openbao/auth-method.d.ts +6 -0
- package/dist/openbao/auth-method.d.ts.map +1 -1
- package/dist/openbao/auth-role-form.d.ts +9 -0
- package/dist/openbao/auth-role-form.d.ts.map +1 -1
- package/dist/openbao/auth-role.d.ts.map +1 -1
- package/dist/openbao/auth-token-form.d.ts +48 -0
- package/dist/openbao/auth-token-form.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/bao-identity.d.ts +81 -0
- package/dist/openbao/bao-identity.d.ts.map +1 -0
- package/dist/openbao/bao-namespace.d.ts +20 -0
- package/dist/openbao/bao-namespace.d.ts.map +1 -0
- package/dist/openbao/cloudflare-roles-expand.d.ts.map +1 -1
- package/dist/openbao/host-approles.d.ts +62 -0
- package/dist/openbao/host-approles.d.ts.map +1 -0
- package/dist/openbao/index.d.ts +26 -0
- package/dist/openbao/index.d.ts.map +1 -1
- package/dist/openbao/index.js +1889 -477
- package/dist/openbao/index.js.map +46 -17
- package/dist/openbao/jwt-config-form.d.ts +51 -0
- package/dist/openbao/jwt-config-form.d.ts.map +1 -0
- package/dist/openbao/jwt-config.d.ts +25 -0
- package/dist/openbao/jwt-config.d.ts.map +1 -0
- package/dist/openbao/jwt-role-form.d.ts +91 -0
- package/dist/openbao/jwt-role-form.d.ts.map +1 -0
- package/dist/openbao/jwt-role.d.ts +31 -0
- package/dist/openbao/jwt-role.d.ts.map +1 -0
- package/dist/openbao/kubernetes-role-form.d.ts +68 -0
- package/dist/openbao/kubernetes-role-form.d.ts.map +1 -0
- package/dist/openbao/kubernetes-role.d.ts +25 -0
- package/dist/openbao/kubernetes-role.d.ts.map +1 -0
- package/dist/openbao/mfa-enforcement-form.d.ts +46 -0
- package/dist/openbao/mfa-enforcement-form.d.ts.map +1 -0
- package/dist/openbao/mfa-enforcement.d.ts +41 -0
- package/dist/openbao/mfa-enforcement.d.ts.map +1 -0
- package/dist/openbao/mfa-totp-form.d.ts +52 -0
- package/dist/openbao/mfa-totp-form.d.ts.map +1 -0
- package/dist/openbao/mfa-totp.d.ts +33 -0
- package/dist/openbao/mfa-totp.d.ts.map +1 -0
- package/dist/openbao/mfa-wire.d.ts +63 -0
- package/dist/openbao/mfa-wire.d.ts.map +1 -0
- package/dist/openbao/mount-form.d.ts +7 -0
- package/dist/openbao/mount-form.d.ts.map +1 -1
- package/dist/openbao/mount-move.d.ts +49 -0
- package/dist/openbao/mount-move.d.ts.map +1 -0
- package/dist/openbao/mount-path.d.ts +18 -0
- package/dist/openbao/mount-path.d.ts.map +1 -0
- package/dist/openbao/mount-reconcile.d.ts +16 -0
- package/dist/openbao/mount-reconcile.d.ts.map +1 -0
- package/dist/openbao/mount.d.ts +9 -0
- package/dist/openbao/mount.d.ts.map +1 -1
- package/dist/openbao/pki-role-canonical.d.ts +46 -0
- package/dist/openbao/pki-role-canonical.d.ts.map +1 -0
- package/dist/openbao/pki-role-form.d.ts +38 -30
- package/dist/openbao/pki-role-form.d.ts.map +1 -1
- package/dist/openbao/pki-role.d.ts.map +1 -1
- 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/remount-wire.d.ts +44 -0
- package/dist/openbao/remount-wire.d.ts.map +1 -0
- package/dist/openbao/role-reconcile.d.ts +40 -0
- package/dist/openbao/role-reconcile.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/docs/launchd.md +167 -0
- package/package.json +7 -2
- package/src/index.ts +1 -0
- package/src/launchd/fake-runner.ts +176 -0
- package/src/launchd/host-effect.ts +28 -0
- package/src/launchd/host-file-form.ts +71 -0
- package/src/launchd/host-file-lifecycle.test.ts +198 -0
- package/src/launchd/host-file-lifecycle.ts +164 -0
- package/src/launchd/host-file.ts +64 -0
- package/src/launchd/host-lookup.test.ts +92 -0
- package/src/launchd/host-lookup.ts +94 -0
- package/src/launchd/index.ts +26 -0
- package/src/launchd/job-form.test.ts +171 -0
- package/src/launchd/job-form.ts +180 -0
- package/src/launchd/job-identity.test.ts +147 -0
- package/src/launchd/job-lifecycle.test.ts +220 -0
- package/src/launchd/job-lifecycle.ts +194 -0
- package/src/launchd/job-preflight.ts +152 -0
- package/src/launchd/job-validate.ts +134 -0
- package/src/launchd/job.ts +96 -0
- package/src/launchd/launchctl.test.ts +181 -0
- package/src/launchd/launchctl.ts +188 -0
- package/src/launchd/local-runner.test.ts +125 -0
- package/src/launchd/local-runner.ts +143 -0
- package/src/launchd/plist.test.ts +186 -0
- package/src/launchd/plist.ts +156 -0
- package/src/launchd/providers.test.ts +222 -0
- package/src/launchd/providers.ts +21 -0
- package/src/launchd/runner.ts +95 -0
- package/src/launchd/secret-tripwire.test.ts +110 -0
- package/src/launchd/secret-tripwire.ts +81 -0
- package/src/openbao/README.md +181 -0
- package/src/openbao/REPLACE.md +87 -0
- 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-method-form.ts +7 -0
- package/src/openbao/auth-method-reconcile.ts +74 -0
- package/src/openbao/auth-method.ts +20 -41
- package/src/openbao/auth-role-form.test.ts +57 -0
- package/src/openbao/auth-role-form.ts +11 -0
- package/src/openbao/auth-role.ts +12 -0
- package/src/openbao/auth-token-form.ts +161 -0
- package/src/openbao/bao-http.ts +2 -0
- package/src/openbao/bao-identity.test.ts +131 -0
- package/src/openbao/bao-identity.ts +188 -0
- package/src/openbao/bao-namespace.ts +32 -0
- package/src/openbao/cloudflare-roles-expand.ts +2 -5
- package/src/openbao/host-approles.test.ts +121 -0
- package/src/openbao/host-approles.ts +126 -0
- package/src/openbao/index.ts +32 -0
- package/src/openbao/jwt-config-form.ts +161 -0
- package/src/openbao/jwt-config.ts +85 -0
- package/src/openbao/jwt-role-form.test.ts +130 -0
- package/src/openbao/jwt-role-form.ts +231 -0
- package/src/openbao/jwt-role.ts +102 -0
- package/src/openbao/kubernetes-role-form.test.ts +82 -0
- package/src/openbao/kubernetes-role-form.ts +149 -0
- package/src/openbao/kubernetes-role.ts +94 -0
- package/src/openbao/mfa-enforcement-form.ts +147 -0
- package/src/openbao/mfa-enforcement.ts +143 -0
- package/src/openbao/mfa-totp-form.ts +155 -0
- package/src/openbao/mfa-totp.ts +118 -0
- package/src/openbao/mfa-wire.ts +117 -0
- package/src/openbao/mfa.test.ts +200 -0
- package/src/openbao/mount-form.ts +9 -1
- package/src/openbao/mount-move.test.ts +164 -0
- package/src/openbao/mount-move.ts +113 -0
- package/src/openbao/mount-path.test.ts +33 -0
- package/src/openbao/mount-path.ts +31 -0
- package/src/openbao/mount-reconcile.ts +75 -0
- package/src/openbao/mount-wire.test.ts +13 -0
- package/src/openbao/mount.ts +26 -43
- package/src/openbao/pki-role-canonical.ts +163 -0
- package/src/openbao/pki-role-form.test.ts +112 -0
- package/src/openbao/pki-role-form.ts +76 -110
- package/src/openbao/pki-role.ts +12 -27
- 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/remount-wire.test.ts +87 -0
- package/src/openbao/remount-wire.ts +112 -0
- package/src/openbao/role-reconcile.test.ts +104 -0
- package/src/openbao/role-reconcile.ts +84 -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
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The four `launchctl` subcommands LaunchdJob uses — print, print-disabled, bootstrap, bootout —
|
|
3
|
+
* over a HostRunner, plus the parsers for the two read-only ones.
|
|
4
|
+
*
|
|
5
|
+
* ★ MEASURED 2026-09-21 ON macOS 27.2 (read-only `print` / `print-disabled` only; nothing was
|
|
6
|
+
* bootstrapped or booted out to write this):
|
|
7
|
+
* - `print system/<unknown>` exits **113** with "Could not find service … in domain for system"
|
|
8
|
+
* on stderr. 113 is therefore "not loaded", not an error.
|
|
9
|
+
* - `print` of a loaded job is a `<target> = {` block. Its own fields sit one tab deep
|
|
10
|
+
* (`\tstate = running`, `\tpid = 1219`, `\tlast exit code = 0` or `(never exited)`); nested
|
|
11
|
+
* blocks (`resource coalition`, `semaphores`) repeat names like `state` two tabs deep.
|
|
12
|
+
* - `print-disabled <domain>` lists `"label" => enabled|disabled`.
|
|
13
|
+
* - reading needs no root: a uid-501 shell printed system-domain jobs.
|
|
14
|
+
* - `print gui/<uid>/<label>` for a uid with NO LOGIN SESSION exits **112** with "Could not find
|
|
15
|
+
* domain for user gui: <uid>" (and so does `print-disabled gui/<uid>`); a malformed target is
|
|
16
|
+
* 64, a missing service in a live gui domain is 113. So 112 + that text is "the domain does
|
|
17
|
+
* not exist", which means the job cannot be loaded — not an error for a read or a delete.
|
|
18
|
+
* ⚠️ REASONED, NOT MEASURED: bootstrap/bootout exit codes. nix-darwin's own activation script on the
|
|
19
|
+
* same host notes "`bootout` on an already-absent job is a no-op that returns non-zero", which is
|
|
20
|
+
* why bootout here is always preceded by a print and followed by a poll, never trusted alone.
|
|
21
|
+
* ⛔ NEVER PUT `print` STDOUT IN AN ERROR. It contains the job's whole environment.
|
|
22
|
+
*/
|
|
23
|
+
import type { HostRunner } from './runner.ts';
|
|
24
|
+
export declare const LAUNCHCTL = "/bin/launchctl";
|
|
25
|
+
/** `launchctl print` of an unknown service — measured, see above. */
|
|
26
|
+
export declare const NOT_FOUND = 113;
|
|
27
|
+
/** `launchctl print` into a gui domain with no login session — measured, see above. */
|
|
28
|
+
export declare const NO_DOMAIN = 112;
|
|
29
|
+
export declare class LaunchctlError extends Error {
|
|
30
|
+
constructor(command: string, exitCode: number, stderr: string);
|
|
31
|
+
}
|
|
32
|
+
export type ServiceStatus = {
|
|
33
|
+
readonly loaded: boolean;
|
|
34
|
+
/** The domain itself is absent (a gui/<uid> with no login session). Implies `loaded: false`. */
|
|
35
|
+
readonly domainMissing?: true;
|
|
36
|
+
readonly state?: string;
|
|
37
|
+
readonly pid?: number;
|
|
38
|
+
readonly lastExitCode?: number;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* The job block's own `key = value` lines, ignoring nested blocks.
|
|
42
|
+
* ⚠️ A multi-line ProgramArguments entry is printed raw inside `arguments = {`, so a close is only
|
|
43
|
+
* accepted when its indentation matches the open. An argument that itself contains a line of
|
|
44
|
+
* exactly one tab and `}` would still desynchronise this; no real job has one.
|
|
45
|
+
*/
|
|
46
|
+
export declare const printFields: (stdout: string) => Map<string, string>;
|
|
47
|
+
export declare const parsePrint: (stdout: string) => ServiceStatus;
|
|
48
|
+
/** Labels `print-disabled` reports as disabled. Older releases printed `=> true` for disabled. */
|
|
49
|
+
export declare const parseDisabled: (stdout: string) => Set<string>;
|
|
50
|
+
export declare const printService: (runner: HostRunner, target: string) => Promise<ServiceStatus>;
|
|
51
|
+
export declare const isDisabled: (runner: HostRunner, domain: string, label: string) => Promise<boolean>;
|
|
52
|
+
export declare const bootstrap: (runner: HostRunner, domain: string, plistPath: string) => Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Poll `print` until the service is gone. ⚠️ bootout can return before launchd lets go of it.
|
|
55
|
+
* ★ 30 s, because a job gets SIGTERM and then its ExitTimeOut before SIGKILL: launchd.plist(5)
|
|
56
|
+
* documents a 20 s default (a macOS 27.2 `print` showed `exit timeout = 5` for a job that sets
|
|
57
|
+
* none). Giving up sooner would fail a deploy over a job that was, correctly, still exiting.
|
|
58
|
+
*/
|
|
59
|
+
export declare const waitUnloaded: (runner: HostRunner, target: string, attempts?: number, intervalMs?: number) => Promise<void>;
|
|
60
|
+
/** Boot a service out if it is loaded, and wait until launchd has let go. Idempotent. */
|
|
61
|
+
export declare const bootoutIfLoaded: (runner: HostRunner, target: string) => Promise<void>;
|
|
62
|
+
//# sourceMappingURL=launchctl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launchctl.d.ts","sourceRoot":"","sources":["../../src/launchd/launchctl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,eAAO,MAAM,SAAS,mBAAmB,CAAC;AAC1C,qEAAqE;AACrE,eAAO,MAAM,SAAS,MAAM,CAAC;AAC7B,uFAAuF;AACvF,eAAO,MAAM,SAAS,MAAM,CAAC;AAG7B,qBAAa,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAG5D;CACF;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,gGAAgG;IAChG,QAAQ,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,WAAY,MAAM,KAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAuB9D,CAAC;AAOF,eAAO,MAAM,UAAU,WAAY,MAAM,KAAG,aAY3C,CAAC;AAEF,kGAAkG;AAClG,eAAO,MAAM,aAAa,WAAY,MAAM,KAAG,GAAG,CAAC,MAAM,CAQxD,CAAC;AAEF,eAAO,MAAM,YAAY,WAAkB,UAAU,UAAU,MAAM,KAAG,OAAO,CAAC,aAAa,CAW5F,CAAC;AAEF,eAAO,MAAM,UAAU,WACb,UAAU,UACV,MAAM,SACP,MAAM,KACZ,OAAO,CAAC,OAAO,CAMjB,CAAC;AAEF,eAAO,MAAM,SAAS,WACZ,UAAU,UACV,MAAM,aACH,MAAM,KAChB,OAAO,CAAC,IAAI,CAKd,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,WACf,UAAU,UACV,MAAM,6CAGb,OAAO,CAAC,IAAI,CAQd,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,eAAe,WAAkB,UAAU,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAqBtF,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HostRunner } from './runner.ts';
|
|
2
|
+
export type LocalRunnerOptions = {
|
|
3
|
+
/** Kill a spawned program after this long. @default 120_000 */
|
|
4
|
+
readonly execTimeoutMs?: number;
|
|
5
|
+
};
|
|
6
|
+
export declare const localRunner: (options?: LocalRunnerOptions) => HostRunner;
|
|
7
|
+
//# sourceMappingURL=local-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-runner.d.ts","sourceRoot":"","sources":["../../src/launchd/local-runner.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAwB,UAAU,EAAgB,MAAM,aAAa,CAAC;AAElF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAyEF,eAAO,MAAM,WAAW,aAAa,kBAAkB,KAAQ,UAgD9D,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An XML property list serializer — the subset launchd.plist(5) reads, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* ★ WHY NOT AN npm LIBRARY. Nothing in this workspace depends on one (checked 2026-09-21: no
|
|
5
|
+
* `plist`, `xmlbuilder` or `@xmldom/xmldom` anywhere under node_modules), and the common one pulls
|
|
6
|
+
* an XML DOM in for PARSING, which a provider that only RENDERS never does. The subset a launchd
|
|
7
|
+
* job needs is five node types, so this is ~100 lines plus exhaustive tests (plist.test.ts), which
|
|
8
|
+
* also round-trip every case through Apple's own `plutil` when the tests run on macOS.
|
|
9
|
+
*
|
|
10
|
+
* ⛔ THE OUTPUT MUST BE DETERMINISTIC. LaunchdJob diffs by the SHA-256 of these bytes, so two
|
|
11
|
+
* renders of one declaration must be byte-identical: dict keys are sorted, indentation is fixed,
|
|
12
|
+
* and nothing depends on insertion order. A non-deterministic render is a forever-`update`.
|
|
13
|
+
* ⚠️ AND A CHANGE TO THIS FILE'S OUTPUT RESTARTS EVERY JOB. A new header, indent or escape changes
|
|
14
|
+
* every digest, so the next deploy after the kit upgrade updates — restarts — every declared
|
|
15
|
+
* job at once. Treat any byte change here as a breaking change and say so in its changeset.
|
|
16
|
+
*
|
|
17
|
+
* ⛔ REFUSE, NEVER COERCE. `undefined`, `null`, a fraction, NaN, a class instance or a character XML
|
|
18
|
+
* 1.0 cannot carry is a thrown PlistError naming the path, not a silently dropped key. A dropped
|
|
19
|
+
* `KeepAlive` is a daemon that never restarts, and nothing downstream would say why.
|
|
20
|
+
*/
|
|
21
|
+
/** Every value launchd reads. ⚠️ No `<real>`, `<date>` or `<data>`: no launchd key needs one. */
|
|
22
|
+
export type PlistValue = string | number | boolean | readonly PlistValue[] | {
|
|
23
|
+
readonly [key: string]: PlistValue;
|
|
24
|
+
};
|
|
25
|
+
export type PlistDict = {
|
|
26
|
+
readonly [key: string]: PlistValue;
|
|
27
|
+
};
|
|
28
|
+
export declare class PlistError extends Error {
|
|
29
|
+
readonly path: string;
|
|
30
|
+
constructor(path: string, detail: string);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Escape text content.
|
|
34
|
+
* ⚠️ CR IS A CHARACTER REFERENCE, NOT A LITERAL. An XML parser normalises a literal CR (and CRLF)
|
|
35
|
+
* to LF before the application sees it (XML 1.0 §2.11), so `a\r\nb` would come back as `a\nb` and
|
|
36
|
+
* the argument launchd passes would differ from the one declared. ` ` survives normalisation.
|
|
37
|
+
*/
|
|
38
|
+
export declare const escapeText: (text: string, path: string) => string;
|
|
39
|
+
/** Render a top-level dict as a complete XML plist document, ending in a newline. */
|
|
40
|
+
export declare const renderPlist: (dict: PlistDict) => string;
|
|
41
|
+
//# sourceMappingURL=plist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plist.d.ts","sourceRoot":"","sources":["../../src/launchd/plist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,iGAAiG;AACjG,MAAM,MAAM,UAAU,GAClB,MAAM,GACN,MAAM,GACN,OAAO,GACP,SAAS,UAAU,EAAE,GACrB;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,CAAC;AAE3C,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,CAAC;AAE/D,qBAAa,UAAW,SAAQ,KAAK;IAEjC,QAAQ,CAAC,IAAI,EAAE,MAAM;IADvB,YACW,IAAI,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EAIf;CACF;AAoBD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,SAAU,MAAM,QAAQ,MAAM,KAAG,MAcvD,CAAC;AAoEF,qFAAqF;AACrF,eAAO,MAAM,WAAW,SAAU,SAAS,KAAG,MAO7C,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Both launchd-subpath providers with one HostRunner, as a single layer for a host stack.
|
|
3
|
+
*
|
|
4
|
+
* Layer.mergeAll(launchdProviders(), …the stack's other providers)
|
|
5
|
+
*
|
|
6
|
+
* ★ ONE RUNNER FOR BOTH, so a daemon's config file (HostFile) and the daemon (LaunchdJob) are
|
|
7
|
+
* written by the same identity on the same host — a stack cannot write the file as one user and
|
|
8
|
+
* bootstrap the job as another by accident.
|
|
9
|
+
* ⚠️ The default is localRunner(): this process, never elevated. A system-domain job therefore
|
|
10
|
+
* needs the deploy itself started as root, or a runner passed here with `privileged: true`.
|
|
11
|
+
*/
|
|
12
|
+
import * as Layer from 'effect/Layer';
|
|
13
|
+
import { type HostRunner } from './runner.ts';
|
|
14
|
+
export declare const launchdProviders: (runner?: HostRunner) => Layer.Layer<import("alchemy").Provider<import("./host-file.ts").HostFile> | import("alchemy").Provider<import("./job.ts").LaunchdJob>, never, never>;
|
|
15
|
+
//# sourceMappingURL=providers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/launchd/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAItC,OAAO,EAAE,KAAK,UAAU,EAAmB,MAAM,aAAa,CAAC;AAE/D,eAAO,MAAM,gBAAgB,YAAY,UAAU,yJAGhD,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one seam between the launchd providers and the host: every file read, file write and
|
|
3
|
+
* `launchctl` call goes through a HostRunner, and nothing in this directory touches the host any
|
|
4
|
+
* other way.
|
|
5
|
+
*
|
|
6
|
+
* ★ WHY A SEAM AND NOT DIRECT node:fs CALLS. launchd has no SDK — the provider renders a plist and
|
|
7
|
+
* drives `launchctl` — so the only way to test the lifecycle without a real launchd (and without
|
|
8
|
+
* writing /Library) is to swap the host out. fake-runner.ts is an in-memory host with a scripted
|
|
9
|
+
* launchd; local-runner.ts is the real one. The same seam is where a consumer plugs in a runner
|
|
10
|
+
* that reaches a host some other way.
|
|
11
|
+
*
|
|
12
|
+
* ★ PROMISES, NOT EFFECTS, ON PURPOSE. A consumer writing their own runner implements plain async
|
|
13
|
+
* functions; the providers lift them into Effect at the call site (host-effect.ts).
|
|
14
|
+
*/
|
|
15
|
+
import * as Context from 'effect/Context';
|
|
16
|
+
import * as Layer from 'effect/Layer';
|
|
17
|
+
/** One finished process. ⛔ `stdout` may carry anything the job's environment holds — never log it. */
|
|
18
|
+
export type ExecResult = {
|
|
19
|
+
readonly exitCode: number;
|
|
20
|
+
readonly stdout: string;
|
|
21
|
+
readonly stderr: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* What `lstat` says about a path. ⚠️ lstat, not stat: a symlink is reported as a symlink, because
|
|
25
|
+
* writing through one would change a file this resource never named.
|
|
26
|
+
*/
|
|
27
|
+
export type FileStat = {
|
|
28
|
+
readonly kind: 'file' | 'directory' | 'symlink' | 'other';
|
|
29
|
+
/** Permission bits only (`& 0o7777`), never the file-type bits. */
|
|
30
|
+
readonly mode: number;
|
|
31
|
+
readonly uid: number;
|
|
32
|
+
readonly gid: number;
|
|
33
|
+
readonly size: number;
|
|
34
|
+
};
|
|
35
|
+
export type WriteOptions = {
|
|
36
|
+
/** Permission bits for the final file, applied BEFORE it becomes visible at `path`. */
|
|
37
|
+
readonly mode: number;
|
|
38
|
+
/** Numeric owner; omitted means whoever the runner writes as. */
|
|
39
|
+
readonly uid?: number;
|
|
40
|
+
/** Numeric group; omitted means the directory's default. */
|
|
41
|
+
readonly gid?: number;
|
|
42
|
+
};
|
|
43
|
+
export type HostUser = {
|
|
44
|
+
readonly uid: number;
|
|
45
|
+
readonly gid: number;
|
|
46
|
+
readonly home: string;
|
|
47
|
+
};
|
|
48
|
+
export interface HostRunner {
|
|
49
|
+
/**
|
|
50
|
+
* ⛔ DECLARED, NEVER INFERRED. `true` only when every call this runner makes already runs with
|
|
51
|
+
* root's authority — a runner that talks to a root helper, say. The providers refuse
|
|
52
|
+
* system-domain writes unless `effectiveUid()` is 0 or this is `true`, so claiming it falsely
|
|
53
|
+
* turns that refusal into an EACCES halfway through a deploy.
|
|
54
|
+
* ⛔ THE KIT NEVER ELEVATES. No runner here calls `sudo`: a deploy that prompts for a password
|
|
55
|
+
* mid-plan, or silently uses a cached sudo ticket, is exactly the "no silent sudo" failure. A
|
|
56
|
+
* consumer who wants elevation writes that runner deliberately and sets this flag.
|
|
57
|
+
*/
|
|
58
|
+
readonly privileged: boolean;
|
|
59
|
+
/** The uid file writes and launchctl calls run as. */
|
|
60
|
+
effectiveUid(): number;
|
|
61
|
+
/** Run a program with an argv — ⛔ never through a shell. */
|
|
62
|
+
exec(argv: readonly string[]): Promise<ExecResult>;
|
|
63
|
+
/** File bytes, or `undefined` when nothing is at `path`. */
|
|
64
|
+
readFile(path: string): Promise<Uint8Array | undefined>;
|
|
65
|
+
/** lstat, or `undefined` when nothing is at `path`. */
|
|
66
|
+
stat(path: string): Promise<FileStat | undefined>;
|
|
67
|
+
/**
|
|
68
|
+
* Write `bytes` so that `path` holds either the old file or the new one, never a torn mix:
|
|
69
|
+
* a temporary file in the same directory, mode and owner set on it, then rename(2) over `path`.
|
|
70
|
+
* ⚠️ The parent directory must exist; creating it would invent an owner and mode nobody declared.
|
|
71
|
+
*/
|
|
72
|
+
writeFileAtomic(path: string, bytes: Uint8Array, options: WriteOptions): Promise<void>;
|
|
73
|
+
/** Remove a file. Idempotent: nothing at `path` is success. */
|
|
74
|
+
removeFile(path: string): Promise<void>;
|
|
75
|
+
/** A user by name or numeric id, or `undefined` when the host has none. */
|
|
76
|
+
lookupUser(nameOrId: string): Promise<HostUser | undefined>;
|
|
77
|
+
/** A group's gid by name or numeric id, or `undefined` when the host has none. */
|
|
78
|
+
lookupGroup(nameOrId: string): Promise<number | undefined>;
|
|
79
|
+
/** Wait between launchctl polls. ★ Injected so a test never sleeps for real. */
|
|
80
|
+
sleep(ms: number): Promise<void>;
|
|
81
|
+
}
|
|
82
|
+
declare const HostRunnerService_base: Context.ServiceClass<HostRunnerService, "homeflare/launchd/HostRunner", HostRunner>;
|
|
83
|
+
/** The Effect service every launchd provider reads its runner from. */
|
|
84
|
+
export declare class HostRunnerService extends HostRunnerService_base {
|
|
85
|
+
}
|
|
86
|
+
/** Provide a runner to the providers: `Layer.provide(hostRunnerLayer(localRunner()))`. */
|
|
87
|
+
export declare const hostRunnerLayer: (runner: HostRunner) => Layer.Layer<HostRunnerService>;
|
|
88
|
+
/** True when this runner may write root-owned paths and drive the system domain. */
|
|
89
|
+
export declare const canActAsRoot: (runner: HostRunner) => boolean;
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/launchd/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,sGAAsG;AACtG,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;IAC1D,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,uFAAuF;IACvF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F,MAAM,WAAW,UAAU;IACzB;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,sDAAsD;IACtD,YAAY,IAAI,MAAM,CAAC;IACvB,4DAA4D;IAC5D,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACnD,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IACxD,uDAAuD;IACvD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IAClD;;;;OAIG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF,+DAA+D;IAC/D,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,2EAA2E;IAC3E,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IAC5D,kFAAkF;IAClF,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC3D,gFAAgF;IAChF,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;;AAED,uEAAuE;AACvE,qBAAa,iBAAkB,SAAQ,sBAEtC;CAAG;AAEJ,0FAA0F;AAC1F,eAAO,MAAM,eAAe,WAAY,UAAU,KAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CACxC,CAAC;AAE3C,oFAAoF;AACpF,eAAO,MAAM,YAAY,WAAY,UAAU,KAAG,OACA,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tripwire for the obvious ways a secret ends up in Alchemy state through these providers.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ WHY IT EXISTS. Alchemy persists every prop UNENCRYPTED in its state store — the same reason
|
|
5
|
+
* the OpenBao families are metadata-only (openbao/policy.ts). A LaunchdJob's `environment` and
|
|
6
|
+
* `programArguments`, and a HostFile's `content`, are props. A token declared there is a token
|
|
7
|
+
* copied into the state store, into every plan diff, and into the plist or file on disk.
|
|
8
|
+
* ★ THE ALTERNATIVE, NAMED IN EVERY REFUSAL: let a secret renderer (openbao-agent, or any other)
|
|
9
|
+
* write the secret to a root-owned file, and declare only its PATH — `FOO_TOKEN_FILE=/path`,
|
|
10
|
+
* `--token-file /path`. The job reads the file at start; the state holds a path.
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ A TRIPWIRE, NOT A SCANNER. It catches the names and shapes people actually type. A token in
|
|
13
|
+
* an innocently named variable passes; nothing here can make a secret safe to declare.
|
|
14
|
+
*/
|
|
15
|
+
import type { PlistDict } from './plist.ts';
|
|
16
|
+
/**
|
|
17
|
+
* Refusals for a job's environment, argv and extraKeys. Empty means nothing tripped.
|
|
18
|
+
* ★ extraKeys too: it is a prop like the others, so it lands in state just the same.
|
|
19
|
+
*/
|
|
20
|
+
export declare const jobSecretProblems: (environment: Readonly<Record<string, string>> | undefined, argv: readonly string[], extraKeys?: PlistDict) => string[];
|
|
21
|
+
/** Refusals for a HostFile's content. */
|
|
22
|
+
export declare const fileSecretProblems: (content: string) => string[];
|
|
23
|
+
//# sourceMappingURL=secret-tripwire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-tripwire.d.ts","sourceRoot":"","sources":["../../src/launchd/secret-tripwire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AAgCxD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,gBACf,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,QACnD,SAAS,MAAM,EAAE,cACX,SAAS,KACpB,MAAM,EAsBR,CAAC;AAEF,yCAAyC;AACzC,eAAO,MAAM,kBAAkB,YAAa,MAAM,KAAG,MAAM,EACuB,CAAC"}
|
|
@@ -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"}
|
|
@@ -9,6 +9,13 @@ export interface BaoAuthMethodProps {
|
|
|
9
9
|
defaultLeaseTtl?: string;
|
|
10
10
|
/** Max lease TTL, e.g. `8760h`. */
|
|
11
11
|
maxLeaseTtl?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The path this method lives at NOW, when `path` is where it should move to — `sys/remount`
|
|
14
|
+
* from `auth/<remountFrom>`, keeping every role. Without it a changed `path` fails the plan
|
|
15
|
+
* (mount-move.ts). ⚠️ Every token issued through the method is revoked by the move, and login
|
|
16
|
+
* URLs, policies and MFA enforcements by accessor are not rewritten (the accessor is kept).
|
|
17
|
+
*/
|
|
18
|
+
remountFrom?: string;
|
|
12
19
|
}
|
|
13
20
|
export interface BaoAuthMethodAttributes {
|
|
14
21
|
path: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-method-form.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-method-form.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAoB,CAAC;AAiB1D,eAAO,MAAM,YAAY,UAChB,kBAAkB,QACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,uBAUF,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAiC,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,QAAQ,SAAU,MAAM,WAA6B,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,UAAU,UAAW,kBAAkB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAI3E,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,QAAQ,UAAW,kBAAkB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAMzE,CAAC;AASF,6EAA6E;AAC7E,eAAO,MAAM,OAAO,eAAgB,uBAAuB,SAAS,kBAAkB,YAIlC,CAAC;AAErD,eAAO,MAAM,SAAS,UAAW,kBAAkB,YAGlB,CAAC"}
|
|
1
|
+
{"version":3,"file":"auth-method-form.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-method-form.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAoB,CAAC;AAiB1D,eAAO,MAAM,YAAY,UAChB,kBAAkB,QACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,uBAUF,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAiC,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,QAAQ,SAAU,MAAM,WAA6B,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,UAAU,UAAW,kBAAkB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAI3E,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,QAAQ,UAAW,kBAAkB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAMzE,CAAC;AASF,6EAA6E;AAC7E,eAAO,MAAM,OAAO,eAAgB,uBAAuB,SAAS,kBAAkB,YAIlC,CAAC;AAErD,eAAO,MAAM,SAAS,UAAW,kBAAkB,YAGlB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bao.AuthMethod's read and reconcile as plain effects — split from auth-method.ts so the move runs
|
|
3
|
+
* against a fake server. Same shape as mount-reconcile.ts; the one difference that matters is the
|
|
4
|
+
* `auth/` prefix sys/remount needs (vault/logical_system.go:1295-1310 refuses to move an auth mount
|
|
5
|
+
* to a non-auth path, and `token/` cannot move at all).
|
|
6
|
+
*/
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import type * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
9
|
+
import { type BaoAuthMethodAttributes, type BaoAuthMethodProps } from './auth-method-form.ts';
|
|
10
|
+
import type { BaoError } from './bao-status.ts';
|
|
11
|
+
import type { RemountError, RemountOptions } from './remount-wire.ts';
|
|
12
|
+
export declare const readMethod: (props: BaoAuthMethodProps) => Effect.Effect<BaoAuthMethodAttributes | undefined, BaoError, HttpClient.HttpClient>;
|
|
13
|
+
/** Converge one auth method. `stated` is the path in Alchemy's state, if any. */
|
|
14
|
+
export declare const reconcileAuthMethod: (news: BaoAuthMethodProps, stated: string | undefined, options?: RemountOptions) => Effect.Effect<BaoAuthMethodAttributes, BaoError | RemountError, HttpClient.HttpClient>;
|
|
15
|
+
//# sourceMappingURL=auth-method-reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-method-reconcile.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-method-reconcile.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAKxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEtE,eAAO,MAAM,UAAU,UACd,kBAAkB,KACxB,MAAM,CAAC,MAAM,CAAC,uBAAuB,GAAG,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAC,UAAU,CAKjF,CAAC;AAUL,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,SACxB,kBAAkB,UAChB,MAAM,GAAG,SAAS,YAChB,cAAc,KACvB,MAAM,CAAC,MAAM,CAAC,uBAAuB,EAAE,QAAQ,GAAG,YAAY,EAAE,UAAU,CAAC,UAAU,CA8BpF,CAAC"}
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
* ★ `defaultRemovalPolicy: 'retain'` — disabling a method destroys every role under it
|
|
8
8
|
* and revokes tokens minted through those roles. Opt in with
|
|
9
9
|
* `.pipe(RemovalPolicy.destroy())`.
|
|
10
|
+
*
|
|
11
|
+
* ★ REPLACE SEMANTICS (audited 2026-09-21, see REPLACE.md): `type` changed → plans `replace`, whose
|
|
12
|
+
* create half dies on the still-occupied path before any write, so the apply fails and nothing is
|
|
13
|
+
* disabled (auth-method-reconcile.ts); `path` changed → FAILS unless `remountFrom` names the old
|
|
14
|
+
* path (then an in-place move). It used to plan `update` and enable an empty method at the new
|
|
15
|
+
* path, exactly as Bao.Mount did.
|
|
10
16
|
*/
|
|
11
17
|
import { Resource } from 'alchemy';
|
|
12
18
|
import * as Provider from 'alchemy/Provider';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-method.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-method.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auth-method.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-method.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAExB,MAAM,uBAAuB,CAAC;AAK/B,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,CAAC;AAE5D,MAAM,WAAW,aAAc,SAAQ,QAAQ,CAC7C,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,aAAa,gDAExB,CAAC;AAEH,eAAO,MAAM,qBAAqB,oGAqD/B,CAAC"}
|
|
@@ -33,6 +33,15 @@ export declare const readPath: (name: string) => string;
|
|
|
33
33
|
* ★ ALL STRINGS, EXACTLY THE `k=v` PAIRS `bao write` USED TO SEND — see `baoWrite` in bao-http.ts.
|
|
34
34
|
*/
|
|
35
35
|
export declare const writeBody: (props: BaoAuthRoleProps) => Record<string, string>;
|
|
36
|
+
/**
|
|
37
|
+
* Whether a changed `name` names a DIFFERENT role — the diff's `replace` question.
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ CASE-INSENSITIVELY, BECAUSE THE SERVER IS. AppRole stores `role/<lowercased name>` (openbao
|
|
40
|
+
* v2.6.2 builtin/credential/approle/path_role.go:1485, and roleEntry reads the same key, :1537),
|
|
41
|
+
* so `Host` → `host` is the SAME role: a `replace` there would write it, then — under `destroy` —
|
|
42
|
+
* delete the old generation, which is that same role.
|
|
43
|
+
*/
|
|
44
|
+
export declare const isRename: (from: string, to: string) => boolean;
|
|
36
45
|
/** True when live already matches the declaration on every managed field. */
|
|
37
46
|
export declare const matches: (attributes: BaoAuthRoleAttributes, props: BaoAuthRoleProps) => boolean;
|
|
38
47
|
//# sourceMappingURL=auth-role-form.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-role-form.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-role-form.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAwBD,eAAO,MAAM,YAAY,UAChB,gBAAgB,QACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,qBAWF,CAAC;AAEF,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAgC,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,SAAS,UAAW,gBAAgB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAYxE,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-role-form.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-role-form.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAwBD,eAAO,MAAM,YAAY,UAChB,gBAAgB,QACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,qBAWF,CAAC;AAEF,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAgC,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,SAAS,UAAW,gBAAgB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAYxE,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,SAAU,MAAM,MAAM,MAAM,KAAG,OACX,CAAC;AAW1C,6EAA6E;AAC7E,eAAO,MAAM,OAAO,eAAgB,qBAAqB,SAAS,gBAAgB,YAUjF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-role.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"auth-role.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EAMtB,MAAM,qBAAqB,CAAC;AAG7B,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,CAAC;AAExD,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAEtB,CAAC;AASH,eAAO,MAAM,mBAAmB,kGAiE7B,CAAC"}
|