@intentic/keyring 0.0.0-stage → 1.338.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 (179) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +136 -2
  3. package/dist/address.d.ts +14 -0
  4. package/dist/address.d.ts.map +1 -0
  5. package/dist/address.js +17 -0
  6. package/dist/address.js.map +1 -0
  7. package/dist/app.d.ts +3 -0
  8. package/dist/app.d.ts.map +1 -0
  9. package/dist/app.js +100 -0
  10. package/dist/app.js.map +1 -0
  11. package/dist/askpass/askpass-run.d.ts +7 -0
  12. package/dist/askpass/askpass-run.d.ts.map +1 -0
  13. package/dist/askpass/askpass-run.js +25 -0
  14. package/dist/askpass/askpass-run.js.map +1 -0
  15. package/dist/askpass/askpass.d.ts +9 -0
  16. package/dist/askpass/askpass.d.ts.map +1 -0
  17. package/dist/askpass/askpass.js +70 -0
  18. package/dist/askpass/askpass.js.map +1 -0
  19. package/dist/askpass-cli.d.ts +3 -0
  20. package/dist/askpass-cli.d.ts.map +1 -0
  21. package/dist/askpass-cli.js +4 -0
  22. package/dist/askpass-cli.js.map +1 -0
  23. package/dist/audit.d.ts +18 -0
  24. package/dist/audit.d.ts.map +1 -0
  25. package/dist/audit.js +14 -0
  26. package/dist/audit.js.map +1 -0
  27. package/dist/backends.d.ts +19 -0
  28. package/dist/backends.d.ts.map +1 -0
  29. package/dist/backends.js +97 -0
  30. package/dist/backends.js.map +1 -0
  31. package/dist/cli.d.ts +3 -0
  32. package/dist/cli.d.ts.map +1 -0
  33. package/dist/cli.js +11 -0
  34. package/dist/cli.js.map +1 -0
  35. package/dist/client.d.ts +10 -0
  36. package/dist/client.d.ts.map +1 -0
  37. package/dist/client.js +59 -0
  38. package/dist/client.js.map +1 -0
  39. package/dist/consent/consent-posix.d.ts +6 -0
  40. package/dist/consent/consent-posix.d.ts.map +1 -0
  41. package/dist/consent/consent-posix.js +57 -0
  42. package/dist/consent/consent-posix.js.map +1 -0
  43. package/dist/consent/consent-windows.d.ts +5 -0
  44. package/dist/consent/consent-windows.d.ts.map +1 -0
  45. package/dist/consent/consent-windows.js +244 -0
  46. package/dist/consent/consent-windows.js.map +1 -0
  47. package/dist/consent/consent.d.ts +67 -0
  48. package/dist/consent/consent.d.ts.map +1 -0
  49. package/dist/consent/consent.js +89 -0
  50. package/dist/consent/consent.js.map +1 -0
  51. package/dist/core.d.ts +61 -0
  52. package/dist/core.d.ts.map +1 -0
  53. package/dist/core.js +882 -0
  54. package/dist/core.js.map +1 -0
  55. package/dist/index.d.ts +8 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +8 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/keyring-fetch.d.ts +4 -0
  60. package/dist/keyring-fetch.d.ts.map +1 -0
  61. package/dist/keyring-fetch.js +20 -0
  62. package/dist/keyring-fetch.js.map +1 -0
  63. package/dist/keys.d.ts +27 -0
  64. package/dist/keys.d.ts.map +1 -0
  65. package/dist/keys.js +55 -0
  66. package/dist/keys.js.map +1 -0
  67. package/dist/policy.d.ts +71 -0
  68. package/dist/policy.d.ts.map +1 -0
  69. package/dist/policy.js +170 -0
  70. package/dist/policy.js.map +1 -0
  71. package/dist/powershell.d.ts +22 -0
  72. package/dist/powershell.d.ts.map +1 -0
  73. package/dist/powershell.js +53 -0
  74. package/dist/powershell.js.map +1 -0
  75. package/dist/protocol.d.ts +923 -0
  76. package/dist/protocol.d.ts.map +1 -0
  77. package/dist/protocol.js +125 -0
  78. package/dist/protocol.js.map +1 -0
  79. package/dist/run.d.ts +20 -0
  80. package/dist/run.d.ts.map +1 -0
  81. package/dist/run.js +34 -0
  82. package/dist/run.js.map +1 -0
  83. package/dist/seal.d.ts +3 -0
  84. package/dist/seal.d.ts.map +1 -0
  85. package/dist/seal.js +3 -0
  86. package/dist/seal.js.map +1 -0
  87. package/dist/serve.d.ts +17 -0
  88. package/dist/serve.d.ts.map +1 -0
  89. package/dist/serve.js +65 -0
  90. package/dist/serve.js.map +1 -0
  91. package/dist/server.d.ts +9 -0
  92. package/dist/server.d.ts.map +1 -0
  93. package/dist/server.js +77 -0
  94. package/dist/server.js.map +1 -0
  95. package/dist/service/acl.d.ts +9 -0
  96. package/dist/service/acl.d.ts.map +1 -0
  97. package/dist/service/acl.js +25 -0
  98. package/dist/service/acl.js.map +1 -0
  99. package/dist/service/channel.d.ts +17 -0
  100. package/dist/service/channel.d.ts.map +1 -0
  101. package/dist/service/channel.js +64 -0
  102. package/dist/service/channel.js.map +1 -0
  103. package/dist/service/door.d.ts +89 -0
  104. package/dist/service/door.d.ts.map +1 -0
  105. package/dist/service/door.js +28 -0
  106. package/dist/service/door.js.map +1 -0
  107. package/dist/service/hello-verify.d.ts +2 -0
  108. package/dist/service/hello-verify.d.ts.map +1 -0
  109. package/dist/service/hello-verify.js +10 -0
  110. package/dist/service/hello-verify.js.map +1 -0
  111. package/dist/service/helper.d.ts +3 -0
  112. package/dist/service/helper.d.ts.map +1 -0
  113. package/dist/service/helper.js +117 -0
  114. package/dist/service/helper.js.map +1 -0
  115. package/dist/service/install.d.ts +10 -0
  116. package/dist/service/install.d.ts.map +1 -0
  117. package/dist/service/install.js +77 -0
  118. package/dist/service/install.js.map +1 -0
  119. package/dist/service/lines.d.ts +13 -0
  120. package/dist/service/lines.d.ts.map +1 -0
  121. package/dist/service/lines.js +89 -0
  122. package/dist/service/lines.js.map +1 -0
  123. package/dist/service/paths.d.ts +19 -0
  124. package/dist/service/paths.d.ts.map +1 -0
  125. package/dist/service/paths.js +27 -0
  126. package/dist/service/paths.js.map +1 -0
  127. package/dist/service/remote.d.ts +21 -0
  128. package/dist/service/remote.d.ts.map +1 -0
  129. package/dist/service/remote.js +83 -0
  130. package/dist/service/remote.js.map +1 -0
  131. package/dist/service/service-run.d.ts +3 -0
  132. package/dist/service/service-run.d.ts.map +1 -0
  133. package/dist/service/service-run.js +77 -0
  134. package/dist/service/service-run.js.map +1 -0
  135. package/dist/service/sid.d.ts +3 -0
  136. package/dist/service/sid.d.ts.map +1 -0
  137. package/dist/service/sid.js +10 -0
  138. package/dist/service/sid.js.map +1 -0
  139. package/dist/service/task.d.ts +11 -0
  140. package/dist/service/task.d.ts.map +1 -0
  141. package/dist/service/task.js +38 -0
  142. package/dist/service/task.js.map +1 -0
  143. package/dist/service/wire.d.ts +53 -0
  144. package/dist/service/wire.d.ts.map +1 -0
  145. package/dist/service/wire.js +17 -0
  146. package/dist/service/wire.js.map +1 -0
  147. package/dist/typing/android.d.ts +14 -0
  148. package/dist/typing/android.d.ts.map +1 -0
  149. package/dist/typing/android.js +117 -0
  150. package/dist/typing/android.js.map +1 -0
  151. package/dist/typing/index.d.ts +9 -0
  152. package/dist/typing/index.d.ts.map +1 -0
  153. package/dist/typing/index.js +21 -0
  154. package/dist/typing/index.js.map +1 -0
  155. package/dist/typing/linux.d.ts +6 -0
  156. package/dist/typing/linux.d.ts.map +1 -0
  157. package/dist/typing/linux.js +72 -0
  158. package/dist/typing/linux.js.map +1 -0
  159. package/dist/typing/mac.d.ts +5 -0
  160. package/dist/typing/mac.d.ts.map +1 -0
  161. package/dist/typing/mac.js +40 -0
  162. package/dist/typing/mac.js.map +1 -0
  163. package/dist/typing/typist.d.ts +32 -0
  164. package/dist/typing/typist.d.ts.map +1 -0
  165. package/dist/typing/typist.js +9 -0
  166. package/dist/typing/typist.js.map +1 -0
  167. package/dist/typing/windows.d.ts +4 -0
  168. package/dist/typing/windows.d.ts.map +1 -0
  169. package/dist/typing/windows.js +200 -0
  170. package/dist/typing/windows.js.map +1 -0
  171. package/dist/vault.d.ts +530 -0
  172. package/dist/vault.d.ts.map +1 -0
  173. package/dist/vault.js +188 -0
  174. package/dist/vault.js.map +1 -0
  175. package/dist/version.d.ts +2 -0
  176. package/dist/version.d.ts.map +1 -0
  177. package/dist/version.js +10 -0
  178. package/dist/version.js.map +1 -0
  179. package/package.json +56 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Artur Kurowski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,137 @@
