mrplex 0.0.1

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 (319) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +284 -0
  3. package/dist/cli/config.d.ts +28 -0
  4. package/dist/cli/config.js +48 -0
  5. package/dist/cli/config.js.map +1 -0
  6. package/dist/cli/exit-codes.d.ts +13 -0
  7. package/dist/cli/exit-codes.js +39 -0
  8. package/dist/cli/exit-codes.js.map +1 -0
  9. package/dist/cli/format.d.ts +22 -0
  10. package/dist/cli/format.js +148 -0
  11. package/dist/cli/format.js.map +1 -0
  12. package/dist/cli/main.d.ts +16 -0
  13. package/dist/cli/main.js +1518 -0
  14. package/dist/cli/main.js.map +1 -0
  15. package/dist/client/kernel-client.d.ts +97 -0
  16. package/dist/client/kernel-client.js +14 -0
  17. package/dist/client/kernel-client.js.map +1 -0
  18. package/dist/client/local.d.ts +26 -0
  19. package/dist/client/local.js +103 -0
  20. package/dist/client/local.js.map +1 -0
  21. package/dist/client/remote-mcp.d.ts +35 -0
  22. package/dist/client/remote-mcp.js +174 -0
  23. package/dist/client/remote-mcp.js.map +1 -0
  24. package/dist/embed/backfill.d.ts +24 -0
  25. package/dist/embed/backfill.js +33 -0
  26. package/dist/embed/backfill.js.map +1 -0
  27. package/dist/embed/chunker.d.ts +31 -0
  28. package/dist/embed/chunker.js +121 -0
  29. package/dist/embed/chunker.js.map +1 -0
  30. package/dist/embed/cmd-hook.d.ts +21 -0
  31. package/dist/embed/cmd-hook.js +100 -0
  32. package/dist/embed/cmd-hook.js.map +1 -0
  33. package/dist/embed/config.d.ts +61 -0
  34. package/dist/embed/config.js +109 -0
  35. package/dist/embed/config.js.map +1 -0
  36. package/dist/embed/hook.d.ts +38 -0
  37. package/dist/embed/hook.js +56 -0
  38. package/dist/embed/hook.js.map +1 -0
  39. package/dist/embed/http-hook.d.ts +13 -0
  40. package/dist/embed/http-hook.js +39 -0
  41. package/dist/embed/http-hook.js.map +1 -0
  42. package/dist/embed/worker.d.ts +62 -0
  43. package/dist/embed/worker.js +228 -0
  44. package/dist/embed/worker.js.map +1 -0
  45. package/dist/fixtures/hwe/characters/albion-ever.md +12 -0
  46. package/dist/fixtures/hwe/characters/cent.md +11 -0
  47. package/dist/fixtures/hwe/concepts/cent-demiurge-resonance.md +10 -0
  48. package/dist/fixtures/hwe/concepts/phase-looper.md +10 -0
  49. package/dist/fixtures/hwe/entities/demiurge.md +11 -0
  50. package/dist/fixtures/hwe/entities/monad.md +11 -0
  51. package/dist/fixtures/hwe/moc/globocorp-network.md +19 -0
  52. package/dist/fixtures/hwe/organizations/globocorp.md +11 -0
  53. package/dist/fixtures/hwe/readme.md +24 -0
  54. package/dist/fixtures/starship/crew/aria-okonkwo.md +14 -0
  55. package/dist/fixtures/starship/crew/bexley-orr.md +14 -0
  56. package/dist/fixtures/starship/crew/dax-thorne.md +14 -0
  57. package/dist/fixtures/starship/crew/isolde-marsh.md +16 -0
  58. package/dist/fixtures/starship/crew/kestrel-vance.md +15 -0
  59. package/dist/fixtures/starship/crew/quill-vasquez.md +14 -0
  60. package/dist/fixtures/starship/crew/soren-halloway.md +16 -0
  61. package/dist/fixtures/starship/encounters/the-drift-choir.md +14 -0
  62. package/dist/fixtures/starship/encounters/the-hollow-signal.md +16 -0
  63. package/dist/fixtures/starship/encounters/the-wandering-buoy.md +14 -0
  64. package/dist/fixtures/starship/equipment/coolant-loop-b.md +13 -0
  65. package/dist/fixtures/starship/equipment/emergency-ration-bars.md +12 -0
  66. package/dist/fixtures/starship/equipment/long-range-sensors.md +13 -0
  67. package/dist/fixtures/starship/equipment/plasma-manifold-3.md +15 -0
  68. package/dist/fixtures/starship/equipment/shuttle-corvid.md +13 -0
  69. package/dist/fixtures/starship/logs/marsh-4419-1.md +13 -0
  70. package/dist/fixtures/starship/logs/okonkwo-4415-1.md +13 -0
  71. package/dist/fixtures/starship/logs/orr-4413-1.md +13 -0
  72. package/dist/fixtures/starship/logs/orr-4420-1.md +12 -0
  73. package/dist/fixtures/starship/logs/thorne-4413-2.md +13 -0
  74. package/dist/fixtures/starship/logs/vance-4419-2.md +13 -0
  75. package/dist/fixtures/starship/misc/the-galley-ficus.md +11 -0
  76. package/dist/fixtures/starship/missions/the-cinder-run.md +17 -0
  77. package/dist/fixtures/starship/missions/the-drift-choir.md +17 -0
  78. package/dist/fixtures/starship/missions/the-hollow-signal.md +17 -0
  79. package/dist/fixtures/starship/missions/the-silent-beacon.md +17 -0
  80. package/dist/fixtures/starship/moc/crew.md +19 -0
  81. package/dist/fixtures/starship/moc/encounters.md +14 -0
  82. package/dist/fixtures/starship/moc/missions.md +15 -0
  83. package/dist/fixtures/starship/readme.md +60 -0
  84. package/dist/index.d.ts +1 -0
  85. package/dist/index.js +3 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/kernel/auth/glob.d.ts +46 -0
  88. package/dist/kernel/auth/glob.js +112 -0
  89. package/dist/kernel/auth/glob.js.map +1 -0
  90. package/dist/kernel/auth/scope.d.ts +53 -0
  91. package/dist/kernel/auth/scope.js +83 -0
  92. package/dist/kernel/auth/scope.js.map +1 -0
  93. package/dist/kernel/casefold.d.ts +37 -0
  94. package/dist/kernel/casefold.js +44 -0
  95. package/dist/kernel/casefold.js.map +1 -0
  96. package/dist/kernel/constants.d.ts +21 -0
  97. package/dist/kernel/constants.js +22 -0
  98. package/dist/kernel/constants.js.map +1 -0
  99. package/dist/kernel/context.d.ts +70 -0
  100. package/dist/kernel/context.js +114 -0
  101. package/dist/kernel/context.js.map +1 -0
  102. package/dist/kernel/deletion.d.ts +50 -0
  103. package/dist/kernel/deletion.js +83 -0
  104. package/dist/kernel/deletion.js.map +1 -0
  105. package/dist/kernel/diff.d.ts +36 -0
  106. package/dist/kernel/diff.js +61 -0
  107. package/dist/kernel/diff.js.map +1 -0
  108. package/dist/kernel/errors.d.ts +37 -0
  109. package/dist/kernel/errors.js +55 -0
  110. package/dist/kernel/errors.js.map +1 -0
  111. package/dist/kernel/frontmatter-input.d.ts +32 -0
  112. package/dist/kernel/frontmatter-input.js +85 -0
  113. package/dist/kernel/frontmatter-input.js.map +1 -0
  114. package/dist/kernel/graph.d.ts +34 -0
  115. package/dist/kernel/graph.js +559 -0
  116. package/dist/kernel/graph.js.map +1 -0
  117. package/dist/kernel/history.d.ts +74 -0
  118. package/dist/kernel/history.js +143 -0
  119. package/dist/kernel/history.js.map +1 -0
  120. package/dist/kernel/kernel.d.ts +162 -0
  121. package/dist/kernel/kernel.js +675 -0
  122. package/dist/kernel/kernel.js.map +1 -0
  123. package/dist/kernel/path-config.d.ts +101 -0
  124. package/dist/kernel/path-config.js +171 -0
  125. package/dist/kernel/path-config.js.map +1 -0
  126. package/dist/kernel/query/ast.d.ts +24 -0
  127. package/dist/kernel/query/ast.js +52 -0
  128. package/dist/kernel/query/ast.js.map +1 -0
  129. package/dist/kernel/query/cel-parse.d.ts +37 -0
  130. package/dist/kernel/query/cel-parse.js +88 -0
  131. package/dist/kernel/query/cel-parse.js.map +1 -0
  132. package/dist/kernel/query/degrees.d.ts +31 -0
  133. package/dist/kernel/query/degrees.js +135 -0
  134. package/dist/kernel/query/degrees.js.map +1 -0
  135. package/dist/kernel/query/graph-ast.d.ts +51 -0
  136. package/dist/kernel/query/graph-ast.js +139 -0
  137. package/dist/kernel/query/graph-ast.js.map +1 -0
  138. package/dist/kernel/query/query.d.ts +56 -0
  139. package/dist/kernel/query/query.js +276 -0
  140. package/dist/kernel/query/query.js.map +1 -0
  141. package/dist/kernel/validation.d.ts +64 -0
  142. package/dist/kernel/validation.js +183 -0
  143. package/dist/kernel/validation.js.map +1 -0
  144. package/dist/kernel/version-id.d.ts +10 -0
  145. package/dist/kernel/version-id.js +21 -0
  146. package/dist/kernel/version-id.js.map +1 -0
  147. package/dist/kernel/wire.d.ts +148 -0
  148. package/dist/kernel/wire.js +6 -0
  149. package/dist/kernel/wire.js.map +1 -0
  150. package/dist/links/backfill.d.ts +28 -0
  151. package/dist/links/backfill.js +33 -0
  152. package/dist/links/backfill.js.map +1 -0
  153. package/dist/links/extract.d.ts +29 -0
  154. package/dist/links/extract.js +233 -0
  155. package/dist/links/extract.js.map +1 -0
  156. package/dist/links/link-config.d.ts +70 -0
  157. package/dist/links/link-config.js +107 -0
  158. package/dist/links/link-config.js.map +1 -0
  159. package/dist/links/maintain.d.ts +37 -0
  160. package/dist/links/maintain.js +91 -0
  161. package/dist/links/maintain.js.map +1 -0
  162. package/dist/links/repair.d.ts +46 -0
  163. package/dist/links/repair.js +96 -0
  164. package/dist/links/repair.js.map +1 -0
  165. package/dist/links/resolve.d.ts +41 -0
  166. package/dist/links/resolve.js +121 -0
  167. package/dist/links/resolve.js.map +1 -0
  168. package/dist/links/stale.d.ts +32 -0
  169. package/dist/links/stale.js +73 -0
  170. package/dist/links/stale.js.map +1 -0
  171. package/dist/markdown/content-hash.d.ts +34 -0
  172. package/dist/markdown/content-hash.js +46 -0
  173. package/dist/markdown/content-hash.js.map +1 -0
  174. package/dist/markdown/frontmatter.d.ts +69 -0
  175. package/dist/markdown/frontmatter.js +147 -0
  176. package/dist/markdown/frontmatter.js.map +1 -0
  177. package/dist/markdown/hash-backfill.d.ts +22 -0
  178. package/dist/markdown/hash-backfill.js +43 -0
  179. package/dist/markdown/hash-backfill.js.map +1 -0
  180. package/dist/mcp/query-syntax.d.ts +20 -0
  181. package/dist/mcp/query-syntax.js +194 -0
  182. package/dist/mcp/query-syntax.js.map +1 -0
  183. package/dist/mcp/render.d.ts +31 -0
  184. package/dist/mcp/render.js +110 -0
  185. package/dist/mcp/render.js.map +1 -0
  186. package/dist/mcp/server.d.ts +60 -0
  187. package/dist/mcp/server.js +170 -0
  188. package/dist/mcp/server.js.map +1 -0
  189. package/dist/mcp/tools.d.ts +93 -0
  190. package/dist/mcp/tools.js +1218 -0
  191. package/dist/mcp/tools.js.map +1 -0
  192. package/dist/rest/conditional.d.ts +37 -0
  193. package/dist/rest/conditional.js +58 -0
  194. package/dist/rest/conditional.js.map +1 -0
  195. package/dist/rest/negotiate.d.ts +33 -0
  196. package/dist/rest/negotiate.js +54 -0
  197. package/dist/rest/negotiate.js.map +1 -0
  198. package/dist/rest/routes.d.ts +37 -0
  199. package/dist/rest/routes.js +744 -0
  200. package/dist/rest/routes.js.map +1 -0
  201. package/dist/seed/fixture-seed.d.ts +35 -0
  202. package/dist/seed/fixture-seed.js +75 -0
  203. package/dist/seed/fixture-seed.js.map +1 -0
  204. package/dist/seed/starship-config.d.ts +3 -0
  205. package/dist/seed/starship-config.js +5 -0
  206. package/dist/seed/starship-config.js.map +1 -0
  207. package/dist/server/headers.d.ts +36 -0
  208. package/dist/server/headers.js +30 -0
  209. package/dist/server/headers.js.map +1 -0
  210. package/dist/server/http-error.d.ts +53 -0
  211. package/dist/server/http-error.js +104 -0
  212. package/dist/server/http-error.js.map +1 -0
  213. package/dist/server/serve.d.ts +56 -0
  214. package/dist/server/serve.js +135 -0
  215. package/dist/server/serve.js.map +1 -0
  216. package/dist/shell/audit.d.ts +32 -0
  217. package/dist/shell/audit.js +28 -0
  218. package/dist/shell/audit.js.map +1 -0
  219. package/dist/shell/guard.d.ts +50 -0
  220. package/dist/shell/guard.js +176 -0
  221. package/dist/shell/guard.js.map +1 -0
  222. package/dist/shell/keys.d.ts +36 -0
  223. package/dist/shell/keys.js +64 -0
  224. package/dist/shell/keys.js.map +1 -0
  225. package/dist/shell/login.d.ts +56 -0
  226. package/dist/shell/login.js +130 -0
  227. package/dist/shell/login.js.map +1 -0
  228. package/dist/shell/oidc.d.ts +62 -0
  229. package/dist/shell/oidc.js +102 -0
  230. package/dist/shell/oidc.js.map +1 -0
  231. package/dist/shell/policy.d.ts +100 -0
  232. package/dist/shell/policy.js +232 -0
  233. package/dist/shell/policy.js.map +1 -0
  234. package/dist/shell/proxy-policy.d.ts +34 -0
  235. package/dist/shell/proxy-policy.js +120 -0
  236. package/dist/shell/proxy-policy.js.map +1 -0
  237. package/dist/shell/proxy.d.ts +47 -0
  238. package/dist/shell/proxy.js +253 -0
  239. package/dist/shell/proxy.js.map +1 -0
  240. package/dist/shell/serve.d.ts +86 -0
  241. package/dist/shell/serve.js +0 -0
  242. package/dist/shell/serve.js.map +1 -0
  243. package/dist/shell/stdio.d.ts +55 -0
  244. package/dist/shell/stdio.js +63 -0
  245. package/dist/shell/stdio.js.map +1 -0
  246. package/dist/storage/registry.d.ts +20 -0
  247. package/dist/storage/registry.js +38 -0
  248. package/dist/storage/registry.js.map +1 -0
  249. package/dist/storage/search-plan.d.ts +93 -0
  250. package/dist/storage/search-plan.js +15 -0
  251. package/dist/storage/search-plan.js.map +1 -0
  252. package/dist/storage/types.d.ts +448 -0
  253. package/dist/storage/types.js +14 -0
  254. package/dist/storage/types.js.map +1 -0
  255. package/dist/storage/versions-since.d.ts +72 -0
  256. package/dist/storage/versions-since.js +92 -0
  257. package/dist/storage/versions-since.js.map +1 -0
  258. package/dist/storage-postgres/adapter.d.ts +17 -0
  259. package/dist/storage-postgres/adapter.js +782 -0
  260. package/dist/storage-postgres/adapter.js.map +1 -0
  261. package/dist/storage-postgres/compile-postgres.d.ts +22 -0
  262. package/dist/storage-postgres/compile-postgres.js +830 -0
  263. package/dist/storage-postgres/compile-postgres.js.map +1 -0
  264. package/dist/storage-postgres/errors.d.ts +34 -0
  265. package/dist/storage-postgres/errors.js +50 -0
  266. package/dist/storage-postgres/errors.js.map +1 -0
  267. package/dist/storage-postgres/migrations/0001_init.sql +113 -0
  268. package/dist/storage-postgres/migrations/0002_content_hash.sql +7 -0
  269. package/dist/storage-postgres/migrations/index.d.ts +22 -0
  270. package/dist/storage-postgres/migrations/index.js +70 -0
  271. package/dist/storage-postgres/migrations/index.js.map +1 -0
  272. package/dist/storage-sqlite/adapter.d.ts +2 -0
  273. package/dist/storage-sqlite/adapter.js +688 -0
  274. package/dist/storage-sqlite/adapter.js.map +1 -0
  275. package/dist/storage-sqlite/compile-filter.d.ts +41 -0
  276. package/dist/storage-sqlite/compile-filter.js +881 -0
  277. package/dist/storage-sqlite/compile-filter.js.map +1 -0
  278. package/dist/storage-sqlite/compile-sqlite.d.ts +19 -0
  279. package/dist/storage-sqlite/compile-sqlite.js +176 -0
  280. package/dist/storage-sqlite/compile-sqlite.js.map +1 -0
  281. package/dist/storage-sqlite/migrations/0001_init.sql +123 -0
  282. package/dist/storage-sqlite/migrations/0002_content_hash.sql +7 -0
  283. package/dist/storage-sqlite/migrations/index.d.ts +6 -0
  284. package/dist/storage-sqlite/migrations/index.js +41 -0
  285. package/dist/storage-sqlite/migrations/index.js.map +1 -0
  286. package/dist/storage-sqlite/vec.d.ts +40 -0
  287. package/dist/storage-sqlite/vec.js +62 -0
  288. package/dist/storage-sqlite/vec.js.map +1 -0
  289. package/dist/sync/cursor.d.ts +36 -0
  290. package/dist/sync/cursor.js +66 -0
  291. package/dist/sync/cursor.js.map +1 -0
  292. package/dist/sync/daemon.d.ts +40 -0
  293. package/dist/sync/daemon.js +202 -0
  294. package/dist/sync/daemon.js.map +1 -0
  295. package/dist/sync/feed.d.ts +38 -0
  296. package/dist/sync/feed.js +125 -0
  297. package/dist/sync/feed.js.map +1 -0
  298. package/dist/sync/fs-store.d.ts +8 -0
  299. package/dist/sync/fs-store.js +76 -0
  300. package/dist/sync/fs-store.js.map +1 -0
  301. package/dist/sync/intrinsics.d.ts +52 -0
  302. package/dist/sync/intrinsics.js +76 -0
  303. package/dist/sync/intrinsics.js.map +1 -0
  304. package/dist/sync/paths.d.ts +38 -0
  305. package/dist/sync/paths.js +56 -0
  306. package/dist/sync/paths.js.map +1 -0
  307. package/dist/sync/push.d.ts +48 -0
  308. package/dist/sync/push.js +328 -0
  309. package/dist/sync/push.js.map +1 -0
  310. package/dist/sync/reconcile.d.ts +54 -0
  311. package/dist/sync/reconcile.js +252 -0
  312. package/dist/sync/reconcile.js.map +1 -0
  313. package/dist/sync/run.d.ts +27 -0
  314. package/dist/sync/run.js +46 -0
  315. package/dist/sync/run.js.map +1 -0
  316. package/dist/version.d.ts +2 -0
  317. package/dist/version.js +5 -0
  318. package/dist/version.js.map +1 -0
  319. package/package.json +80 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * OIDC verification — auth-shell plan §1 (authn front 2), WS5.
