@edspencer/paddock 0.0.1 → 0.56.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 (294) hide show
  1. package/README.md +69 -0
  2. package/install-notice.mjs +29 -0
  3. package/package.json +58 -5
  4. package/packages/server/dist/adoptable.d.ts +127 -0
  5. package/packages/server/dist/adoptable.js +315 -0
  6. package/packages/server/dist/app.d.ts +67 -0
  7. package/packages/server/dist/app.js +325 -0
  8. package/packages/server/dist/archive.d.ts +42 -0
  9. package/packages/server/dist/archive.js +125 -0
  10. package/packages/server/dist/attachments-config.d.ts +114 -0
  11. package/packages/server/dist/attachments-config.js +165 -0
  12. package/packages/server/dist/attachments-hint.d.ts +53 -0
  13. package/packages/server/dist/attachments-hint.js +140 -0
  14. package/packages/server/dist/attachments.d.ts +33 -0
  15. package/packages/server/dist/attachments.js +117 -0
  16. package/packages/server/dist/auth.d.ts +84 -0
  17. package/packages/server/dist/auth.js +247 -0
  18. package/packages/server/dist/background.d.ts +36 -0
  19. package/packages/server/dist/background.js +137 -0
  20. package/packages/server/dist/bind-safety.d.ts +56 -0
  21. package/packages/server/dist/bind-safety.js +51 -0
  22. package/packages/server/dist/brand.d.ts +44 -0
  23. package/packages/server/dist/brand.js +88 -0
  24. package/packages/server/dist/chat-dto.d.ts +210 -0
  25. package/packages/server/dist/chat-dto.js +308 -0
  26. package/packages/server/dist/cli/import-chats.d.ts +21 -0
  27. package/packages/server/dist/cli/import-chats.js +197 -0
  28. package/packages/server/dist/cli/paddock.d.ts +2 -0
  29. package/packages/server/dist/cli/paddock.js +247 -0
  30. package/packages/server/dist/concurrency.d.ts +57 -0
  31. package/packages/server/dist/concurrency.js +71 -0
  32. package/packages/server/dist/config.d.ts +512 -0
  33. package/packages/server/dist/config.js +593 -0
  34. package/packages/server/dist/curation-config.d.ts +55 -0
  35. package/packages/server/dist/curation-config.js +64 -0
  36. package/packages/server/dist/event-bus.d.ts +87 -0
  37. package/packages/server/dist/event-bus.js +48 -0
  38. package/packages/server/dist/git.d.ts +128 -0
  39. package/packages/server/dist/git.js +446 -0
  40. package/packages/server/dist/github-auth.d.ts +44 -0
  41. package/packages/server/dist/github-auth.js +169 -0
  42. package/packages/server/dist/herdctl-agent-config.d.ts +98 -0
  43. package/packages/server/dist/herdctl-agent-config.js +331 -0
  44. package/packages/server/dist/herdctl-agent-names.d.ts +172 -0
  45. package/packages/server/dist/herdctl-agent-names.js +228 -0
  46. package/packages/server/dist/herdctl-jobs-index.d.ts +65 -0
  47. package/packages/server/dist/herdctl-jobs-index.js +225 -0
  48. package/packages/server/dist/herdctl-jobs.d.ts +98 -0
  49. package/packages/server/dist/herdctl-jobs.js +225 -0
  50. package/packages/server/dist/herdctl.d.ts +572 -0
  51. package/packages/server/dist/herdctl.js +1218 -0
  52. package/packages/server/dist/hook-config.d.ts +217 -0
  53. package/packages/server/dist/hook-config.js +224 -0
  54. package/packages/server/dist/http-bytes.d.ts +24 -0
  55. package/packages/server/dist/http-bytes.js +52 -0
  56. package/packages/server/dist/index.d.ts +1 -0
  57. package/packages/server/dist/index.js +14 -0
  58. package/packages/server/dist/instance-config.d.ts +135 -0
  59. package/packages/server/dist/instance-config.js +345 -0
  60. package/packages/server/dist/localcommand.d.ts +23 -0
  61. package/packages/server/dist/localcommand.js +140 -0
  62. package/packages/server/dist/management-auth.d.ts +99 -0
  63. package/packages/server/dist/management-auth.js +165 -0
  64. package/packages/server/dist/management-config.d.ts +164 -0
  65. package/packages/server/dist/management-config.js +286 -0
  66. package/packages/server/dist/management-mcp-server.d.ts +20 -0
  67. package/packages/server/dist/management-mcp-server.js +95 -0
  68. package/packages/server/dist/management-metadata.d.ts +67 -0
  69. package/packages/server/dist/management-metadata.js +56 -0
  70. package/packages/server/dist/management-ops.d.ts +110 -0
  71. package/packages/server/dist/management-ops.js +449 -0
  72. package/packages/server/dist/management-policy.d.ts +172 -0
  73. package/packages/server/dist/management-policy.js +259 -0
  74. package/packages/server/dist/message-provenance.d.ts +112 -0
  75. package/packages/server/dist/message-provenance.js +260 -0
  76. package/packages/server/dist/models.d.ts +164 -0
  77. package/packages/server/dist/models.js +208 -0
  78. package/packages/server/dist/openapi.d.ts +66 -0
  79. package/packages/server/dist/openapi.js +183 -0
  80. package/packages/server/dist/parent-detach.d.ts +30 -0
  81. package/packages/server/dist/parent-detach.js +109 -0
  82. package/packages/server/dist/preload.d.ts +43 -0
  83. package/packages/server/dist/preload.js +55 -0
  84. package/packages/server/dist/project-files.d.ts +76 -0
  85. package/packages/server/dist/project-files.js +191 -0
  86. package/packages/server/dist/project-mime.d.ts +35 -0
  87. package/packages/server/dist/project-mime.js +55 -0
  88. package/packages/server/dist/project-paths.d.ts +88 -0
  89. package/packages/server/dist/project-paths.js +126 -0
  90. package/packages/server/dist/project-types.d.ts +316 -0
  91. package/packages/server/dist/project-types.js +48 -0
  92. package/packages/server/dist/projects.d.ts +202 -0
  93. package/packages/server/dist/projects.js +894 -0
  94. package/packages/server/dist/queued-message.d.ts +35 -0
  95. package/packages/server/dist/queued-message.js +116 -0
  96. package/packages/server/dist/read-state.d.ts +46 -0
  97. package/packages/server/dist/read-state.js +139 -0
  98. package/packages/server/dist/recovery-config.d.ts +111 -0
  99. package/packages/server/dist/recovery-config.js +105 -0
  100. package/packages/server/dist/recovery.d.ts +262 -0
  101. package/packages/server/dist/recovery.js +490 -0
  102. package/packages/server/dist/resume-drain.d.ts +122 -0
  103. package/packages/server/dist/resume-drain.js +158 -0
  104. package/packages/server/dist/route-context.d.ts +168 -0
  105. package/packages/server/dist/route-context.js +39 -0
  106. package/packages/server/dist/route-errors.d.ts +8 -0
  107. package/packages/server/dist/route-errors.js +13 -0
  108. package/packages/server/dist/routes/chats.d.ts +12 -0
  109. package/packages/server/dist/routes/chats.js +1406 -0
  110. package/packages/server/dist/routes/git.d.ts +13 -0
  111. package/packages/server/dist/routes/git.js +236 -0
  112. package/packages/server/dist/routes/mcp.d.ts +83 -0
  113. package/packages/server/dist/routes/mcp.js +250 -0
  114. package/packages/server/dist/routes/meta.d.ts +15 -0
  115. package/packages/server/dist/routes/meta.js +447 -0
  116. package/packages/server/dist/routes/projects.d.ts +25 -0
  117. package/packages/server/dist/routes/projects.js +736 -0
  118. package/packages/server/dist/routes/triggers.d.ts +21 -0
  119. package/packages/server/dist/routes/triggers.js +324 -0
  120. package/packages/server/dist/routes/workspace-mount.d.ts +48 -0
  121. package/packages/server/dist/routes/workspace-mount.js +32 -0
  122. package/packages/server/dist/routes.d.ts +26 -0
  123. package/packages/server/dist/routes.js +29 -0
  124. package/packages/server/dist/run-provenance.d.ts +109 -0
  125. package/packages/server/dist/run-provenance.js +206 -0
  126. package/packages/server/dist/runs.d.ts +92 -0
  127. package/packages/server/dist/runs.js +72 -0
  128. package/packages/server/dist/schedule-config.d.ts +68 -0
  129. package/packages/server/dist/schedule-config.js +75 -0
  130. package/packages/server/dist/schedule-session.d.ts +34 -0
  131. package/packages/server/dist/schedule-session.js +120 -0
  132. package/packages/server/dist/self-mcp-descriptions.d.ts +20 -0
  133. package/packages/server/dist/self-mcp-descriptions.js +145 -0
  134. package/packages/server/dist/self-mcp-read.d.ts +15 -0
  135. package/packages/server/dist/self-mcp-read.js +97 -0
  136. package/packages/server/dist/self-mcp-triggers.d.ts +30 -0
  137. package/packages/server/dist/self-mcp-triggers.js +144 -0
  138. package/packages/server/dist/self-mcp-types.d.ts +248 -0
  139. package/packages/server/dist/self-mcp-types.js +10 -0
  140. package/packages/server/dist/self-mcp-util.d.ts +73 -0
  141. package/packages/server/dist/self-mcp-util.js +157 -0
  142. package/packages/server/dist/self-mcp-write.d.ts +35 -0
  143. package/packages/server/dist/self-mcp-write.js +215 -0
  144. package/packages/server/dist/self-mcp.d.ts +95 -0
  145. package/packages/server/dist/self-mcp.js +448 -0
  146. package/packages/server/dist/send-file-mcp.d.ts +46 -0
  147. package/packages/server/dist/send-file-mcp.js +281 -0
  148. package/packages/server/dist/session-hub.d.ts +186 -0
  149. package/packages/server/dist/session-hub.js +291 -0
  150. package/packages/server/dist/spawn-capability.d.ts +99 -0
  151. package/packages/server/dist/spawn-capability.js +106 -0
  152. package/packages/server/dist/star.d.ts +26 -0
  153. package/packages/server/dist/star.js +94 -0
  154. package/packages/server/dist/start.d.ts +1 -0
  155. package/packages/server/dist/start.js +21 -0
  156. package/packages/server/dist/subagents.d.ts +337 -0
  157. package/packages/server/dist/subagents.js +626 -0
  158. package/packages/server/dist/sweep.d.ts +122 -0
  159. package/packages/server/dist/sweep.js +696 -0
  160. package/packages/server/dist/tooldetails.d.ts +33 -0
  161. package/packages/server/dist/tooldetails.js +466 -0
  162. package/packages/server/dist/transcribe.d.ts +69 -0
  163. package/packages/server/dist/transcribe.js +119 -0
  164. package/packages/server/dist/transcripts.d.ts +42 -0
  165. package/packages/server/dist/transcripts.js +164 -0
  166. package/packages/server/dist/trigger-config.d.ts +278 -0
  167. package/packages/server/dist/trigger-config.js +417 -0
  168. package/packages/server/dist/trigger-runtime.d.ts +94 -0
  169. package/packages/server/dist/trigger-runtime.js +82 -0
  170. package/packages/server/dist/trigger-session.d.ts +34 -0
  171. package/packages/server/dist/trigger-session.js +122 -0
  172. package/packages/server/dist/triggers.d.ts +83 -0
  173. package/packages/server/dist/triggers.js +124 -0
  174. package/packages/server/dist/trusted-proxy.d.ts +57 -0
  175. package/packages/server/dist/trusted-proxy.js +157 -0
  176. package/packages/server/dist/turn-notice.d.ts +154 -0
  177. package/packages/server/dist/turn-notice.js +271 -0
  178. package/packages/server/dist/unread.d.ts +36 -0
  179. package/packages/server/dist/unread.js +123 -0
  180. package/packages/server/dist/usage.d.ts +66 -0
  181. package/packages/server/dist/usage.js +243 -0
  182. package/packages/server/dist/wake-injection.d.ts +156 -0
  183. package/packages/server/dist/wake-injection.js +136 -0
  184. package/packages/server/dist/whisper-local.d.ts +9 -0
  185. package/packages/server/dist/whisper-local.js +122 -0
  186. package/packages/server/dist/ws-context.d.ts +158 -0
  187. package/packages/server/dist/ws-context.js +1 -0
  188. package/packages/server/dist/ws-protocol.d.ts +423 -0
  189. package/packages/server/dist/ws-protocol.js +251 -0
  190. package/packages/server/dist/ws-self-mcp.d.ts +52 -0
  191. package/packages/server/dist/ws-self-mcp.js +28 -0
  192. package/packages/server/dist/ws-triggers.d.ts +29 -0
  193. package/packages/server/dist/ws-triggers.js +261 -0
  194. package/packages/server/dist/ws-turn.d.ts +86 -0
  195. package/packages/server/dist/ws-turn.js +751 -0
  196. package/packages/server/dist/ws.d.ts +62 -0
  197. package/packages/server/dist/ws.js +1024 -0
  198. package/packages/server/package.json +6 -0
  199. package/packages/web/dist/assets/InstanceConfigPage-4cQGTj9Y.js +2 -0
  200. package/packages/web/dist/assets/MarkdownRenderer-CJZFLj_8.js +30 -0
  201. package/packages/web/dist/assets/ProjectRedirect-BHwVbbqB.js +2 -0
  202. package/packages/web/dist/assets/ProjectView-BOvuAITg.js +13 -0
  203. package/packages/web/dist/assets/ProjectsGrid-BCojZOGY.js +2 -0
  204. package/packages/web/dist/assets/arc-BVyxvnkl.js +2 -0
  205. package/packages/web/dist/assets/architectureDiagram-3BPJPVTR-Cd9Puqg1.js +37 -0
  206. package/packages/web/dist/assets/bash-I8pq0VWm.js +2 -0
  207. package/packages/web/dist/assets/blockDiagram-GPEHLZMM-B_AQt_Rx.js +133 -0
  208. package/packages/web/dist/assets/c-D2F1sQUL.js +2 -0
  209. package/packages/web/dist/assets/c4Diagram-AAUBKEIU-7e0TQtXB.js +11 -0
  210. package/packages/web/dist/assets/channel-C_1aISNn.js +2 -0
  211. package/packages/web/dist/assets/chunk-2J33WTMH-Dz4WZmUZ.js +2 -0
  212. package/packages/web/dist/assets/chunk-4BX2VUAB-DVWYldo6.js +2 -0
  213. package/packages/web/dist/assets/chunk-55IACEB6-BIMrizQd.js +2 -0
  214. package/packages/web/dist/assets/chunk-727SXJPM-CL2q9fF1.js +207 -0
  215. package/packages/web/dist/assets/chunk-AQP2D5EJ-BSq3HlQ9.js +232 -0
  216. package/packages/web/dist/assets/chunk-FMBD7UC4-Bgt64pLE.js +16 -0
  217. package/packages/web/dist/assets/chunk-ND2GUHAM-C8Pr2A38.js +2 -0
  218. package/packages/web/dist/assets/chunk-QZHKN3VN-BAOqh-MN.js +2 -0
  219. package/packages/web/dist/assets/classDiagram-4FO5ZUOK-B8JE7cHJ.js +2 -0
  220. package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-B8JE7cHJ.js +2 -0
  221. package/packages/web/dist/assets/core-lJeGACQg.js +4 -0
  222. package/packages/web/dist/assets/cose-bilkent-S5V4N54A-BF4tameA.js +2 -0
  223. package/packages/web/dist/assets/cpp-Bm3ii6kY.js +2 -0
  224. package/packages/web/dist/assets/csharp-BNpuYZMG.js +2 -0
  225. package/packages/web/dist/assets/css-DazXZka4.js +2 -0
  226. package/packages/web/dist/assets/cytoscape.esm-CUqq0XTU.js +332 -0
  227. package/packages/web/dist/assets/dagre-BM42HDAG-DdsLM6ql.js +5 -0
  228. package/packages/web/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
  229. package/packages/web/dist/assets/diagram-2AECGRRQ-BQ78S_RM.js +44 -0
  230. package/packages/web/dist/assets/diagram-5GNKFQAL-CAKE6nHE.js +11 -0
  231. package/packages/web/dist/assets/diagram-KO2AKTUF-fEvHRXgr.js +4 -0
  232. package/packages/web/dist/assets/diagram-LMA3HP47-CtWsgeSm.js +25 -0
  233. package/packages/web/dist/assets/diagram-OG6HWLK6-Buog8k7_.js +25 -0
  234. package/packages/web/dist/assets/erDiagram-TEJ5UH35-CWvVVPZS.js +86 -0
  235. package/packages/web/dist/assets/flowDiagram-I6XJVG4X-DsCGXtp4.js +163 -0
  236. package/packages/web/dist/assets/format-DamJQ9LO.js +2 -0
  237. package/packages/web/dist/assets/ganttDiagram-6RSMTGT7-CsdwIvvC.js +293 -0
  238. package/packages/web/dist/assets/gitGraphDiagram-PVQCEYII-hZvrX1sD.js +107 -0
  239. package/packages/web/dist/assets/go-BPx_y7j8.js +2 -0
  240. package/packages/web/dist/assets/graph-BlwPajkw.js +2 -0
  241. package/packages/web/dist/assets/index-CGbZdrm8.js +71 -0
  242. package/packages/web/dist/assets/index-DJDQ210-.css +1 -0
  243. package/packages/web/dist/assets/infoDiagram-5YYISTIA-nE_Vtz_l.js +3 -0
  244. package/packages/web/dist/assets/ini-B3BVgEsG.js +2 -0
  245. package/packages/web/dist/assets/init-Gi6I4Gst.js +2 -0
  246. package/packages/web/dist/assets/ishikawaDiagram-YF4QCWOH-2Nlqs0oH.js +71 -0
  247. package/packages/web/dist/assets/java-B5VKqtH2.js +2 -0
  248. package/packages/web/dist/assets/javascript-BKRaQes9.js +2 -0
  249. package/packages/web/dist/assets/journeyDiagram-JHISSGLW-mjU1fMc4.js +140 -0
  250. package/packages/web/dist/assets/json-DIYVocXf.js +2 -0
  251. package/packages/web/dist/assets/kanban-definition-UN3LZRKU-C_9xNaa3.js +90 -0
  252. package/packages/web/dist/assets/katex-C5jXJg4s.js +258 -0
  253. package/packages/web/dist/assets/kotlin-Cw-eF_bm.js +3 -0
  254. package/packages/web/dist/assets/lastTab-CU1zsF0j.js +2 -0
  255. package/packages/web/dist/assets/layout-Boy8vhSP.js +2 -0
  256. package/packages/web/dist/assets/linear-BxKd91ho.js +2 -0
  257. package/packages/web/dist/assets/mermaid.core-C-CuhD8I.js +310 -0
  258. package/packages/web/dist/assets/mindmap-definition-RKZ34NQL-Bs6yfcZv.js +97 -0
  259. package/packages/web/dist/assets/ordinal-Cboi1Yqb.js +2 -0
  260. package/packages/web/dist/assets/php-CRdd4dfS.js +3 -0
  261. package/packages/web/dist/assets/pieDiagram-4H26LBE5-DNdqX7O5.js +31 -0
  262. package/packages/web/dist/assets/python-kSlZHhJb.js +2 -0
  263. package/packages/web/dist/assets/quadrantDiagram-W4KKPZXB-ZMJl9OdZ.js +8 -0
  264. package/packages/web/dist/assets/queued-LamEU0GH.js +2 -0
  265. package/packages/web/dist/assets/requirementDiagram-4Y6WPE33-RmxMdm2q.js +85 -0
  266. package/packages/web/dist/assets/ruby-7T4ktxyv.js +2 -0
  267. package/packages/web/dist/assets/rust-HC3MBUhY.js +2 -0
  268. package/packages/web/dist/assets/sankeyDiagram-5OEKKPKP-OAAhWcfK.js +41 -0
  269. package/packages/web/dist/assets/scss-DZLA7YXz.js +2 -0
  270. package/packages/web/dist/assets/sequenceDiagram-3UESZ5HK-BWF7rCyN.js +163 -0
  271. package/packages/web/dist/assets/sql-BC88IN-V.js +2 -0
  272. package/packages/web/dist/assets/stateDiagram-AJRCARHV-M3b_zqqo.js +2 -0
  273. package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-CI7saFDa.js +2 -0
  274. package/packages/web/dist/assets/swift-Dn8kKO_1.js +2 -0
  275. package/packages/web/dist/assets/timeline-definition-PNZ67QCA-BpOCh63N.js +121 -0
  276. package/packages/web/dist/assets/typescript-C2FFdlUC.js +2 -0
  277. package/packages/web/dist/assets/vennDiagram-CIIHVFJN-D_iPH45Q.js +35 -0
  278. package/packages/web/dist/assets/wardley-L42UT6IY-DVD9I2Om.js +174 -0
  279. package/packages/web/dist/assets/wardleyDiagram-YWT4CUSO-Bn1IKwjn.js +79 -0
  280. package/packages/web/dist/assets/xml-BXBhIUeX.js +2 -0
  281. package/packages/web/dist/assets/xychartDiagram-2RQKCTM6-DwtI0bkR.js +8 -0
  282. package/packages/web/dist/assets/yaml-drsDnIKT.js +2 -0
  283. package/packages/web/dist/favicon.ico +0 -0
  284. package/packages/web/dist/fonts/inter-latin.woff2 +0 -0
  285. package/packages/web/dist/fonts/jetbrains-mono-latin.woff2 +0 -0
  286. package/packages/web/dist/icons/apple-touch-icon.png +0 -0
  287. package/packages/web/dist/icons/favicon-16.png +0 -0
  288. package/packages/web/dist/icons/favicon-32.png +0 -0
  289. package/packages/web/dist/icons/icon-192.png +0 -0
  290. package/packages/web/dist/icons/icon-512.png +0 -0
  291. package/packages/web/dist/icons/maskable-512.png +0 -0
  292. package/packages/web/dist/index.html +56 -0
  293. package/packages/web/dist/manifest.webmanifest +32 -0
  294. package/packages/web/dist/sw.js +139 -0