1
- # Temporary Holding Version
1
+ # keyring
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `intentic-keyring`, the password vault on a user's own computer: a process of its own beside
4
+ [intentic-machine](../machine) that types a secret into a window, answers an ssh or sudo prompt, or hands a value to a
5
+ sandbox for one use, without the sandbox's agent ever holding the value.
6
+
7
+ ```mermaid
8
+ flowchart LR
9
+ sb["Sandbox daemon<br/>signs grants"] -->|"keyring* procedures<br/>over the device link"| m["intentic-machine<br/>relays, reads nothing"]
10
+ m -->|"local pipe"| k(["intentic-keyring<br/>vault · consent · typing"])
11
+ k -->|"checks window, types"| app["FortiClient, RDP,<br/>any native app"]
12
+ k -->|"adb"| phone["Android app"]
13
+ ssh["ssh · sudo · git"] --> ap["intentic-askpass"] -->|"local pipe"| k
14
+ k -->|"sealed to the sandbox"| m
15
+ k <-->|"sealed to each other,<br/>through a sandbox"| k2(["keyring on another<br/>of the owner's computers"])
16
+ ```
17
+
18
+ ## Why a process of its own
19
+
20
+ intentic-machine is the agent's hands on the computer: it runs the agent's `run_command`, reads files, types. A vault
21
+ key kept in its memory would be one bug in its scope checks away from the agent, and it updates itself on a timer at a
22
+ sandbox maintainer's word. The keyring does only what a vault does, updates only when a person says so
23
+ (`intentic-machine keyring upgrade` asks the running keyring, which asks the person), and trusts no caller on its pipe
24
+ for who it is. Every caller is a process of the same user, so the pipe is not the boundary; three things are:
25
+
26
+ - **A grant the sandbox signed.** A release or a typing for a sandbox carries a grant (`@intentic/sandbox-contract`
27
+ `schemas/keyring.ts`), Ed25519-signed by that sandbox's own key, which the keyring fetched over TLS from the
28
+ sandbox's address when the entry was shared (`GET /keyring/keys`), never from whoever relayed the share. A grant
29
+ names one secret, one target and one nonce, and lives two minutes. The agent's own commands on this computer can
30
+ reach the pipe but cannot sign one.
31
+ - **A person at this computer.** Anything that changes the vault, takes a value in, hands one to a local program
32
+ (askpass), or answers an `each-use` share asks: Windows Hello where the computer has it, the keyring's passphrase
33
+ otherwise. Every prompt says what is asked for, by whom, and where it goes.
34
+ - **The window it lands in.** Typing checks the focused window's program against the entry's list and, for a password,
35
+ that the focused field is a password field. A value is never typed into a field that would echo it back to a
36
+ screenshot unless the entry says `anyField`.
37
+
38
+ What a caller learns with none of these is names and rules, never a value.
39
+
40
+ ## Share modes
41
+
42
+ Each entry is shared with each sandbox on its own terms:
43
+
44
+ | Mode | The sandbox gets a value when… | Works with the computer off |
45
+ | --- | --- | --- |
46
+ | `each-use` | the person at this computer says yes, each time | no |
47
+ | `while-here` | the keyring is unlocked: one Hello tap or passphrase opens it for up to the minutes chosen | no |
48
+ | `copy` | always: a copy, sealed to that sandbox, kept there as an ordinary secret and refreshed when the entry changes | yes |
49
+
50
+ `copy` is the explicit downgrade: the value then lives in the sandbox and its guards are the sandbox's.
51
+
52
+ ## Files
53
+
54
+ Under `~/.intentic/keyring` (0700, its files 0600):
55
+
56
+ - `vault.json`: the entries, sealed under the vault key (AES-256-GCM), and the vault key wrapped once per way of
57
+ unlocking: by a key derived from the passphrase (scrypt), and on Windows by a key derived from a Windows Hello
58
+ signature over a fixed challenge (KeyCredentialManager signs with an RSA key in the TPM; PKCS#1 v1.5 signatures are
59
+ deterministic, so the same challenge gives the same key, and only a Hello gesture produces one). The inbox and sync
60
+ private keys live inside the sealed part.
61
+ - `keys.json`: what the keyring needs while locked: its `seal` key pair (for values sent to it to type) and each
62
+ sandbox's pinned keys.
63
+ - `audit.jsonl`: one line per decision (who asked, for what, the answer), never a value.
64
+ - `keyring.log`, `keyring.pid`.
65
+
66
+ ## The pipe
67
+
68
+ A named pipe on Windows (`\\.\pipe\intentic-keyring-<user hash>`), a unix socket in the keyring's directory elsewhere.
69
+ One JSON line in, one out (`src/protocol.ts`). A value sent in from this computer (`intentic-machine keyring add`, the
70
+ desktop app) is sealed to the vault's inbox key first, so a process squatting the pipe holds only ciphertext.
71
+
72
+ ## Typing
73
+
74
+ On Windows the keyring inspects the foreground window (its process's executable, and through UI Automation whether the
75
+ focused element is a password field) and types with `SendInput` Unicode events, never `SendKeys`: the value goes to a
76
+ PowerShell helper on its standard input, never on a command line another process could read. It checks the
77
+ foreground window again after typing and says so if it changed. On Linux it types with `xdotool type --file -` or
78
+ `wtype -`, value on stdin; X11 cannot say whether a field is a password field, so a password there needs `anyField` or
79
+ a yes at the computer. On an Android phone attached over adb it checks the foreground app and the focused node's
80
+ `password` flag in a uiautomator dump, then types with `input text` through `adb shell` reading its commands from stdin.
81
+
82
+ ## askpass
83
+
84
+ `intentic-askpass` is the keyring binary under a second name (a hard link beside it). Pointed at by `SSH_ASKPASS`,
85
+ `SUDO_ASKPASS` or `GIT_ASKPASS` (`intentic-machine keyring askpass install` sets them for the user), it sends the
86
+ prompt it was given to the keyring, which finds the entry whose `askpass` matcher fits (`ssh:user@host`,
87
+ `ssh-key:/path`, `sudo`, `git:https://host`, `prompt:<text>`), asks the person, and prints the value for the program
88
+ that asked. It asks every time unless the entry sets `askpassRememberMinutes`; a remembered answer is one any program
89
+ of this user could collect in that window, the agent's own commands included, and `keyring list` says so beside it.
90
+
91
+ ## Password managers
92
+
93
+ An entry may name where its value is read at each use instead of holding it: 1Password (`op read op://…`, unlocked
94
+ by the 1Password app's own prompt, or a service-account token kept as another entry), Bitwarden (`bw get`, unlocked
95
+ with a master password kept as another entry) or KeePassXC (`keepassxc-cli show`, its database password kept as
96
+ another entry). Secrets reach those programs on standard input or in their environment, never on a command line.
97
+
98
+ ## Two computers, one keyring
99
+
100
+ Two keyrings are paired once (`intentic-machine keyring sync pair`, or the editor's Pair), at a person's yes on each
101
+ computer, each prompt showing both sync-key fingerprints to compare. After that each change is sealed to the other's
102
+ sync key and left in a sandbox both are linked to, which carries ciphertext only; the other takes it in at its next
103
+ unlock. The newer revision of an entry wins.
104
+
105
+ ## Hardened mode (Windows)
106
+
107
+ `intentic-machine keyring harden` (one UAC prompt) moves the vault out of the user's reach:
108
+
109
+ - The vault, its keys and its audit move to `%ProgramData%\Intentic\Keyring\<user SID>`, whose access list names only
110
+ `LOCAL SERVICE`, SYSTEM and an elevated administrator. No process of the user, the agent's `run_command` included,
111
+ can read the file or the memory of the process that holds it.
112
+ - That process is the keyring itself, run by a startup task (`\Intentic\Keyring-<SID>`) as `LOCAL SERVICE` from its
113
+ own copy in `%ProgramData%\Intentic\Keyring\bin`, which only an administrator can replace. A task and not a Windows
114
+ service because a compiled script cannot answer the service manager's start handshake (`src/service/task.ts`).
115
+ - A pipe the service created would refuse the user's writes, so it listens on loopback and writes its port and a random
116
+ token to a door file only that user can read (`src/service/door.ts`). The token keeps other users out; it does not keep
117
+ out the user's own programs, which is why every value still needs a sandbox's grant or a person's yes.
118
+ - The session helper, `intentic-keyring session`, starts at sign-in in place of `serve`. It answers the user's usual pipe
119
+ by carrying each request through the door, so intentic-machine and intentic-askpass work unchanged, and it is the
120
+ service's hands: it shows the prompts and does the typing. It proves itself once per sign-in with a Windows Hello
121
+ signature over the service's challenge (or the passphrase), and every yes it relays afterwards is a Hello signature
122
+ over a fresh challenge, checked by the service against the Hello key pinned at setup (`src/service/hello-verify.ts`),
123
+ so a helper the agent started cannot say yes for the person.
124
+
125
+ What it does not cover: a value being typed passes through the helper, a process of the user, for that moment. Every
126
+ other value, the vault key and the vault itself stay with the service.
127
+
128
+ ## Limits
129
+
130
+ - Without hardened mode, a process of the same user can read the vault key from the keyring's memory while it is
131
+ unlocked, and could trick a person into a Hello tap meant for the keyring; `while-here` is for a computer you are at.
132
+ - Windows Hello's own prompt says only "Making sure it's you": the topmost notice beside it is what names the request.
133
+ A program that raises a Hello prompt of its own gets the same dialog without the notice.
134
+ - A Windows Hello prompt raised by a process with no window can open behind other windows; the keyring shows a
135
+ topmost notice naming what is asked while it waits.
136
+ - Typing cannot be taken back: if focus moves between the check and the last character, the keyring stops and says
137
+ where the rest went.
@@ -0,0 +1,14 @@
1
+ export declare const KEYRING_HOME = "keyring";
2
+ export interface KeyringPaths {
3
+ readonly dir: string;
4
+ readonly vault: string;
5
+ readonly keys: string;
6
+ readonly audit: string;
7
+ readonly log: string;
8
+ readonly pid: string;
9
+ readonly address: string;
10
+ }
11
+ export declare const userTag: () => string;
12
+ export declare const keyringAddress: (dir: string, platform?: NodeJS.Platform) => string;
13
+ export declare const keyringPaths: (dir?: string, platform?: NodeJS.Platform) => KeyringPaths;
14
+ //# sourceMappingURL=address.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,YAAY,YAAY,CAAC;AAEtC,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC5B;AAGD,eAAO,MAAM,OAAO,QAAO,MAAmG,CAAC;AAE/H,eAAO,MAAM,cAAc,QAAS,MAAM,aAAY,MAAM,CAAC,QAAQ,KAAsB,MACQ,CAAC;AAEpG,eAAO,MAAM,YAAY,SAAS,MAAM,aAA0C,MAAM,CAAC,QAAQ,KAAsB,YAQrH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { createHash } from "node:crypto";
2
+ import { userInfo } from "node:os";
3
+ import { join } from "node:path";
4
+ import { agentHome } from "@intentic/local-agent";
5
+ export const KEYRING_HOME = "keyring";
6
+ export const userTag = () => createHash("sha256").update(userInfo().username.toLowerCase()).digest("hex").slice(0, 12);
7
+ export const keyringAddress = (dir, platform = process.platform) => platform === "win32" ? `\\\\.\\pipe\\intentic-keyring-${userTag()}` : join(dir, "keyring.sock");
8
+ export const keyringPaths = (dir = agentHome(KEYRING_HOME).dir, platform = process.platform) => ({
9
+ dir,
10
+ vault: join(dir, "vault.json"),
11
+ keys: join(dir, "keys.json"),
12
+ audit: join(dir, "audit.jsonl"),
13
+ log: join(dir, "keyring.log"),
14
+ pid: join(dir, "keyring.pid"),
15
+ address: keyringAddress(dir, platform),
16
+ });
17
+ //# sourceMappingURL=address.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"address.js","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAMlD,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC;AAiBtC,MAAM,CAAC,MAAM,OAAO,GAAG,GAAW,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAE/H,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAW,EAAE,QAAQ,GAAoB,OAAO,CAAC,QAAQ,EAAU,EAAE,CAChG,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,iCAAiC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;AAEpG,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAG,GAAW,SAAS,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,QAAQ,GAAoB,OAAO,CAAC,QAAQ,EAAgB,EAAE,CAAC,CAAC;IACpI,GAAG;IACH,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC;IAC9B,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC;IAC5B,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC;IAC/B,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC;IAC7B,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC;IAC7B,OAAO,EAAE,cAAc,CAAC,GAAG,EAAE,QAAQ,CAAC;CACzC,CAAC,CAAC"}
package/dist/app.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { CommandContext } from "@stricli/core";
2
+ export declare const app: import("@stricli/core").Application<CommandContext>;
3
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA2GpD,eAAO,MAAM,GAAG,qDAKd,CAAC"}
package/dist/app.js ADDED
@@ -0,0 +1,100 @@
1
+ import { agentException } from "@intentic/local-agent";
2
+ import { buildApplication, buildCommand, buildRouteMap, text_en } from "@stricli/core";
3
+ import { askpass } from "./askpass/askpass-run.js";
4
+ import { serve } from "./serve.js";
5
+ import { runSession } from "./service/helper.js";
6
+ import { installService, uninstallService } from "./service/install.js";
7
+ import { SID_RE } from "./service/paths.js";
8
+ import { runService } from "./service/service-run.js";
9
+ import { realRunner } from "./run.js";
10
+ import { KEYRING_VERSION } from "./version.js";
11
+ const serveCommand = buildCommand({
12
+ docs: { brief: "Run the keyring (started at sign-in; there is one per user)" },
13
+ parameters: {},
14
+ async func() {
15
+ const served = await serve();
16
+ if (!served.ok) {
17
+ this.process.stderr.write(`${served.reason}\n`);
18
+ process.exitCode = 1;
19
+ return;
20
+ }
21
+ const stop = () => {
22
+ void served.stop().then(() => process.exit(0));
23
+ };
24
+ process.once("SIGINT", stop);
25
+ process.once("SIGTERM", stop);
26
+ await new Promise(() => undefined);
27
+ },
28
+ });
29
+ const versionCommand = buildCommand({
30
+ docs: { brief: "Print this keyring's version" },
31
+ parameters: {},
32
+ func() {
33
+ this.process.stdout.write(`${KEYRING_VERSION}\n`);
34
+ },
35
+ });
36
+ const askpassCommand = buildCommand({
37
+ docs: { brief: "Answer an ssh, sudo or git password prompt from the keyring (what intentic-askpass runs)" },
38
+ parameters: { flags: {}, positional: { kind: "array", parameter: { brief: "The prompt the program showed", parse: String } } },
39
+ async func(_flags, ...prompt) {
40
+ process.exitCode = await askpass(prompt.join(" "), this.process.stdout, this.process.stderr);
41
+ },
42
+ });
43
+ const windowsOnly = () => {
44
+ if (process.platform !== "win32") {
45
+ process.stderr.write("Hardened mode is Windows-only.\n");
46
+ process.exitCode = 1;
47
+ return false;
48
+ }
49
+ return true;
50
+ };
51
+ const installCommand = buildCommand({
52
+ docs: { brief: "Move the vault under the hardened service (run elevated; intentic-machine keyring harden does this)" },
53
+ parameters: {},
54
+ async func() {
55
+ if (windowsOnly()) {
56
+ const done = await installService(realRunner);
57
+ this.process.stdout.write(`${JSON.stringify({ ok: true, ...done })}\n`);
58
+ }
59
+ },
60
+ });
61
+ const uninstallCommand = buildCommand({
62
+ docs: { brief: "Put the vault back in your own folder and remove the hardened service (run elevated)" },
63
+ parameters: {},
64
+ async func() {
65
+ if (windowsOnly()) {
66
+ const done = await uninstallService(realRunner);
67
+ this.process.stdout.write(`${JSON.stringify({ ok: true, ...done })}\n`);
68
+ }
69
+ },
70
+ });
71
+ const runCommand = buildCommand({
72
+ docs: { brief: "Run the hardened service (what its startup task starts)" },
73
+ parameters: { flags: { user: { kind: "parsed", parse: String, brief: "The user's security identifier" } } },
74
+ async func(flags) {
75
+ if (!SID_RE.test(flags.user)) {
76
+ this.process.stderr.write("--user takes a security identifier (S-1-5-…).\n");
77
+ process.exitCode = 1;
78
+ return;
79
+ }
80
+ if (windowsOnly()) {
81
+ await runService(flags.user);
82
+ }
83
+ },
84
+ });
85
+ const sessionCommand = buildCommand({
86
+ docs: { brief: "Run the session helper of a hardened keyring (started at sign-in)" },
87
+ parameters: {},
88
+ async func() {
89
+ if (windowsOnly()) {
90
+ await runSession();
91
+ }
92
+ },
93
+ });
94
+ export const app = buildApplication(buildRouteMap({ routes: { serve: serveCommand, version: versionCommand, askpass: askpassCommand, service: buildRouteMap({ routes: { install: installCommand, uninstall: uninstallCommand, run: runCommand }, docs: { brief: "Hardened mode (Windows)" } }), session: sessionCommand }, docs: { brief: "The Intentic keyring" } }), {
95
+ name: "intentic-keyring",
96
+ versionInfo: { currentVersion: KEYRING_VERSION },
97
+ scanner: { caseStyle: "allow-kebab-for-camel" },
98
+ localization: { text: { ...text_en, formatException: agentException, exceptionWhileRunningCommand: (exc) => `error: ${agentException(exc)}` } },
99
+ });
100
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ interface Writable {
2
+ readonly write: (text: string) => void;
3
+ }
4
+ export declare const askpass: (prompt: string, stdout: Writable, stderr: Writable) => Promise<number>;
5
+ export declare const startedAsAskpass: (argv0?: string, argv1?: string | undefined) => boolean;
6
+ export {};
7
+ //# sourceMappingURL=askpass-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"askpass-run.d.ts","sourceRoot":"","sources":["../../src/askpass/askpass-run.ts"],"names":[],"mappings":"AASA,UAAU,QAAQ;IACd,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAQD,eAAO,MAAM,OAAO,WAAkB,MAAM,UAAU,QAAQ,UAAU,QAAQ,KAAG,OAAO,CAAC,MAAM,CAchG,CAAC;AAIF,eAAO,MAAM,gBAAgB,WAAW,MAAM,UAA4B,MAAM,GAAG,SAAS,KAAqB,OACK,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { keyringPaths } from "../address.js";
2
+ import { askKeyring, KeyringUnavailable } from "../client.js";
3
+ import { basenameOf } from "../policy.js";
4
+ const parentName = () => {
5
+ const name = process.env["SUDO_COMMAND"] === undefined ? undefined : "sudo";
6
+ return name ?? (process.env["GIT_ASKPASS"] !== undefined && process.env["GIT_EXEC_PATH"] !== undefined ? "git" : process.env["SSH_ASKPASS"] !== undefined ? "ssh" : undefined);
7
+ };
8
+ export const askpass = async (prompt, stdout, stderr) => {
9
+ try {
10
+ const parent = parentName();
11
+ const answer = await askKeyring({ address: keyringPaths().address }, parent === undefined ? { op: "askpass", prompt } : { op: "askpass", prompt, parent });
12
+ if (answer.ok) {
13
+ stdout.write(`${answer.value}\n`);
14
+ return 0;
15
+ }
16
+ stderr.write(`intentic-askpass: ${answer.refusal}\n`);
17
+ return 1;
18
+ }
19
+ catch (error) {
20
+ stderr.write(`intentic-askpass: ${error instanceof KeyringUnavailable ? error.message : "the keyring could not be asked"}\n`);
21
+ return 1;
22
+ }
23
+ };
24
+ export const startedAsAskpass = (argv0 = process.execPath, argv1 = process.argv[1]) => [argv0, argv1].some((path) => path !== undefined && basenameOf(path).toLowerCase().startsWith("intentic-askpass"));
25
+ //# sourceMappingURL=askpass-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"askpass-run.js","sourceRoot":"","sources":["../../src/askpass/askpass-run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAY1C,MAAM,UAAU,GAAG,GAAuB,EAAE;IACxC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;IAC5E,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACnL,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,EAAE,MAAc,EAAE,MAAgB,EAAE,MAAgB,EAAmB,EAAE;IACjG,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3J,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;YAClC,OAAO,CAAC,CAAC;QACb,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,qBAAqB,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;QACtD,OAAO,CAAC,CAAC;IACb,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,qBAAqB,KAAK,YAAY,kBAAkB,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,gCAAgC,IAAI,CAAC,CAAC;QAC9H,OAAO,CAAC,CAAC;IACb,CAAC;AACL,CAAC,CAAC;AAIF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAK,GAAW,OAAO,CAAC,QAAQ,EAAE,KAAK,GAAuB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAW,EAAE,CACvH,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface AskpassQuestion {
2
+ readonly wants: "secret" | "username";
3
+ readonly matchers: readonly string[];
4
+ readonly prompt: string;
5
+ }
6
+ export declare const parseAskpassPrompt: (prompt: string) => AskpassQuestion;
7
+ export declare const normalMatcher: (matcher: string) => string;
8
+ export declare const askpassMatch: (question: AskpassQuestion, matchers: readonly string[]) => string | undefined;
9
+ //# sourceMappingURL=askpass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"askpass.d.ts","sourceRoot":"","sources":["../../src/askpass/askpass.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAE5B,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,CAAC;IAEtC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAErC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CAC3B;AAwBD,eAAO,MAAM,kBAAkB,WAAY,MAAM,KAAG,eA0BnD,CAAC;AAIF,eAAO,MAAM,aAAa,YAAa,MAAM,KAAG,MAc/C,CAAC;AAGF,eAAO,MAAM,YAAY,aAAc,eAAe,YAAY,SAAS,MAAM,EAAE,KAAG,MAAM,GAAG,SAS9F,CAAC"}
@@ -0,0 +1,70 @@
1
+ const SSH_PASSWORD = /^(?<user>[^\s@']+)@(?<host>[^\s']+)'s password:\s*$/u;
2
+ const SSH_KEYBOARD = /^\((?<user>[^\s@()]+)@(?<host>[^\s()]+)\) Password:\s*$/iu;
3
+ const SSH_KEY = /^Enter passphrase for key '(?<path>[^']+)':\s*$/u;
4
+ const SUDO = /^\[sudo\] password for (?<user>[^\s:]+):\s*$/u;
5
+ const GIT_PASSWORD = /^Password for '(?<url>[^']+)':\s*$/u;
6
+ const GIT_USERNAME = /^Username for '(?<url>[^']+)':\s*$/u;
7
+ const gitSite = (url) => {
8
+ let parsed;
9
+ try {
10
+ parsed = new URL(url);
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ return `${parsed.protocol}//${parsed.host.toLowerCase()}`;
16
+ };
17
+ const lineOf = (prompt) => prompt.replace(/\s+/gu, " ").trim();
18
+ export const parseAskpassPrompt = (prompt) => {
19
+ const text = prompt.trim();
20
+ const shown = lineOf(prompt);
21
+ const ssh = SSH_PASSWORD.exec(text) ?? SSH_KEYBOARD.exec(text);
22
+ if (ssh?.groups?.["user"] !== undefined && ssh.groups["host"] !== undefined) {
23
+ return { wants: "secret", matchers: [`ssh:${ssh.groups["user"]}@${ssh.groups["host"].toLowerCase()}`], prompt: shown };
24
+ }
25
+ const key = SSH_KEY.exec(text)?.groups?.["path"];
26
+ if (key !== undefined) {
27
+ return { wants: "secret", matchers: [`ssh-key:${key}`], prompt: shown };
28
+ }
29
+ const sudo = SUDO.exec(text)?.groups?.["user"];
30
+ if (sudo !== undefined) {
31
+ return { wants: "secret", matchers: [`sudo:${sudo}`, "sudo"], prompt: shown };
32
+ }
33
+ for (const [pattern, wants] of [
34
+ [GIT_PASSWORD, "secret"],
35
+ [GIT_USERNAME, "username"],
36
+ ]) {
37
+ const url = pattern.exec(text)?.groups?.["url"];
38
+ const site = url === undefined ? undefined : gitSite(url);
39
+ if (site !== undefined) {
40
+ return { wants, matchers: [`git:${site}`], prompt: shown };
41
+ }
42
+ }
43
+ return { wants: "secret", matchers: [`prompt:${shown}`], prompt: shown };
44
+ };
45
+ export const normalMatcher = (matcher) => {
46
+ const colon = matcher.indexOf(":");
47
+ const kind = colon === -1 ? matcher : matcher.slice(0, colon);
48
+ const rest = colon === -1 ? "" : matcher.slice(colon + 1);
49
+ switch (kind) {
50
+ case "ssh":
51
+ return `ssh:${rest.replace(/@(.*)$/u, (_match, host) => `@${host.toLowerCase()}`)}`;
52
+ case "git":
53
+ return `git:${gitSite(rest) ?? rest.toLowerCase()}`;
54
+ case "prompt":
55
+ return `prompt:${lineOf(rest).replace(/[:?]\s*$/u, "").toLowerCase()}`;
56
+ default:
57
+ return matcher;
58
+ }
59
+ };
60
+ export const askpassMatch = (question, matchers) => {
61
+ const own = new Map(matchers.map((matcher) => [normalMatcher(matcher), matcher]));
62
+ for (const wanted of question.matchers) {
63
+ const found = own.get(normalMatcher(wanted));
64
+ if (found !== undefined) {
65
+ return found;
66
+ }
67
+ }
68
+ return undefined;
69
+ };
70
+ //# sourceMappingURL=askpass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"askpass.js","sourceRoot":"","sources":["../../src/askpass/askpass.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=askpass-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"askpass-cli.d.ts","sourceRoot":"","sources":["../src/askpass-cli.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { askpass } from "./askpass/askpass-run.js";
3
+ process.exitCode = await askpass(process.argv.slice(2).join(" "), process.stdout, process.stderr);
4
+ //# sourceMappingURL=askpass-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"askpass-cli.js","sourceRoot":"","sources":["../src/askpass-cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAGnD,OAAO,CAAC,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface AuditRecord {
2
+ readonly op: string;
3
+ readonly outcome: "granted" | "refused" | "done" | "pending";
4
+ readonly code?: string | undefined;
5
+ readonly secret?: string | undefined;
6
+ readonly sandbox?: string | undefined;
7
+ readonly target?: string | undefined;
8
+ readonly program?: string | undefined;
9
+ readonly asked?: boolean | undefined;
10
+ readonly how?: string | undefined;
11
+ readonly detail?: string | undefined;
12
+ readonly conversation?: string | undefined;
13
+ readonly reason?: string | undefined;
14
+ }
15
+ export type Audit = (record: AuditRecord) => Promise<void>;
16
+ export declare const fileAudit: (path: string, dir: string, now?: () => number) => Audit;
17
+ export declare const noAudit: Audit;
18
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,MAAM,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE3D,eAAO,MAAM,SAAS,SAAU,MAAM,OAAO,MAAM,QAAO,MAAM,MAAM,KAAc,KAUnF,CAAC;AAGF,eAAO,MAAM,OAAO,EAAE,KAA+B,CAAC"}
package/dist/audit.js ADDED
@@ -0,0 +1,14 @@
1
+ import { appendFile, chmod, mkdir } from "node:fs/promises";
2
+ import { serialLock } from "@intentic/base/async";
3
+ import { rotateIfLarge } from "@intentic/local-agent";
4
+ export const fileAudit = (path, dir, now = Date.now) => {
5
+ const queue = serialLock();
6
+ return async (record) => await queue(async () => {
7
+ await mkdir(dir, { recursive: true, mode: 0o700 });
8
+ await rotateIfLarge(path);
9
+ await appendFile(path, `${JSON.stringify({ at: new Date(now()).toISOString(), ...record })}\n`, { mode: 0o600 });
10
+ await chmod(path, 0o600);
11
+ });
12
+ };
13
+ export const noAudit = () => Promise.resolve();
14
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AA0BtD,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,IAAY,EAAE,GAAW,EAAE,GAAG,GAAiB,IAAI,CAAC,GAAG,EAAS,EAAE;IACxF,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;IAC3B,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE,CACpB,MAAM,KAAK,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACnD,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,UAAU,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAEjH,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACX,CAAC,CAAC;AAGF,MAAM,CAAC,MAAM,OAAO,GAAU,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { BackendConfig, BackendRef } from "./protocol.js";
2
+ import { type Runner } from "./run.js";
3
+ export type BackendRead = {
4
+ readonly ok: true;
5
+ readonly value: string;
6
+ } | {
7
+ readonly ok: false;
8
+ readonly code: "unavailable";
9
+ readonly reason: string;
10
+ };
11
+ export type SecretOf = (name: string) => Promise<string | undefined>;
12
+ export declare class BackendReader {
13
+ #private;
14
+ private readonly runner;
15
+ constructor(runner: Runner);
16
+ forget(): void;
17
+ read(ref: BackendRef, configs: readonly BackendConfig[], secretOf: SecretOf): Promise<BackendRead>;
18
+ }
19
+ //# sourceMappingURL=backends.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backends.d.ts","sourceRoot":"","sources":["../src/backends.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAqC,KAAK,MAAM,EAA8B,MAAM,UAAU,CAAC;AAUtG,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAGxJ,MAAM,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AAkBrE,qBAAa,aAAa;;IAIV,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAnC,YAA6B,MAAM,EAAE,MAAM,EAAI;IAG/C,MAAM,IAAI,IAAI,CAEb;IAEK,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,aAAa,EAAE,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,CAiBvG;CA+DJ"}