@synoikia/core 0.1.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 (209) hide show
  1. package/LICENSE +21 -0
  2. package/dist/app.d.ts +79 -0
  3. package/dist/app.js +155 -0
  4. package/dist/app.js.map +1 -0
  5. package/dist/approvals/links.d.ts +23 -0
  6. package/dist/approvals/links.js +49 -0
  7. package/dist/approvals/links.js.map +1 -0
  8. package/dist/approvals/service.d.ts +110 -0
  9. package/dist/approvals/service.js +182 -0
  10. package/dist/approvals/service.js.map +1 -0
  11. package/dist/audit-query.d.ts +52 -0
  12. package/dist/audit-query.js +116 -0
  13. package/dist/audit-query.js.map +1 -0
  14. package/dist/audit.d.ts +5 -0
  15. package/dist/audit.js +8 -0
  16. package/dist/audit.js.map +1 -0
  17. package/dist/auth/mcp-auth.d.ts +50 -0
  18. package/dist/auth/mcp-auth.js +160 -0
  19. package/dist/auth/mcp-auth.js.map +1 -0
  20. package/dist/auth/mcp-tokens.d.ts +57 -0
  21. package/dist/auth/mcp-tokens.js +143 -0
  22. package/dist/auth/mcp-tokens.js.map +1 -0
  23. package/dist/auth/oauth.d.ts +188 -0
  24. package/dist/auth/oauth.js +470 -0
  25. package/dist/auth/oauth.js.map +1 -0
  26. package/dist/auth/oidc.d.ts +110 -0
  27. package/dist/auth/oidc.js +221 -0
  28. package/dist/auth/oidc.js.map +1 -0
  29. package/dist/auth/sessions.d.ts +34 -0
  30. package/dist/auth/sessions.js +83 -0
  31. package/dist/auth/sessions.js.map +1 -0
  32. package/dist/auth/throttle.d.ts +31 -0
  33. package/dist/auth/throttle.js +52 -0
  34. package/dist/auth/throttle.js.map +1 -0
  35. package/dist/auth/tokens.d.ts +7 -0
  36. package/dist/auth/tokens.js +40 -0
  37. package/dist/auth/tokens.js.map +1 -0
  38. package/dist/auth/totp.d.ts +14 -0
  39. package/dist/auth/totp.js +81 -0
  40. package/dist/auth/totp.js.map +1 -0
  41. package/dist/auth/users.d.ts +70 -0
  42. package/dist/auth/users.js +296 -0
  43. package/dist/auth/users.js.map +1 -0
  44. package/dist/catalog/groups.d.ts +102 -0
  45. package/dist/catalog/groups.js +363 -0
  46. package/dist/catalog/groups.js.map +1 -0
  47. package/dist/catalog/registry.d.ts +25 -0
  48. package/dist/catalog/registry.js +80 -0
  49. package/dist/catalog/registry.js.map +1 -0
  50. package/dist/catalog/rules.d.ts +128 -0
  51. package/dist/catalog/rules.js +178 -0
  52. package/dist/catalog/rules.js.map +1 -0
  53. package/dist/catalog/sync.d.ts +29 -0
  54. package/dist/catalog/sync.js +222 -0
  55. package/dist/catalog/sync.js.map +1 -0
  56. package/dist/cli.d.ts +3 -0
  57. package/dist/cli.js +83 -0
  58. package/dist/cli.js.map +1 -0
  59. package/dist/config/env.d.ts +40 -0
  60. package/dist/config/env.js +71 -0
  61. package/dist/config/env.js.map +1 -0
  62. package/dist/crypto/index.d.ts +28 -0
  63. package/dist/crypto/index.js +88 -0
  64. package/dist/crypto/index.js.map +1 -0
  65. package/dist/crypto/rotate.d.ts +15 -0
  66. package/dist/crypto/rotate.js +61 -0
  67. package/dist/crypto/rotate.js.map +1 -0
  68. package/dist/db/index.d.ts +17 -0
  69. package/dist/db/index.js +29 -0
  70. package/dist/db/index.js.map +1 -0
  71. package/dist/db/schema.d.ts +4389 -0
  72. package/dist/db/schema.js +372 -0
  73. package/dist/db/schema.js.map +1 -0
  74. package/dist/endpoints/slug.d.ts +3 -0
  75. package/dist/endpoints/slug.js +7 -0
  76. package/dist/endpoints/slug.js.map +1 -0
  77. package/dist/errors.d.ts +16 -0
  78. package/dist/errors.js +29 -0
  79. package/dist/errors.js.map +1 -0
  80. package/dist/events.d.ts +43 -0
  81. package/dist/events.js +16 -0
  82. package/dist/events.js.map +1 -0
  83. package/dist/gate/access.d.ts +47 -0
  84. package/dist/gate/access.js +48 -0
  85. package/dist/gate/access.js.map +1 -0
  86. package/dist/gate/attestation.d.ts +23 -0
  87. package/dist/gate/attestation.js +33 -0
  88. package/dist/gate/attestation.js.map +1 -0
  89. package/dist/gate/canonical.d.ts +3 -0
  90. package/dist/gate/canonical.js +11 -0
  91. package/dist/gate/canonical.js.map +1 -0
  92. package/dist/gate/match.d.ts +67 -0
  93. package/dist/gate/match.js +133 -0
  94. package/dist/gate/match.js.map +1 -0
  95. package/dist/gate/pipeline.d.ts +61 -0
  96. package/dist/gate/pipeline.js +284 -0
  97. package/dist/gate/pipeline.js.map +1 -0
  98. package/dist/gate/preapproval.d.ts +24 -0
  99. package/dist/gate/preapproval.js +48 -0
  100. package/dist/gate/preapproval.js.map +1 -0
  101. package/dist/gate/rate-limit.d.ts +35 -0
  102. package/dist/gate/rate-limit.js +82 -0
  103. package/dist/gate/rate-limit.js.map +1 -0
  104. package/dist/gate/redact.d.ts +23 -0
  105. package/dist/gate/redact.js +87 -0
  106. package/dist/gate/redact.js.map +1 -0
  107. package/dist/http/admin/auth.d.ts +33 -0
  108. package/dist/http/admin/auth.js +338 -0
  109. package/dist/http/admin/auth.js.map +1 -0
  110. package/dist/http/admin/instances.d.ts +5 -0
  111. package/dist/http/admin/instances.js +193 -0
  112. package/dist/http/admin/instances.js.map +1 -0
  113. package/dist/http/admin/system.d.ts +5 -0
  114. package/dist/http/admin/system.js +225 -0
  115. package/dist/http/admin/system.js.map +1 -0
  116. package/dist/http/admin-app.d.ts +9 -0
  117. package/dist/http/admin-app.js +51 -0
  118. package/dist/http/admin-app.js.map +1 -0
  119. package/dist/http/common.d.ts +32 -0
  120. package/dist/http/common.js +80 -0
  121. package/dist/http/common.js.map +1 -0
  122. package/dist/http/mcp/approval-routes.d.ts +11 -0
  123. package/dist/http/mcp/approval-routes.js +117 -0
  124. package/dist/http/mcp/approval-routes.js.map +1 -0
  125. package/dist/http/mcp/endpoint.d.ts +32 -0
  126. package/dist/http/mcp/endpoint.js +293 -0
  127. package/dist/http/mcp/endpoint.js.map +1 -0
  128. package/dist/http/mcp/oauth-routes.d.ts +29 -0
  129. package/dist/http/mcp/oauth-routes.js +465 -0
  130. package/dist/http/mcp/oauth-routes.js.map +1 -0
  131. package/dist/http/mcp/pages.d.ts +63 -0
  132. package/dist/http/mcp/pages.js +200 -0
  133. package/dist/http/mcp/pages.js.map +1 -0
  134. package/dist/http/mcp-app.d.ts +7 -0
  135. package/dist/http/mcp-app.js +30 -0
  136. package/dist/http/mcp-app.js.map +1 -0
  137. package/dist/instances/connection.d.ts +37 -0
  138. package/dist/instances/connection.js +109 -0
  139. package/dist/instances/connection.js.map +1 -0
  140. package/dist/instances/manager.d.ts +313 -0
  141. package/dist/instances/manager.js +600 -0
  142. package/dist/instances/manager.js.map +1 -0
  143. package/dist/instances/settings.d.ts +42 -0
  144. package/dist/instances/settings.js +57 -0
  145. package/dist/instances/settings.js.map +1 -0
  146. package/dist/lenient.d.ts +16 -0
  147. package/dist/lenient.js +51 -0
  148. package/dist/lenient.js.map +1 -0
  149. package/dist/lock.d.ts +28 -0
  150. package/dist/lock.js +77 -0
  151. package/dist/lock.js.map +1 -0
  152. package/dist/main.d.ts +1 -0
  153. package/dist/main.js +38 -0
  154. package/dist/main.js.map +1 -0
  155. package/dist/maintenance.d.ts +10 -0
  156. package/dist/maintenance.js +48 -0
  157. package/dist/maintenance.js.map +1 -0
  158. package/dist/notify/service.d.ts +149 -0
  159. package/dist/notify/service.js +358 -0
  160. package/dist/notify/service.js.map +1 -0
  161. package/dist/plugins/discovery.d.ts +39 -0
  162. package/dist/plugins/discovery.js +157 -0
  163. package/dist/plugins/discovery.js.map +1 -0
  164. package/dist/plugins/minisign.d.ts +25 -0
  165. package/dist/plugins/minisign.js +49 -0
  166. package/dist/plugins/minisign.js.map +1 -0
  167. package/dist/plugins/process.d.ts +55 -0
  168. package/dist/plugins/process.js +162 -0
  169. package/dist/plugins/process.js.map +1 -0
  170. package/dist/plugins/repos.d.ts +198 -0
  171. package/dist/plugins/repos.js +612 -0
  172. package/dist/plugins/repos.js.map +1 -0
  173. package/dist/plugins/supervisor.d.ts +50 -0
  174. package/dist/plugins/supervisor.js +125 -0
  175. package/dist/plugins/supervisor.js.map +1 -0
  176. package/dist/runtime/index.d.ts +28 -0
  177. package/dist/runtime/index.js +248 -0
  178. package/dist/runtime/index.js.map +1 -0
  179. package/dist/sandbox/index.d.ts +53 -0
  180. package/dist/sandbox/index.js +197 -0
  181. package/dist/sandbox/index.js.map +1 -0
  182. package/dist/server.d.ts +16 -0
  183. package/dist/server.js +36 -0
  184. package/dist/server.js.map +1 -0
  185. package/dist/settings.d.ts +43 -0
  186. package/dist/settings.js +99 -0
  187. package/dist/settings.js.map +1 -0
  188. package/dist/testing/index.d.ts +73 -0
  189. package/dist/testing/index.js +174 -0
  190. package/dist/testing/index.js.map +1 -0
  191. package/dist/version.d.ts +2 -0
  192. package/dist/version.js +4 -0
  193. package/dist/version.js.map +1 -0
  194. package/drizzle/0000_init.sql +334 -0
  195. package/drizzle/0001_totp_last_step.sql +1 -0
  196. package/drizzle/0002_access_levels.sql +16 -0
  197. package/drizzle/0003_drop_exclusion_flags.sql +2 -0
  198. package/drizzle/0004_grant_instance_ids.sql +1 -0
  199. package/drizzle/0005_rule_strict_miss.sql +1 -0
  200. package/drizzle/0006_attestation_waived.sql +1 -0
  201. package/drizzle/meta/0000_snapshot.json +2318 -0
  202. package/drizzle/meta/0001_snapshot.json +2325 -0
  203. package/drizzle/meta/0002_snapshot.json +2348 -0
  204. package/drizzle/meta/0003_snapshot.json +2332 -0
  205. package/drizzle/meta/0004_snapshot.json +2339 -0
  206. package/drizzle/meta/0005_snapshot.json +2346 -0
  207. package/drizzle/meta/0006_snapshot.json +2354 -0
  208. package/drizzle/meta/_journal.json +55 -0
  209. package/package.json +58 -0