3
+ *
4
+ * Verify a caller's JWT against the IdP's JWKS (issuer + audience pinned in
5
+ * shell config), then match the verified claims to a policy principal by its
6
+ * `oidc.sub` / `oidc.email` binding. Where the matched principal has no static
7
+ * `author`, derive one from the token as `name <email>` — the convention and
8
+ * the credential line up by construction (plan §3).
9
+ *
10
+ * Shared by the HTTP front and `mcp-stdio`'s `MRPLEX_SHELL_TOKEN` front: both
11
+ * hand a bearer JWT here and get back a principal id + author. The shell is the
12
+ * OAuth *resource server*; the authorization-server role belongs entirely to
13
+ * the IdP (this is also the on-ramp to MCP's OAuth 2.1 story).
14
+ */
15
+ import type { Policy } from "./policy.js";
16
+ export type OidcConfig = {
17
+ /** Pinned issuer (`iss` claim must match). */
18
+ issuer: string;
19
+ /** Pinned audience (`aud` claim must include this). */
20
+ audience: string;
21
+ /** JWKS endpoint URL. Defaults to `${issuer}/.well-known/jwks.json` if absent. */
22
+ jwksUri?: string;
23
+ };
24
+ /** The subset of verified claims the shell binds/derives identity from. */
25
+ export type VerifiedClaims = {
26
+ sub: string;
27
+ email?: string;
28
+ /** OIDC `email_verified` — whether the IdP has verified ownership of `email`. */
29
+ email_verified?: boolean;
30
+ name?: string;
31
+ };
32
+ /** Resolution of a verified token: which principal, and the author to stamp. */
33
+ export type OidcResolution = {
34
+ principalId: string;
35
+ author: string;
36
+ };
37
+ export declare class OidcError extends Error {
38
+ constructor(message: string);
39
+ }
40
+ /**
41
+ * A verifier bound to one IdP. Construction sets up the (cached, auto-rotating)
42
+ * remote JWKS; `verify()` checks a token and returns its claims. Kept as a
43
+ * factory so the JWKS fetcher is created once and reused across requests.
44
+ */
45
+ export type OidcVerifier = {
46
+ verify: (jwt: string) => Promise<VerifiedClaims>;
47
+ };
48
+ export declare function createOidcVerifier(config: OidcConfig): OidcVerifier;
49
+ /**
50
+ * Match verified claims to a policy principal and resolve the author. A
51
+ * principal binds via `oidc.sub` (exact) or `oidc.email` (exact); `sub` wins
52
+ * when both could match. The author is the principal's static `author` if set,
53
+ * else derived as `${name} <${email}>` (or just `<email>` when the token
54
+ * carries no display name). Throws `OidcError` when no principal binds or no
55
+ * author can be formed.
56
+ *
57
+ * Email binding requires `email_verified === true`: an IdP that lets a user set
58
+ * an arbitrary unverified `email` (or exposes federated emails without
59
+ * re-verification) would otherwise let a hostile user claim another principal's
60
+ * grants. `sub` binding is unaffected — it's issuer-namespaced and IdP-controlled.
61
+ */
62
+ export declare function resolvePrincipalFromClaims(policy: Policy, claims: VerifiedClaims): OidcResolution;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * OIDC verification — auth-shell plan §1 (authn front 2), WS5.
3
+ *
4
+ * Verify a caller's JWT against the IdP's JWKS (issuer + audience pinned in
5
+ * shell config), then match the verified claims to a policy principal by its
6
+ * `oidc.sub` / `oidc.email` binding. Where the matched principal has no static
7
+ * `author`, derive one from the token as `name <email>` — the convention and
8
+ * the credential line up by construction (plan §3).
9
+ *
10
+ * Shared by the HTTP front and `mcp-stdio`'s `MRPLEX_SHELL_TOKEN` front: both
11
+ * hand a bearer JWT here and get back a principal id + author. The shell is the
12
+ * OAuth *resource server*; the authorization-server role belongs entirely to
13
+ * the IdP (this is also the on-ramp to MCP's OAuth 2.1 story).
14
+ */
15
+ import { createRemoteJWKSet, jwtVerify } from "jose";
16
+ export class OidcError extends Error {
17
+ constructor(message) {
18
+ super(message);
19
+ this.name = "OidcError";
20
+ }
21
+ }
22
+ export function createOidcVerifier(config) {
23
+ const jwksUri = config.jwksUri ?? `${config.issuer.replace(/\/$/, "")}/.well-known/jwks.json`;
24
+ const jwks = createRemoteJWKSet(new URL(jwksUri));
25
+ return {
26
+ verify: async (jwt) => {
27
+ let payload;
28
+ try {
29
+ const result = await jwtVerify(jwt, jwks, {
30
+ issuer: config.issuer,
31
+ audience: config.audience,
32
+ });
33
+ payload = result.payload;
34
+ }
35
+ catch (err) {
36
+ throw new OidcError(`token verification failed: ${err.message}`);
37
+ }
38
+ const sub = payload.sub;
39
+ if (typeof sub !== "string" || sub.length === 0) {
40
+ throw new OidcError("token has no `sub` claim");
41
+ }
42
+ const claims = { sub };
43
+ if (typeof payload.email === "string")
44
+ claims.email = payload.email;
45
+ // Some IdPs emit email_verified as the string "true"/"false"; accept both.
46
+ if (typeof payload.email_verified === "boolean") {
47
+ claims.email_verified = payload.email_verified;
48
+ }
49
+ else if (payload.email_verified === "true" || payload.email_verified === "false") {
50
+ claims.email_verified = payload.email_verified === "true";
51
+ }
52
+ if (typeof payload.name === "string")
53
+ claims.name = payload.name;
54
+ return claims;
55
+ },
56
+ };
57
+ }
58
+ /**
59
+ * Match verified claims to a policy principal and resolve the author. A
60
+ * principal binds via `oidc.sub` (exact) or `oidc.email` (exact); `sub` wins
61
+ * when both could match. The author is the principal's static `author` if set,
62
+ * else derived as `${name} <${email}>` (or just `<email>` when the token
63
+ * carries no display name). Throws `OidcError` when no principal binds or no
64
+ * author can be formed.
65
+ *
66
+ * Email binding requires `email_verified === true`: an IdP that lets a user set
67
+ * an arbitrary unverified `email` (or exposes federated emails without
68
+ * re-verification) would otherwise let a hostile user claim another principal's
69
+ * grants. `sub` binding is unaffected — it's issuer-namespaced and IdP-controlled.
70
+ */
71
+ export function resolvePrincipalFromClaims(policy, claims) {
72
+ let matchBySub;
73
+ let matchByEmail;
74
+ const emailUsable = claims.email !== undefined && claims.email_verified === true;
75
+ for (const [id, principal] of Object.entries(policy.principals)) {
76
+ const binding = principal.oidc;
77
+ if (!binding)
78
+ continue;
79
+ if (binding.sub !== undefined && binding.sub === claims.sub)
80
+ matchBySub = id;
81
+ if (emailUsable && binding.email !== undefined && binding.email === claims.email) {
82
+ matchByEmail = id;
83
+ }
84
+ }
85
+ const principalId = matchBySub ?? matchByEmail;
86
+ if (principalId === undefined) {
87
+ throw new OidcError("no policy principal binds this token's sub/email");
88
+ }
89
+ const principal = policy.principals[principalId];
90
+ const author = principal?.author ?? deriveAuthor(claims);
91
+ if (author === undefined) {
92
+ throw new OidcError(`principal "${principalId}" has no static author and the token carries no email to derive one`);
93
+ }
94
+ return { principalId, author };
95
+ }
96
+ /** `name <email>` / `<email>` from claims, or undefined when there's no email. */
97
+ function deriveAuthor(claims) {
98
+ if (claims.email === undefined)
99
+ return undefined;
100
+ return claims.name !== undefined ? `${claims.name} <${claims.email}>` : `<${claims.email}>`;
101
+ }
102
+ //# sourceMappingURL=oidc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.js","sourceRoot":"","sources":["../../src/shell/oidc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Policy + entitlement — auth-shell plan §1, §3, WS1.
3
+ *
4
+ * The shell's one stable internal seam is the compiled `Entitlement`: every
5
+ * authn front, whatever its mechanism (API key today, OIDC tomorrow, capability
6
+ * token someday), resolves a credential to a principal id and then to this
7
+ * tuple, and the guard consumes *only* this tuple. Neither side knows the other
8
+ * exists.
9
+ *
10
+ * A declarative YAML policy file names principals, roles, and grants. A
11
+ * **grant** — `{ repo, read?, write? }` — is the human-facing vocabulary: it
12
+ * pairs a repo pattern with path globs per direction. `compile()` splits each
13
+ * grant into the entitlement's two `ScopeClaim` lists — read globs feed
14
+ * `Entitlement.read` (forwarded to the engine as `ctx.scope`), write globs feed
15
+ * `Entitlement.write` (enforced BY THE SHELL against write-op paths). One glob
16
+ * dialect throughout (the engine's `kernel/auth/glob.ts`), no deny rules beyond
17
+ * `!` negation inside a list.
18
+ *
19
+ * `compile(policy, principalId)` is a pure function — no I/O — so it is trivially
20
+ * unit-testable and so a future policy store (SQLite, an IdP's groups, capability
21
+ * tokens) can replace the loader behind it without the guard or the fronts
22
+ * noticing (plan decision 3).
23
+ */
24
+ import type { ScopeClaim } from "../kernel/context.js";
25
+ /**
26
+ * The compiled contract the guard consumes. Fronts compile *to* it; the guard
27
+ * reads *only* it. Stable across authn mechanisms (plan decision 2).
28
+ */
29
+ export type Entitlement = {
30
+ /** Identity the shell stamps on writes (`ctx.author`), derived from the credential. */
31
+ author: string;
32
+ /** Read visibility — forwarded verbatim as `ctx.scope`. */
33
+ read: ScopeClaim[];
34
+ /** Enforced BY THE SHELL against write-op target paths. */
35
+ write: ScopeClaim[];
36
+ /** May run repos.create/rename/delete/set_path_config/set_link_config + link mutations. */
37
+ destructive: boolean;
38
+ /** May supply a caller-chosen author instead of the derived one. */
39
+ impersonate: boolean;
40
+ };
41
+ /**
42
+ * A grant names both directions together: a repo pattern plus optional read
43
+ * and write path globs. Omitting a direction grants nothing in it.
44
+ */
45
+ export type Grant = {
46
+ repo: string | string[];
47
+ read?: string | string[];
48
+ write?: string | string[];
49
+ };
50
+ /** A role is a grant bundle plus the two op-level booleans. */
51
+ export type Role = {
52
+ grants: Grant[];
53
+ destructive?: boolean;
54
+ impersonate?: boolean;
55
+ };
56
+ /** OIDC binding — how a verified token's claims match this principal. */
57
+ export type OidcBinding = {
58
+ email?: string;
59
+ sub?: string;
60
+ };
61
+ export type Principal = {
62
+ /** Static author. Required unless `oidc` is present (then derived at login). */
63
+ author?: string;
64
+ roles: string[];
65
+ /** `sha256:<hex>` API-key hashes. Issuance/revocation is a diff to this file. */
66
+ keys?: string[];
67
+ oidc?: OidcBinding;
68
+ };
69
+ export type Policy = {
70
+ roles: Record<string, Role>;
71
+ principals: Record<string, Principal>;
72
+ };
73
+ /**
74
+ * A policy-file or compile error. Distinct from `KernelError` — this is a
75
+ * startup/config fault (or an operator's `policy check`), not a per-call
76
+ * outcome handed back to an untrusted client.
77
+ */
78
+ export declare class PolicyError extends Error {
79
+ constructor(message: string);
80
+ }
81
+ /**
82
+ * Parse YAML policy text into a validated `Policy`. Uses the `yaml` package's
83
+ * default core-schema parse (YAML 1.2 — no 1.1 implicit-typing footguns), then
84
+ * checks structure with precise, path-prefixed errors. Throws `PolicyError`.
85
+ */
86
+ export declare function parsePolicy(text: string): Policy;
87
+ /** Read + parse + validate a policy file. Throws `PolicyError` on any fault. */
88
+ export declare function loadPolicyFile(path: string): Policy;
89
+ /**
90
+ * Resolve a principal to its `Entitlement` — the union of its roles' grants and
91
+ * op booleans. Pure: no I/O, no clock, no globals. A principal's read/write
92
+ * claim lists are the concatenation of every role's grants (union semantics,
93
+ * §8.2); `destructive`/`impersonate` are OR'd across roles.
94
+ *
95
+ * `author` is `principal.author` when set. For an OIDC principal with no static
96
+ * author, the front derives `name <email>` from claims and passes it as
97
+ * `derivedAuthor`. If neither is available, this throws — a write with no
98
+ * attributable author is never stamped with the engine default here.
99
+ */
100
+ export declare function compile(policy: Policy, principalId: string, derivedAuthor?: string): Entitlement;
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Policy + entitlement — auth-shell plan §1, §3, WS1.
3
+ *
4
+ * The shell's one stable internal seam is the compiled `Entitlement`: every
5
+ * authn front, whatever its mechanism (API key today, OIDC tomorrow, capability
6
+ * token someday), resolves a credential to a principal id and then to this
7
+ * tuple, and the guard consumes *only* this tuple. Neither side knows the other
8
+ * exists.
9
+ *
10
+ * A declarative YAML policy file names principals, roles, and grants. A
11
+ * **grant** — `{ repo, read?, write? }` — is the human-facing vocabulary: it
12
+ * pairs a repo pattern with path globs per direction. `compile()` splits each
13
+ * grant into the entitlement's two `ScopeClaim` lists — read globs feed
14
+ * `Entitlement.read` (forwarded to the engine as `ctx.scope`), write globs feed
15
+ * `Entitlement.write` (enforced BY THE SHELL against write-op paths). One glob
16
+ * dialect throughout (the engine's `kernel/auth/glob.ts`), no deny rules beyond
17
+ * `!` negation inside a list.
18
+ *
19
+ * `compile(policy, principalId)` is a pure function — no I/O — so it is trivially
20
+ * unit-testable and so a future policy store (SQLite, an IdP's groups, capability
21
+ * tokens) can replace the loader behind it without the guard or the fronts
22
+ * noticing (plan decision 3).
23
+ */
24
+ import { readFileSync } from "node:fs";
25
+ import { parse as parseYaml } from "yaml";
26
+ /**
27
+ * A policy-file or compile error. Distinct from `KernelError` — this is a
28
+ * startup/config fault (or an operator's `policy check`), not a per-call
29
+ * outcome handed back to an untrusted client.
30
+ */
31
+ export class PolicyError extends Error {
32
+ constructor(message) {
33
+ super(message);
34
+ this.name = "PolicyError";
35
+ }
36
+ }
37
+ // -----------------------------------------------------------------------------
38
+ // Parse + validate.
39
+ // -----------------------------------------------------------------------------
40
+ const isString = (v) => typeof v === "string";
41
+ const isStringList = (v) => Array.isArray(v) && v.every(isString);
42
+ const isStrOrStrList = (v) => isString(v) || isStringList(v);
43
+ const isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
44
+ /**
45
+ * Parse YAML policy text into a validated `Policy`. Uses the `yaml` package's
46
+ * default core-schema parse (YAML 1.2 — no 1.1 implicit-typing footguns), then
47
+ * checks structure with precise, path-prefixed errors. Throws `PolicyError`.
48
+ */
49
+ export function parsePolicy(text) {
50
+ let raw;
51
+ try {
52
+ raw = parseYaml(text);
53
+ }
54
+ catch (err) {
55
+ throw new PolicyError(`policy is not valid YAML: ${err.message}`);
56
+ }
57
+ if (!isObject(raw)) {
58
+ throw new PolicyError("policy must be a mapping with `roles` and `principals`");
59
+ }
60
+ const roles = validateRoles(raw.roles);
61
+ const principals = validatePrincipals(raw.principals, roles);
62
+ return { roles, principals };
63
+ }
64
+ /** Read + parse + validate a policy file. Throws `PolicyError` on any fault. */
65
+ export function loadPolicyFile(path) {
66
+ let text;
67
+ try {
68
+ text = readFileSync(path, "utf8");
69
+ }
70
+ catch (err) {
71
+ throw new PolicyError(`cannot read policy file ${path}: ${err.message}`);
72
+ }
73
+ return parsePolicy(text);
74
+ }
75
+ function validateGrant(g, where) {
76
+ if (!isObject(g))
77
+ throw new PolicyError(`${where} must be a mapping`);
78
+ if (!isStrOrStrList(g.repo)) {
79
+ throw new PolicyError(`${where}.repo must be a string or string[]`);
80
+ }
81
+ if (g.read !== undefined && !isStrOrStrList(g.read)) {
82
+ throw new PolicyError(`${where}.read must be a string or string[]`);
83
+ }
84
+ if (g.write !== undefined && !isStrOrStrList(g.write)) {
85
+ throw new PolicyError(`${where}.write must be a string or string[]`);
86
+ }
87
+ const grant = { repo: g.repo };
88
+ if (g.read !== undefined)
89
+ grant.read = g.read;
90
+ if (g.write !== undefined)
91
+ grant.write = g.write;
92
+ return grant;
93
+ }
94
+ function validateRoles(raw) {
95
+ if (raw === undefined)
96
+ return {};
97
+ if (!isObject(raw))
98
+ throw new PolicyError("`roles` must be a mapping of role name → role");
99
+ const roles = {};
100
+ for (const [name, value] of Object.entries(raw)) {
101
+ const where = `roles.${name}`;
102
+ if (!isObject(value))
103
+ throw new PolicyError(`${where} must be a mapping`);
104
+ if (value.grants !== undefined && !Array.isArray(value.grants)) {
105
+ throw new PolicyError(`${where}.grants must be a list`);
106
+ }
107
+ const grantsRaw = (value.grants ?? []);
108
+ const grants = grantsRaw.map((g, i) => validateGrant(g, `${where}.grants[${i}]`));
109
+ if (value.destructive !== undefined && typeof value.destructive !== "boolean") {
110
+ throw new PolicyError(`${where}.destructive must be a boolean`);
111
+ }
112
+ if (value.impersonate !== undefined && typeof value.impersonate !== "boolean") {
113
+ throw new PolicyError(`${where}.impersonate must be a boolean`);
114
+ }
115
+ const role = { grants };
116
+ if (value.destructive !== undefined)
117
+ role.destructive = value.destructive;
118
+ if (value.impersonate !== undefined)
119
+ role.impersonate = value.impersonate;
120
+ roles[name] = role;
121
+ }
122
+ return roles;
123
+ }
124
+ function validatePrincipals(raw, roles) {
125
+ if (!isObject(raw)) {
126
+ throw new PolicyError("`principals` must be a mapping of principal id → principal");
127
+ }
128
+ const principals = {};
129
+ for (const [id, value] of Object.entries(raw)) {
130
+ const where = `principals.${id}`;
131
+ if (!isObject(value))
132
+ throw new PolicyError(`${where} must be a mapping`);
133
+ if (value.author !== undefined && !isString(value.author)) {
134
+ throw new PolicyError(`${where}.author must be a string`);
135
+ }
136
+ if (!isStringList(value.roles)) {
137
+ throw new PolicyError(`${where}.roles must be a list of role names`);
138
+ }
139
+ for (const r of value.roles) {
140
+ if (!(r in roles)) {
141
+ throw new PolicyError(`${where}.roles references unknown role "${r}"`);
142
+ }
143
+ }
144
+ if (value.keys !== undefined && !isStringList(value.keys)) {
145
+ throw new PolicyError(`${where}.keys must be a list of "sha256:<hex>" strings`);
146
+ }
147
+ for (const k of value.keys ?? []) {
148
+ if (!/^sha256:[0-9a-f]{64}$/.test(k)) {
149
+ throw new PolicyError(`${where}.keys entry "${k}" must be "sha256:<64 hex chars>"`);
150
+ }
151
+ }
152
+ let oidc;
153
+ if (value.oidc !== undefined) {
154
+ if (!isObject(value.oidc))
155
+ throw new PolicyError(`${where}.oidc must be a mapping`);
156
+ if (value.oidc.email !== undefined && !isString(value.oidc.email)) {
157
+ throw new PolicyError(`${where}.oidc.email must be a string`);
158
+ }
159
+ if (value.oidc.sub !== undefined && !isString(value.oidc.sub)) {
160
+ throw new PolicyError(`${where}.oidc.sub must be a string`);
161
+ }
162
+ oidc = {};
163
+ if (value.oidc.email !== undefined)
164
+ oidc.email = value.oidc.email;
165
+ if (value.oidc.sub !== undefined)
166
+ oidc.sub = value.oidc.sub;
167
+ }
168
+ // A principal must have an author to stamp writes with, OR an OIDC binding
169
+ // that lets one be derived from claims at login (plan §3). A key-only
170
+ // principal with no author has no identity to attribute — reject it here,
171
+ // where the operator can see it, rather than stamping the engine default.
172
+ if (value.author === undefined && oidc === undefined) {
173
+ throw new PolicyError(`${where} needs an \`author\` or an \`oidc\` binding to derive one`);
174
+ }
175
+ const principal = { roles: value.roles };
176
+ if (value.author !== undefined)
177
+ principal.author = value.author;
178
+ if (value.keys !== undefined)
179
+ principal.keys = value.keys;
180
+ if (oidc !== undefined)
181
+ principal.oidc = oidc;
182
+ principals[id] = principal;
183
+ }
184
+ return principals;
185
+ }
186
+ // -----------------------------------------------------------------------------
187
+ // Compile — the pure policy-evaluation function.
188
+ // -----------------------------------------------------------------------------
189
+ /**
190
+ * Resolve a principal to its `Entitlement` — the union of its roles' grants and
191
+ * op booleans. Pure: no I/O, no clock, no globals. A principal's read/write
192
+ * claim lists are the concatenation of every role's grants (union semantics,
193
+ * §8.2); `destructive`/`impersonate` are OR'd across roles.
194
+ *
195
+ * `author` is `principal.author` when set. For an OIDC principal with no static
196
+ * author, the front derives `name <email>` from claims and passes it as
197
+ * `derivedAuthor`. If neither is available, this throws — a write with no
198
+ * attributable author is never stamped with the engine default here.
199
+ */
200
+ export function compile(policy, principalId, derivedAuthor) {
201
+ const principal = policy.principals[principalId];
202
+ if (!principal) {
203
+ throw new PolicyError(`unknown principal "${principalId}"`);
204
+ }
205
+ const read = [];
206
+ const write = [];
207
+ let destructive = false;
208
+ let impersonate = false;
209
+ for (const roleName of principal.roles) {
210
+ const role = policy.roles[roleName];
211
+ // validatePrincipals already checked role existence; guard anyway so a
212
+ // hand-built Policy object can't slip an unknown role past compile.
213
+ if (!role)
214
+ throw new PolicyError(`principal "${principalId}" references unknown role "${roleName}"`);
215
+ if (role.destructive)
216
+ destructive = true;
217
+ if (role.impersonate)
218
+ impersonate = true;
219
+ for (const grant of role.grants) {
220
+ if (grant.read !== undefined)
221
+ read.push({ repo: grant.repo, paths: grant.read });
222
+ if (grant.write !== undefined)
223
+ write.push({ repo: grant.repo, paths: grant.write });
224
+ }
225
+ }
226
+ const author = principal.author ?? derivedAuthor;
227
+ if (author === undefined) {
228
+ throw new PolicyError(`principal "${principalId}" has no static author and none was derived (OIDC login supplies it)`);
229
+ }
230
+ return { author, read, write, destructive, impersonate };
231
+ }
232
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/shell/policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Route-aware REST policy classification for proxy mode — auth-shell plan §1
3
+ * "Serve mode: fronting proxy", WS4.
4
+ *
5
+ * The fronting proxy is mrplex-aware (not a generic reverse proxy): it parses
6
+ * the known REST routes to decide what an entitlement must permit. Crucially,
7
+ * on the REST surface the URL path is *authoritative* for writes — a
8
+ * `PUT /repos/notes/docs/drafts/x.md` names its target path directly, and a
9
+ * MOVE carries source (URL) + destination (`Destination` header). So the proxy
10
+ * can replay the guard's §8.2 rules (both-endpoints on move, sigil paths
11
+ * skipped, write ≠ read) WITHOUT a kernel handle — unlike the opaque MCP tool
12
+ * args, which is why MCP writes stay in embedded mode.
13
+ *
14
+ * This module is pure request classification — no I/O, no forwarding — so it is
15
+ * unit-testable and the proxy transport composes it.
16
+ */
17
+ /** What an incoming REST request requires of the caller's entitlement. */
18
+ export type RouteRequirement = {
19
+ kind: "read";
20
+ } | {
21
+ kind: "write";
22
+ repo: string;
23
+ paths: string[];
24
+ } | {
25
+ kind: "destructive";
26
+ } | {
27
+ kind: "unknown";
28
+ };
29
+ /**
30
+ * Classify a REST request into its policy requirement. `pathname` is the raw
31
+ * URL path (no query); `destination` is the `Destination` header value, needed
32
+ * for MOVE both-endpoints. Percent-encoded segments are decoded per-segment.
33
+ */
34
+ export declare function classifyRestRequest(method: string, pathname: string, destination: string | undefined): RouteRequirement;