@@ -0,0 +1,99 @@
1
+ import type { ManagementApiConfig } from "./management-config.js";
2
+ import type { ManagementPrincipal, ManagementDeniedError } from "./management-policy.js";
3
+ /** Why a credential was refused. Shapes the challenge; never leaks the secret. */
4
+ export type ManagementAuthFailure =
5
+ /** No credential presented at all. */
6
+ "missing"
7
+ /** An `Authorization` header we could not parse as a bearer token. */
8
+ | "malformed"
9
+ /** Parsed fine, but matched no configured client (or failed instance binding). */
10
+ | "invalid";
11
+ export type ManagementAuthResult = {
12
+ ok: true;
13
+ principal: ManagementPrincipal;
14
+ } | {
15
+ ok: false;
16
+ failure: ManagementAuthFailure;
17
+ };
18
+ /**
19
+ * Compare two secrets without leaking their relationship through timing.
20
+ *
21
+ * `timingSafeEqual` requires equal-length buffers and throws otherwise — which
22
+ * would itself leak length. Hashing both sides to a fixed-width digest first
23
+ * makes the comparison length-independent as well as time-independent.
24
+ */
25
+ export declare function constantTimeEqual(a: string, b: string): boolean;
26
+ /**
27
+ * Extract a bearer token from an `Authorization` header value.
28
+ * Returns undefined when absent, and null when present but not a bearer token
29
+ * (so the caller can distinguish "missing" from "malformed").
30
+ */
31
+ export declare function bearerToken(headerValue: string | string[] | undefined): string | null | undefined;
32
+ /**
33
+ * Match a presented token against the configured clients.
34
+ *
35
+ * Scans EVERY client without early-exit so total work does not depend on which
36
+ * client matched (or on how far down the list a match sits). Instance binding is
37
+ * checked first: a token carrying an instance id that isn't ours is refused even
38
+ * if its secret would otherwise match, which is what makes a credential minted
39
+ * for another Paddock useless here.
40
+ */
41
+ export declare function resolveTokenPrincipal(token: string, cfg: ManagementApiConfig): ManagementPrincipal | null;
42
+ /**
43
+ * Authenticate a request's `Authorization` header against the configured
44
+ * clients. The caller decides the HTTP shape (see {@link challengeHeader}).
45
+ */
46
+ export declare function authenticateManagementRequest(authorization: string | string[] | undefined, cfg: ManagementApiConfig): ManagementAuthResult;
47
+ /**
48
+ * Build the `WWW-Authenticate` challenge for a refusal (RFC 6750 §3).
49
+ *
50
+ * `resourceMetadataUrl` is the RFC 9728 protected-resource-metadata location.
51
+ * M2 supplies it so an MCP client can discover how to obtain a token; M1 omits
52
+ * it (there is no OAuth path yet) and the header still forms a valid challenge.
53
+ */
54
+ export declare function challengeHeader(failure: ManagementAuthFailure, resourceMetadataUrl?: string): string;
55
+ /**
56
+ * The challenge for an authenticated caller whose SCOPE doesn't cover what it
57
+ * asked for (MCP spec revision 2025-11-25 / RFC 6750 §3.1).
58
+ *
59
+ * Deliberately distinct from the 401 above: the credential was valid, so
60
+ * re-authenticating changes nothing — the caller needs a WIDER GRANT. The
61
+ * transport pairs this with **403**, and the `scope` parameter names what was
62
+ * required, which is what lets a client ask for incremental consent instead of
63
+ * failing opaquely.
64
+ */
65
+ export declare function insufficientScopeChallenge(requiredScope: string, resourceMetadataUrl?: string): string;
66
+ /**
67
+ * The HTTP shape for a policy denial raised by the ops layer.
68
+ *
69
+ * Kept HERE, next to the other challenge builders, so that every transport maps
70
+ * a `ManagementDeniedError` the same way instead of inventing its own status.
71
+ * Both denial codes are scope problems, hence 403 rather than 401.
72
+ */
73
+ export declare function denialResponse(error: ManagementDeniedError, resourceMetadataUrl?: string): {
74
+ status: 403;
75
+ challenge: string;
76
+ body: {
77
+ error: string;
78
+ code: string;
79
+ message: string;
80
+ };
81
+ };
82
+ /**
83
+ * The RFC 9728 protected-resource metadata URL to advertise in a challenge.
84
+ *
85
+ * Delegates to {@link metadataUrl} so there is ONE rule for when a document
86
+ * exists: advertising a `resource_metadata` URL that then 404s would send a
87
+ * client down a discovery path that cannot succeed.
88
+ */
89
+ export declare function resourceMetadataUrl(cfg: ManagementApiConfig): string | undefined;
90
+ /**
91
+ * Whether the external management API is configured at all (drives the 404).
92
+ *
93
+ * Requires BOTH a client and a canonical public URL: without the latter, M2's
94
+ * discovery document could not be served correctly, so the surface stays off
95
+ * rather than half-working. `resolveManagementApiConfig` already empties the
96
+ * client list when `publicUrl` is missing or bad; this is the belt to that
97
+ * braces.
98
+ */
99
+ export declare function isManagementApiEnabled(cfg: ManagementApiConfig | undefined): boolean;
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Management API authentication (issue #312 M1) — the config-token authenticator.
3
+ *
4
+ * Turns a presented credential into a {@link ManagementPrincipal}, or refuses.
5
+ * Transport-agnostic: it takes a header value and returns a verdict, so the
6
+ * `/mcp` route, a future REST surface (#465), and tests all share one path.
7
+ *
8
+ * ── This is INDEPENDENT of `PADDOCK_AUTH_MODE` and of any reverse proxy ─────
9
+ * The non-negotiable invariant on the ticket: Paddock authenticates the
10
+ * management surface ITSELF. It must be possible to run with no proxy at all —
11
+ * even `auth.mode: none` — and still have a correctly authenticated `/mcp`. A
12
+ * proxy is an operator's choice, never a prerequisite. Consequently `/mcp` is
13
+ * exempted from the browser auth hook (auth.ts) and gated by THIS instead; the
14
+ * exemption is safe only because this runs in its place.
15
+ *
16
+ * ── Why 401 + WWW-Authenticate, never a 302 ────────────────────────────────
17
+ * An SSO proxy answers an unauthenticated request with an HTML login redirect.
18
+ * That breaks MCP clients outright: they cannot follow it, and OAuth discovery
19
+ * (M2) depends on reading a real `401` + `WWW-Authenticate` challenge. So this
20
+ * module owns the challenge header and always fails with a status an MCP client
21
+ * can act on.
22
+ */
23
+ import { createHash, timingSafeEqual } from "node:crypto";
24
+ import { tokenInstanceId } from "./management-config.js";
25
+ import { metadataUrl } from "./management-metadata.js";
26
+ /**
27
+ * Compare two secrets without leaking their relationship through timing.
28
+ *
29
+ * `timingSafeEqual` requires equal-length buffers and throws otherwise — which
30
+ * would itself leak length. Hashing both sides to a fixed-width digest first
31
+ * makes the comparison length-independent as well as time-independent.
32
+ */
33
+ export function constantTimeEqual(a, b) {
34
+ const ha = createHash("sha256").update(a, "utf8").digest();
35
+ const hb = createHash("sha256").update(b, "utf8").digest();
36
+ return timingSafeEqual(ha, hb);
37
+ }
38
+ /**
39
+ * Extract a bearer token from an `Authorization` header value.
40
+ * Returns undefined when absent, and null when present but not a bearer token
41
+ * (so the caller can distinguish "missing" from "malformed").
42
+ */
43
+ export function bearerToken(headerValue) {
44
+ const raw = Array.isArray(headerValue) ? headerValue[0] : headerValue;
45
+ if (raw === undefined || raw.trim().length === 0)
46
+ return undefined;
47
+ const m = /^Bearer[ \t]+(.+)$/i.exec(raw.trim());
48
+ if (!m)
49
+ return null;
50
+ const token = m[1].trim();
51
+ return token.length > 0 ? token : null;
52
+ }
53
+ /**
54
+ * Match a presented token against the configured clients.
55
+ *
56
+ * Scans EVERY client without early-exit so total work does not depend on which
57
+ * client matched (or on how far down the list a match sits). Instance binding is
58
+ * checked first: a token carrying an instance id that isn't ours is refused even
59
+ * if its secret would otherwise match, which is what makes a credential minted
60
+ * for another Paddock useless here.
61
+ */
62
+ export function resolveTokenPrincipal(token, cfg) {
63
+ const embedded = tokenInstanceId(token);
64
+ // A prefixed token names its instance; enforce it when we know our own.
65
+ if (embedded !== undefined && cfg.instanceId !== undefined && embedded !== cfg.instanceId) {
66
+ return null;
67
+ }
68
+ let matched = null;
69
+ for (const client of cfg.clients) {
70
+ if (constantTimeEqual(token, client.token))
71
+ matched = client;
72
+ }
73
+ if (!matched)
74
+ return null;
75
+ return { clientId: matched.clientId, kind: "token", scope: matched.scope };
76
+ }
77
+ /**
78
+ * Authenticate a request's `Authorization` header against the configured
79
+ * clients. The caller decides the HTTP shape (see {@link challengeHeader}).
80
+ */
81
+ export function authenticateManagementRequest(authorization, cfg) {
82
+ const token = bearerToken(authorization);
83
+ if (token === undefined)
84
+ return { ok: false, failure: "missing" };
85
+ if (token === null)
86
+ return { ok: false, failure: "malformed" };
87
+ const principal = resolveTokenPrincipal(token, cfg);
88
+ if (!principal)
89
+ return { ok: false, failure: "invalid" };
90
+ return { ok: true, principal };
91
+ }
92
+ /**
93
+ * Build the `WWW-Authenticate` challenge for a refusal (RFC 6750 §3).
94
+ *
95
+ * `resourceMetadataUrl` is the RFC 9728 protected-resource-metadata location.
96
+ * M2 supplies it so an MCP client can discover how to obtain a token; M1 omits
97
+ * it (there is no OAuth path yet) and the header still forms a valid challenge.
98
+ */
99
+ export function challengeHeader(failure, resourceMetadataUrl) {
100
+ const params = ['realm="paddock"'];
101
+ if (failure !== "missing") {
102
+ params.push('error="invalid_token"');
103
+ params.push(`error_description="${failure === "malformed" ? "malformed Authorization header" : "the access token is invalid"}"`);
104
+ }
105
+ if (resourceMetadataUrl)
106
+ params.push(`resource_metadata="${resourceMetadataUrl}"`);
107
+ return `Bearer ${params.join(", ")}`;
108
+ }
109
+ /**
110
+ * The challenge for an authenticated caller whose SCOPE doesn't cover what it
111
+ * asked for (MCP spec revision 2025-11-25 / RFC 6750 §3.1).
112
+ *
113
+ * Deliberately distinct from the 401 above: the credential was valid, so
114
+ * re-authenticating changes nothing — the caller needs a WIDER GRANT. The
115
+ * transport pairs this with **403**, and the `scope` parameter names what was
116
+ * required, which is what lets a client ask for incremental consent instead of
117
+ * failing opaquely.
118
+ */
119
+ export function insufficientScopeChallenge(requiredScope, resourceMetadataUrl) {
120
+ const params = [
121
+ 'realm="paddock"',
122
+ 'error="insufficient_scope"',
123
+ `error_description="the authenticated client's scope does not permit this operation"`,
124
+ `scope="${requiredScope}"`,
125
+ ];
126
+ if (resourceMetadataUrl)
127
+ params.push(`resource_metadata="${resourceMetadataUrl}"`);
128
+ return `Bearer ${params.join(", ")}`;
129
+ }
130
+ /**
131
+ * The HTTP shape for a policy denial raised by the ops layer.
132
+ *
133
+ * Kept HERE, next to the other challenge builders, so that every transport maps
134
+ * a `ManagementDeniedError` the same way instead of inventing its own status.
135
+ * Both denial codes are scope problems, hence 403 rather than 401.
136
+ */
137
+ export function denialResponse(error, resourceMetadataUrl) {
138
+ return {
139
+ status: 403,
140
+ challenge: insufficientScopeChallenge(error.code === "project_denied" ? `project:${error.projectSlug}` : error.operation, resourceMetadataUrl),
141
+ body: { error: "forbidden", code: error.code, message: error.message },
142
+ };
143
+ }
144
+ /**
145
+ * The RFC 9728 protected-resource metadata URL to advertise in a challenge.
146
+ *
147
+ * Delegates to {@link metadataUrl} so there is ONE rule for when a document
148
+ * exists: advertising a `resource_metadata` URL that then 404s would send a
149
+ * client down a discovery path that cannot succeed.
150
+ */
151
+ export function resourceMetadataUrl(cfg) {
152
+ return metadataUrl(cfg);
153
+ }
154
+ /**
155
+ * Whether the external management API is configured at all (drives the 404).
156
+ *
157
+ * Requires BOTH a client and a canonical public URL: without the latter, M2's
158
+ * discovery document could not be served correctly, so the surface stays off
159
+ * rather than half-working. `resolveManagementApiConfig` already empties the
160
+ * client list when `publicUrl` is missing or bad; this is the belt to that
161
+ * braces.
162
+ */
163
+ export function isManagementApiEnabled(cfg) {
164
+ return (cfg?.clients.length ?? 0) > 0 && Boolean(cfg?.publicUrl);
165
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * `managementApi.clients` config resolution (issue #312 M1).
3
+ *
4
+ * Turns the optional `managementApi` block of `paddock.config.yaml` into the
5
+ * resolved list of external clients the token authenticator will accept, each
6
+ * carrying its {@link ManagementScope}. Pure over an injected environment map so
7
+ * the whole validation matrix is unit-testable without touching `process.env`.
8
+ *
9
+ * ── Why token material is REFERENCED, never inlined ─────────────────────────
10
+ * `paddock.config.yaml` is git-tracked (and editable from the instance Settings
11
+ * screen, #385). A secret written there leaks into history the moment it is
12
+ * committed, and no later edit removes it. So a client's credential is given as
13
+ * an env REFERENCE (`ref: env:PADDOCK_MCP_TOKEN_MY_LAPTOP`) and an inline value
14
+ * is a hard config error — not a warning — because accepting one silently would
15
+ * teach operators the unsafe habit.
16
+ *
17
+ * ── Failure posture ─────────────────────────────────────────────────────────
18
+ * Two different kinds of problem, handled differently on purpose:
19
+ * - **Malformed config** (inline secret, unknown auth type, bad scope shape)
20
+ * is an ERROR. The operator wrote something meaningless; surface it.
21
+ * - **An unresolvable reference** (env var unset/blank/too short) DROPS THAT
22
+ * CLIENT with a loud warning, leaving other clients working. Dropping is the
23
+ * fail-closed direction: the credential simply doesn't exist, so nothing can
24
+ * authenticate as that client. If every client drops, `/mcp` reverts to its
25
+ * unconfigured 404 — the endpoint ceases to exist rather than opening up.
26
+ */
27
+ import { type ManagementScope } from "./management-policy.js";
28
+ import { type TrustedProxies } from "./trusted-proxy.js";
29
+ /**
30
+ * Recommended token prefix. A prefixed token additionally carries the INSTANCE
31
+ * it was minted for (`pdk_<instanceId>_<secret>`), which is what makes a
32
+ * credential for instance A meaningless at instance B — see
33
+ * {@link tokenInstanceId}. Also gives secret scanners something to match on.
34
+ */
35
+ export declare const TOKEN_PREFIX = "pdk_";
36
+ /**
37
+ * Minimum accepted token length. Not a strength guarantee — just a floor that
38
+ * rejects obvious placeholders ("changeme", "test") before they can ever
39
+ * authenticate a turn-spawning client.
40
+ */
41
+ export declare const MIN_TOKEN_LENGTH = 24;
42
+ /** One resolved external client: an identity, a credential, and a scope. */
43
+ export interface ManagementClient {
44
+ /** The `managementApi.clients` map key — the stable, loggable identity. */
45
+ clientId: string;
46
+ /** The resolved secret. NEVER logged, never echoed in an API response. */
47
+ token: string;
48
+ scope: ManagementScope;
49
+ }
50
+ /** The resolved management-API config. */
51
+ export interface ManagementApiConfig {
52
+ /**
53
+ * Resolved external clients. EMPTY means the external management API is off
54
+ * entirely and `/mcp` must 404 (fail closed) — see routes/mcp.ts.
55
+ */
56
+ clients: ManagementClient[];
57
+ /**
58
+ * This instance's identifier, used to bind prefixed tokens to it. Absent ⇒
59
+ * binding is not enforced (an unprefixed-token deployment still works).
60
+ */
61
+ instanceId?: string;
62
+ /**
63
+ * The canonical PUBLIC origin clients reach this instance at, with no trailing
64
+ * slash (e.g. `https://paddock.example.com`).
65
+ *
66
+ * Required whenever clients are configured, because RFC 9728 §3.3 makes the
67
+ * `resource` value in the protected-resource metadata document a byte-for-byte
68
+ * match against the URL the client used — a mismatch means the client MUST
69
+ * discard the document. It cannot be derived from `Host`: behind a
70
+ * TLS-terminating proxy the scheme is wrong, and the header is
71
+ * attacker-controlled. So the operator states it once, here.
72
+ */
73
+ publicUrl?: string;
74
+ /**
75
+ * Issuer URLs of the OAuth authorization servers that may issue tokens for
76
+ * this resource, advertised in the RFC 9728 discovery document.
77
+ *
78
+ * Optional and empty by default: with only config tokens configured there IS
79
+ * no authorization server, and advertising one would send clients into an
80
+ * OAuth flow that cannot complete. Populated when the OAuth authenticator is
81
+ * configured.
82
+ */
83
+ authorizationServers?: string[];
84
+ /**
85
+ * Peers whose `X-Forwarded-Proto` the `/mcp` plaintext guard believes (#474).
86
+ * Absent ⇒ the compatibility default (loopback + private address space); see
87
+ * trusted-proxy.ts for what that default does and does not prove.
88
+ */
89
+ trustedProxies?: TrustedProxies;
90
+ }
91
+ /** On-disk shape of the `managementApi` block. Every field optional. */
92
+ export interface ManagementApiConfigFile {
93
+ instanceId?: string;
94
+ publicUrl?: string;
95
+ authorizationServers?: string[] | string;
96
+ /** IPs / CIDRs / presets — see {@link parseTrustedProxies}. */
97
+ trustedProxies?: string[] | string;
98
+ clients?: Record<string, ManagementClientConfigFile | null | undefined>;
99
+ }
100
+ /**
101
+ * Validate the canonical public URL. Returns the normalised origin (no trailing
102
+ * slash) or an error string.
103
+ *
104
+ * Rejects plaintext for anything but loopback: `/mcp` carries bearer tokens, so
105
+ * advertising an `http://` origin to the world would publish a URL on which the
106
+ * credential is readable in transit. This is the config-time half of the epic's
107
+ * "refuse to serve `/mcp` over plaintext" rule (routes/mcp.ts enforces the
108
+ * per-request half).
109
+ */
110
+ export declare function validatePublicUrl(raw: string): {
111
+ url: string;
112
+ } | {
113
+ error: string;
114
+ };
115
+ export interface ManagementClientConfigFile {
116
+ auth?: {
117
+ type?: string;
118
+ /** `env:VAR_NAME` — the ONLY supported form. An inline value is an error. */
119
+ ref?: string;
120
+ /** Present only in a MISCONFIGURED file; its presence is a hard error. */
121
+ token?: string;
122
+ secret?: string;
123
+ };
124
+ scope?: {
125
+ projects?: string[] | string;
126
+ allow?: string[] | string;
127
+ deny?: string[] | string;
128
+ denyProjects?: string[] | string;
129
+ maxSpawnDepth?: number | string;
130
+ };
131
+ }
132
+ /** Outcome of resolving the block: the clients plus everything worth telling the operator. */
133
+ export interface ManagementConfigResolution {
134
+ config: ManagementApiConfig;
135
+ /** Fatal-to-that-client misconfigurations (logged at error level). */
136
+ errors: string[];
137
+ /** Non-fatal advice + dropped-client notices (logged at warn level). */
138
+ warnings: string[];
139
+ }
140
+ /**
141
+ * The instance id embedded in a prefixed token (`pdk_<instanceId>_<secret>`), or
142
+ * undefined for a token that carries none.
143
+ *
144
+ * This is the audience-binding hook the ticket asks for on day one: the
145
+ * authenticator refuses a prefixed token whose embedded instance doesn't match
146
+ * this instance, so copying a credential to a second Paddock does not make it
147
+ * work there — even though the secret bytes are identical.
148
+ */
149
+ export declare function tokenInstanceId(token: string): string | undefined;
150
+ /**
151
+ * Resolve one client's scope, defaulting to READ-ONLY when the operator gave
152
+ * none. The default is the security posture of the whole feature: a client
153
+ * added without thinking about scope gets the risk class that cannot execute
154
+ * code (see the RCE framing in management-policy.ts).
155
+ */
156
+ export declare function resolveClientScope(file: ManagementClientConfigFile["scope"]): ManagementScope;
157
+ /**
158
+ * Resolve the `managementApi` block against `env`.
159
+ *
160
+ * Never throws: a bad client is reported and dropped so one typo can't take the
161
+ * whole instance down (the browser UI and keepers are unaffected by management-
162
+ * API config). The caller logs `errors`/`warnings` at boot.
163
+ */
164
+ export declare function resolveManagementApiConfig(file: ManagementApiConfigFile | undefined, env: Record<string, string | undefined>): ManagementConfigResolution;