@@ -0,0 +1,200 @@
1
+ import { html, raw } from 'hono/html';
2
+ // Synoikia tokens. The CSP allows no external fonts, so the brand faces apply only when installed
3
+ // locally; the stacks fall back to the closest system faces.
4
+ const STYLE = `
5
+ :root { color-scheme:light; --surface-100:#f7f1e8; --surface-200:#eee4d3; --surface-300:#e4d6bf; --border:#d9c9ab; --border-strong:#c2ac82;
6
+ --ink:#241b12; --ink-muted:#5c5044; --brand:#b5502f; --brand-strong:#8f3d22; --accent:#4f6b52; --on-brand:#fff;
7
+ --link:#8f3d22; --danger:#b23b3b; --danger-text:#9a2f2f; --danger-subtle:#f0dcda;
8
+ --font-display:"Fraunces", Georgia, "Times New Roman", serif; --font-sans:"IBM Plex Sans", system-ui, -apple-system, sans-serif;
9
+ --font-mono:"IBM Plex Mono", ui-monospace, "SF Mono", monospace; }
10
+ @media (prefers-color-scheme: dark) { :root { color-scheme:dark; --surface-100:#14110e; --surface-200:#1d1914; --surface-300:#262019;
11
+ --border:#3a3026; --border-strong:#4d4030; --ink:#f2e9db; --ink-muted:#b8ab98; --brand:#d97250; --brand-strong:#e88a63; --accent:#8fae8a;
12
+ --on-brand:#14110e; --link:#d97250; --danger:#e8685f; --danger-text:#e8685f; --danger-subtle:#2e1c1b; } }
13
+ * { box-sizing: border-box; }
14
+ body { margin:0; min-height:100vh; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:20px;
15
+ background:var(--surface-100); color:var(--ink); font:15px/1.45 var(--font-sans); padding:16px; }
16
+ .brand { display:flex; align-items:center; gap:10px; font-family:var(--font-display); font-weight:600; font-size:23px; line-height:1;
17
+ letter-spacing:-0.01em; } .brand .root { color:var(--brand); } .brand svg { display:block; }
18
+ .brand .frame { stroke:var(--ink-muted); } .brand .paths { stroke:var(--brand-strong); } .brand .dwellings { fill:var(--accent); }
19
+ .brand .hearth { fill:var(--brand); }
20
+ main { width:100%; max-width:460px; background:var(--surface-200); border:1px solid var(--border); border-radius:14px; padding:24px;
21
+ box-shadow:0 1px 2px rgb(36 27 18 / 8%); }
22
+ h1 { font-size:22px; line-height:28px; margin:0 0 4px; } p.sub { margin:0 0 18px; color:var(--ink-muted); }
23
+ a { color:var(--link); }
24
+ label { display:block; font-size:12px; line-height:16px; font-weight:600; color:var(--ink-muted); margin:14px 0 6px; }
25
+ input[type=text], input[type=password] { width:100%; padding:8px 12px; border:1px solid var(--border-strong); border-radius:8px; font:inherit;
26
+ background:var(--surface-100); color:inherit; }
27
+ input:focus { outline:2px solid var(--brand); outline-offset:-1px; border-color:var(--brand); }
28
+ input[type=checkbox], input[type=radio] { accent-color:var(--brand); }
29
+ :focus-visible { outline:2px solid var(--brand); outline-offset:2px; }
30
+ .row { display:flex; gap:8px; margin-top:18px; } .row > * { flex:1; }
31
+ button, a.btn { display:inline-block; text-align:center; padding:8px 16px; border-radius:8px; border:1px solid var(--border-strong);
32
+ background:var(--surface-200); color:var(--ink); font:inherit; font-weight:600; cursor:pointer; text-decoration:none; }
33
+ button:hover, a.btn:hover { background:var(--surface-300); }
34
+ button.primary { background:var(--brand); border-color:var(--brand); color:var(--on-brand); }
35
+ button.primary:hover { background:var(--brand-strong); border-color:var(--brand-strong); } button.danger { color:var(--danger-text); }
36
+ .error { color:var(--danger-text); margin:12px 0 0; } .muted { color:var(--ink-muted); font-size:13px; }
37
+ ul.endpoints { list-style:none; padding:0; margin:8px 0 0; } ul.endpoints li { padding:8px 0; border-top:1px solid var(--border); }
38
+ code, pre { font-family:var(--font-mono); font-size:12px; } pre { white-space:pre-wrap; word-break:break-word; background:var(--surface-100);
39
+ border:1px solid var(--border); border-radius:8px; padding:12px; max-height:260px; overflow:auto; }
40
+ .badge { font-size:12px; font-weight:600; padding:2px 10px; border-radius:999px; background:var(--danger-subtle); color:var(--danger-text);
41
+ vertical-align:middle; }
42
+ hr { border:none; border-top:1px solid var(--border); margin:18px 0; }
43
+ `;
44
+ /** The Synoikia mark (design system assets/Logo/synoikia-mark.svg), themed through the classes above. */
45
+ const BRAND = `<div class="brand"><svg viewBox="0 0 120 120" width="36" height="36" aria-hidden="true" focusable="false">
46
+ <rect class="frame" x="4" y="4" width="112" height="112" rx="24" fill="none" stroke-width="3" opacity="0.32"/>
47
+ <g class="paths" stroke-width="4" stroke-linecap="round" opacity="0.55"><line x1="60" y1="60" x2="60" y2="25"/>
48
+ <line x1="60" y1="60" x2="95" y2="60"/><line x1="60" y1="60" x2="60" y2="95"/><line x1="60" y1="60" x2="25" y2="60"/></g>
49
+ <g class="dwellings"><rect x="52" y="8" width="16" height="16" rx="4"/><rect x="96" y="52" width="16" height="16" rx="4"/>
50
+ <rect x="52" y="96" width="16" height="16" rx="4"/><rect x="8" y="52" width="16" height="16" rx="4"/></g>
51
+ <rect class="hearth" x="48" y="48" width="24" height="24" rx="6"/></svg><span>Syn<span class="root">oikia</span></span></div>`;
52
+ export function page(c, title, body, status = 200) {
53
+ c.header('Content-Security-Policy', "default-src 'none'; style-src 'unsafe-inline'; img-src data:; frame-ancestors 'none'; base-uri 'none'");
54
+ c.header('X-Frame-Options', 'DENY');
55
+ c.header('Referrer-Policy', 'no-referrer');
56
+ c.header('Cache-Control', 'no-store');
57
+ return c.html(html `<!doctype html>
58
+ <html lang="en">
59
+ <head>
60
+ <meta charset="utf-8" />
61
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
62
+ <title>${title} · Synoikia</title>
63
+ <style>
64
+ ${raw(STYLE)}
65
+ </style>
66
+ </head>
67
+ <body>
68
+ ${raw(BRAND)}
69
+ <main>${body}</main>
70
+ </body>
71
+ </html>`, status);
72
+ }
73
+ export function errorPage(c, title, message, status = 400) {
74
+ return page(c, title, html `<h1>${title}</h1>
75
+ <p class="sub">${message}</p>`, status);
76
+ }
77
+ export function loginPage(c, v, status = 200) {
78
+ return page(c, 'Sign in', html `<h1>Sign in</h1>
79
+ <p class="sub">${v.purpose}</p>
80
+ ${v.localLogin
81
+ ? html `<form method="post" action="/oauth/login">
82
+ <input type="hidden" name="csrf" value="${v.csrf}" />
83
+ <input type="hidden" name="continue" value="${v.continueTo}" />
84
+ <label for="u">Username</label
85
+ ><input id="u" type="text" name="username" autocomplete="username" autofocus required />
86
+ <label for="p">Password</label
87
+ ><input id="p" type="password" name="password" autocomplete="current-password" required />
88
+ <div class="row"><button class="primary" type="submit">Sign in</button></div>
89
+ </form>`
90
+ : ''}
91
+ ${v.oidc
92
+ ? html `${v.localLogin ? html `<hr />` : ''}
93
+ <a class="btn" style="width:100%" href="/oauth/login/oidc?continue=${encodeURIComponent(v.continueTo)}"
94
+ >Sign in with ${v.oidc.label}</a
95
+ >`
96
+ : ''}
97
+ ${v.error ? html `<p class="error" role="alert">${v.error}</p>` : ''}`, status);
98
+ }
99
+ export function totpPage(c, v, status = 200) {
100
+ return page(c, 'Two-factor code', html `<h1>Two-factor code</h1>
101
+ <p class="sub">Enter the code from your authenticator app, or a recovery code.</p>
102
+ <form method="post" action="/oauth/login/totp">
103
+ <input type="hidden" name="csrf" value="${v.csrf}" />
104
+ <input type="hidden" name="mfa" value="${v.mfa}" />
105
+ <input type="hidden" name="continue" value="${v.continueTo}" />
106
+ <label for="code">Code</label
107
+ ><input id="code" type="text" name="code" inputmode="numeric" autocomplete="one-time-code" autofocus required />
108
+ <div class="row"><button class="primary" type="submit">Continue</button></div>
109
+ </form>
110
+ ${v.error ? html `<p class="error" role="alert">${v.error}</p>` : ''}`, status);
111
+ }
112
+ export function consentPage(c, v) {
113
+ return page(c, 'Authorize access', html `<h1>Authorize ${v.clientName}</h1>
114
+ <p class="sub">
115
+ Signed in as <strong>${v.username}</strong>. After you approve, you'll be sent to
116
+ <code>${v.redirectHost}</code>.
117
+ </p>
118
+ <form method="post" action="/oauth/consent">
119
+ <input type="hidden" name="form" value="${v.formToken}" />
120
+ <label>Endpoints this client may use</label>
121
+ <ul class="endpoints">
122
+ ${v.endpoints.map((e) => html `<li>
123
+ <label style="display:flex;gap:8px;align-items:center;margin:0;color:inherit;font-weight:500">
124
+ <input type="checkbox" name="resource" value="${e.resource}" ${e.checked ? 'checked' : ''} />
125
+ <span><code>/${e.slug}</code> · ${e.name}</span>
126
+ </label>
127
+ </li>`)}
128
+ </ul>
129
+ <label>Access</label>
130
+ <ul class="endpoints">
131
+ <li>
132
+ <label style="display:flex;gap:8px;align-items:center;margin:0;color:inherit;font-weight:500">
133
+ <input type="radio" name="access" value="read" ${v.access === 'write' ? '' : 'checked'} />
134
+ <span><strong>Read only</strong> · this client can never call a write operation</span>
135
+ </label>
136
+ </li>
137
+ <li>
138
+ <label style="display:flex;gap:8px;align-items:center;margin:0;color:inherit;font-weight:500">
139
+ <input type="radio" name="access" value="write" ${v.access === 'write' ? 'checked' : ''} />
140
+ <span
141
+ ><strong>Read &amp; write</strong> · writes allowed where the endpoint's access levels allow them</span
142
+ >
143
+ </label>
144
+ </li>
145
+ </ul>
146
+ <p class="muted">
147
+ Tools still go through the endpoint's access levels and approvals. To change this later, revoke the client
148
+ under Clients & Tokens and connect again.
149
+ </p>
150
+ ${v.error ? html `<p class="error" role="alert">${v.error}</p>` : ''}
151
+ <div class="row">
152
+ <button class="danger" type="submit" name="decision" value="deny">Deny</button>
153
+ <button class="primary" type="submit" name="decision" value="approve">Approve</button>
154
+ </div>
155
+ </form>`);
156
+ }
157
+ const pretty = (v) => JSON.stringify(v, null, 2);
158
+ export function approvalPage(c, v, status = 200) {
159
+ const open = v.status === 'pending';
160
+ return page(c, 'Approval request', html `<h1>${v.operationKey} ${v.locked ? html `<span class="badge">locked</span>` : ''}</h1>
161
+ <p class="sub"><code>/${v.slug}</code> · ${v.instanceName}</p>
162
+ <p>${v.summary}</p>
163
+ ${v.targets != null
164
+ ? html `<label>Targets</label>
165
+ <pre>${pretty(v.targets)}</pre>`
166
+ : ''}
167
+ ${v.diff != null
168
+ ? html `<label>Changes</label>
169
+ <pre>${pretty(v.diff)}</pre>`
170
+ : ''}
171
+ <label>Parameters (secrets redacted)</label>
172
+ <pre>${pretty(v.params ?? {})}</pre>
173
+ ${open
174
+ ? html `<p class="muted">Signed in as <strong>${v.username}</strong> · expires ${v.expiresAt.toISOString()}</p>
175
+ <form method="post" action="/a/${v.token}">
176
+ <input type="hidden" name="csrf" value="${v.csrf}" />
177
+ ${v.confirmLiteral
178
+ ? html `<label for="confirm">Type <code>${v.confirmLiteral}</code> to approve</label
179
+ ><input id="confirm" type="text" name="confirm" autocomplete="off" />`
180
+ : ''}
181
+ ${v.needsTotp
182
+ ? html `<label for="totp">Authenticator code (required to approve a locked operation)</label
183
+ ><input
184
+ id="totp"
185
+ type="text"
186
+ name="totp"
187
+ inputmode="numeric"
188
+ autocomplete="one-time-code"
189
+ pattern="[0-9]{6}"
190
+ />`
191
+ : ''}
192
+ ${v.error ? html `<p class="error" role="alert">${v.error}</p>` : ''}
193
+ <div class="row">
194
+ <button class="danger" type="submit" name="decision" value="deny">Deny</button>
195
+ <button class="primary" type="submit" name="decision" value="approve">Approve</button>
196
+ </div>
197
+ </form>`
198
+ : html `<p class="error" role="status">This request is ${v.status.replace('_', ' ')}.</p>`}`, status);
199
+ }
200
+ //# sourceMappingURL=pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages.js","sourceRoot":"","sources":["../../../src/http/mcp/pages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { Hono } from 'hono';
2
+ import type { AppContext } from '../app.js';
3
+ /**
4
+ * The public MCP listener (design §2.1): `/{slug}` endpoints, OAuth metadata/flows and approval-link
5
+ * pages. Nothing from the Admin API is mounted here.
6
+ */
7
+ export declare function createMcpApp(ctx: AppContext): Hono;
@@ -0,0 +1,30 @@
1
+ import { Hono } from 'hono';
2
+ import { errorResponse } from './common.js';
3
+ import { McpEndpoints } from './mcp/endpoint.js';
4
+ import { registerApprovalRoutes } from './mcp/approval-routes.js';
5
+ import { registerOAuthRoutes } from './mcp/oauth-routes.js';
6
+ const jsonRpcError = (code, message) => ({
7
+ jsonrpc: '2.0',
8
+ id: null,
9
+ error: { code, message },
10
+ });
11
+ /**
12
+ * The public MCP listener (design §2.1): `/{slug}` endpoints, OAuth metadata/flows and approval-link
13
+ * pages. Nothing from the Admin API is mounted here.
14
+ */
15
+ export function createMcpApp(ctx) {
16
+ const app = new Hono();
17
+ app.onError(errorResponse);
18
+ const endpoints = new McpEndpoints(ctx, ctx.oauth);
19
+ ctx.onStop(() => endpoints.closeAll());
20
+ // Aggregate status only; per-instance detail lives on the admin port (design §11).
21
+ app.get('/healthz', (c) => c.json({ status: 'ok' }));
22
+ registerOAuthRoutes(app, ctx, ctx.oauth);
23
+ registerApprovalRoutes(app, ctx);
24
+ app.all('/.well-known/*', (c) => c.json({ error: 'not_found' }, 404));
25
+ app.all('/oauth/*', (c) => c.json({ error: 'not_found' }, 404));
26
+ app.all('/:slug', (c) => endpoints.handle(c, c.req.param('slug')));
27
+ app.notFound((c) => c.json(jsonRpcError(-32001, 'Not found'), 404));
28
+ return app;
29
+ }
30
+ //# sourceMappingURL=mcp-app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-app.js","sourceRoot":"","sources":["../../src/http/mcp-app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,OAAe,EAAE,EAAE,CAAC,CAAC;IACvD,OAAO,EAAE,KAAc;IACvB,EAAE,EAAE,IAAI;IACR,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;CACzB,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAe;IAC1C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3B,MAAM,SAAS,GAAG,IAAI,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IACnD,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;IAEvC,mFAAmF;IACnF,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAErD,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IACzC,sBAAsB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACjC,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IACtE,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IAEhE,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAEnE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACpE,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { Manifest } from '@synoikia/plugin-sdk';
2
+ /**
3
+ * Connection config handling (design §7.2, §8.3). Fields marked `writeOnly` in the plugin's
4
+ * connection schema are secrets: stored encrypted, passed to the plugin only via `init`, and never
5
+ * returned by the API — only a `{ set, hint }` summary.
6
+ */
7
+ export declare function secretFieldNames(manifest: Manifest): string[];
8
+ /** Validates the merged config + secrets against the plugin's schema and splits them again. */
9
+ export declare function validateConnection(manifest: Manifest, input: Record<string, unknown>): {
10
+ config: Record<string, unknown>;
11
+ secrets: Record<string, string>;
12
+ };
13
+ /**
14
+ * Applies a secrets patch: a provided non-empty value replaces, `null` clears, an omitted key keeps
15
+ * the stored value. Unknown keys are rejected so typos don't silently drop a credential.
16
+ */
17
+ export declare function mergeSecrets(manifest: Manifest, current: Record<string, string>, patch: Record<string, string | null | undefined>): Record<string, string>;
18
+ export interface SecretSummary {
19
+ set: boolean;
20
+ /** Last 4 characters, only for values long enough that this reveals little. */
21
+ hint?: string;
22
+ }
23
+ export declare function summarizeSecrets(manifest: Manifest, secrets: Record<string, string>): Record<string, SecretSummary>;
24
+ /** Non-secret fields that say where the upstream is: changing one decides where the secrets are sent. */
25
+ export declare function addressFieldNames(manifest: Manifest): string[];
26
+ /**
27
+ * The stored secrets a candidate connection may use (design §7.2). If the candidate changes where the
28
+ * upstream is, stored secrets are not carried over: otherwise "Test connection" or a save with an
29
+ * attacker's URL would send the stored API key there. The caller must enter them again.
30
+ */
31
+ export declare function storedSecretsFor(manifest: Manifest, current: {
32
+ config: Record<string, unknown>;
33
+ secrets: Record<string, string>;
34
+ }, candidate: {
35
+ config: Record<string, unknown>;
36
+ secrets?: Record<string, string | null>;
37
+ }): Record<string, string>;
@@ -0,0 +1,109 @@
1
+ import { Ajv } from 'ajv';
2
+ import * as ajvFormats from 'ajv-formats';
3
+ import { ValidationError } from '../errors.js';
4
+ const addFormats = (ajvFormats.default
5
+ .default ?? ajvFormats.default);
6
+ const ajv = new Ajv({ allErrors: true, strict: false, useDefaults: true });
7
+ addFormats(ajv);
8
+ /**
9
+ * Compiled validators by schema content: compiled once per plugin version, not per request (ajv keeps
10
+ * every compiled schema, and a second compile of a schema with an `$id` throws).
11
+ */
12
+ const compiled = new Map();
13
+ const MAX_COMPILED = 200;
14
+ function validatorFor(schema) {
15
+ const key = JSON.stringify(schema);
16
+ let validate = compiled.get(key);
17
+ if (!validate) {
18
+ validate = ajv.compile(schema);
19
+ // Unregister the `$id` so another version of the same plugin can compile its own schema under it.
20
+ const id = schema.$id;
21
+ if (typeof id === 'string')
22
+ ajv.removeSchema(id);
23
+ if (compiled.size >= MAX_COMPILED)
24
+ compiled.delete(compiled.keys().next().value);
25
+ compiled.set(key, validate);
26
+ }
27
+ return validate;
28
+ }
29
+ /**
30
+ * Connection config handling (design §7.2, §8.3). Fields marked `writeOnly` in the plugin's
31
+ * connection schema are secrets: stored encrypted, passed to the plugin only via `init`, and never
32
+ * returned by the API — only a `{ set, hint }` summary.
33
+ */
34
+ export function secretFieldNames(manifest) {
35
+ const props = (manifest.connection.schema.properties ?? {});
36
+ return Object.entries(props)
37
+ .filter(([, p]) => p?.writeOnly === true)
38
+ .map(([k]) => k);
39
+ }
40
+ const describeErrors = (errors) => (errors ?? []).map((e) => `${e.instancePath || '(connection)'} ${e.message ?? 'is invalid'}`.trim());
41
+ /** Validates the merged config + secrets against the plugin's schema and splits them again. */
42
+ export function validateConnection(manifest, input) {
43
+ const validate = validatorFor(manifest.connection.schema);
44
+ const candidate = structuredClone(input);
45
+ if (!validate(candidate)) {
46
+ throw new ValidationError('invalid_connection', 'Connection settings are invalid', describeErrors(validate.errors));
47
+ }
48
+ const secretNames = new Set(secretFieldNames(manifest));
49
+ const config = {};
50
+ const secrets = {};
51
+ for (const [k, v] of Object.entries(candidate)) {
52
+ if (secretNames.has(k)) {
53
+ if (v !== undefined && v !== null && v !== '')
54
+ secrets[k] = String(v);
55
+ }
56
+ else {
57
+ config[k] = v;
58
+ }
59
+ }
60
+ return { config, secrets };
61
+ }
62
+ /**
63
+ * Applies a secrets patch: a provided non-empty value replaces, `null` clears, an omitted key keeps
64
+ * the stored value. Unknown keys are rejected so typos don't silently drop a credential.
65
+ */
66
+ export function mergeSecrets(manifest, current, patch) {
67
+ const names = new Set(secretFieldNames(manifest));
68
+ const next = { ...current };
69
+ for (const [k, v] of Object.entries(patch)) {
70
+ if (!names.has(k))
71
+ throw new ValidationError('unknown_secret', `"${k}" is not a secret field of this plugin`);
72
+ if (v === null)
73
+ delete next[k];
74
+ else if (v !== undefined && v !== '')
75
+ next[k] = v;
76
+ }
77
+ return next;
78
+ }
79
+ export function summarizeSecrets(manifest, secrets) {
80
+ return Object.fromEntries(secretFieldNames(manifest).map((name) => {
81
+ const v = secrets[name];
82
+ return [name, v ? { set: true, ...(v.length >= 12 ? { hint: `…${v.slice(-4)}` } : {}) } : { set: false }];
83
+ }));
84
+ }
85
+ const ADDRESS_FORMATS = new Set(['uri', 'url', 'hostname', 'ipv4', 'ipv6', 'idn-hostname', 'iri']);
86
+ const ADDRESS_NAME = /(url|uri|host|server|endpoint|address|domain|origin)/i;
87
+ /** Non-secret fields that say where the upstream is: changing one decides where the secrets are sent. */
88
+ export function addressFieldNames(manifest) {
89
+ const props = (manifest.connection.schema.properties ?? {});
90
+ return Object.entries(props)
91
+ .filter(([k, p]) => p?.writeOnly !== true && (ADDRESS_FORMATS.has(p?.format ?? '') || ADDRESS_NAME.test(k)))
92
+ .map(([k]) => k);
93
+ }
94
+ /**
95
+ * The stored secrets a candidate connection may use (design §7.2). If the candidate changes where the
96
+ * upstream is, stored secrets are not carried over: otherwise "Test connection" or a save with an
97
+ * attacker's URL would send the stored API key there. The caller must enter them again.
98
+ */
99
+ export function storedSecretsFor(manifest, current, candidate) {
100
+ const moved = addressFieldNames(manifest).filter((k) => JSON.stringify(candidate.config[k] ?? null) !== JSON.stringify(current.config[k] ?? null));
101
+ if (moved.length === 0)
102
+ return current.secrets;
103
+ const missing = Object.keys(current.secrets).filter((k) => !candidate.secrets?.[k]);
104
+ if (missing.length > 0) {
105
+ throw new ValidationError('secrets_required', `Changing ${moved.join(', ')} changes where credentials are sent: enter ${missing.join(', ')} again`, missing);
106
+ }
107
+ return {};
108
+ }
109
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/instances/connection.ts"],"names":[],"mappings":"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"}