@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,50 @@
1
+ import type { InitParams } from '@synoikia/plugin-sdk';
2
+ import { PluginProcess } from './process.js';
3
+ import type { SpawnOptions } from './process.js';
4
+ /**
5
+ * Keeps one plugin instance's child process alive (design §4.4): spawn → `init` → ready. On an
6
+ * unexpected exit the instance goes to `error` and restarts with exponential backoff. A crashed or
7
+ * restarting plugin never makes the gate fail open: callers just get `PluginUnavailableError`.
8
+ */
9
+ export type InstanceStatus = 'stopped' | 'starting' | 'ready' | 'error';
10
+ export interface SupervisorOptions extends SpawnOptions {
11
+ /** Called on every start with this instance's config and freshly decrypted secrets. */
12
+ loadInit: () => Omit<InitParams, 'sdkVersion' | 'instanceId'>;
13
+ onStatus?: (status: InstanceStatus, error?: string) => void;
14
+ onLog?: (level: string, message: string) => void;
15
+ onCatalogChanged?: (reason?: string) => void;
16
+ initTimeoutMs?: number;
17
+ backoff?: {
18
+ initialMs: number;
19
+ maxMs: number;
20
+ };
21
+ }
22
+ export declare class PluginSupervisor {
23
+ private readonly opts;
24
+ private proc?;
25
+ private _status;
26
+ private stopping;
27
+ private restartTimer?;
28
+ private attempt;
29
+ private generation;
30
+ private queue;
31
+ constructor(opts: SupervisorOptions);
32
+ get status(): InstanceStatus;
33
+ /** The live process for RPC calls. Throws unless the instance is ready. */
34
+ get client(): PluginProcess;
35
+ start(): Promise<void>;
36
+ stop(): Promise<void>;
37
+ /** Restart now (e.g. after the admin changed the connection config or secrets). */
38
+ restart(): Promise<void>;
39
+ /**
40
+ * Lifecycle changes run one at a time, so concurrent restarts can't leave two children alive and a
41
+ * stop can't be undone by a start that was already under way.
42
+ */
43
+ private serial;
44
+ /** Supersedes whatever is in flight at once: a child still in `init` is killed, not waited for. */
45
+ private halt;
46
+ private shutdown;
47
+ private setStatus;
48
+ private spawn;
49
+ private scheduleRestart;
50
+ }
@@ -0,0 +1,125 @@
1
+ import { SDK_VERSION } from '@synoikia/plugin-sdk';
2
+ import { PluginProcess, PluginUnavailableError } from './process.js';
3
+ export class PluginSupervisor {
4
+ opts;
5
+ proc;
6
+ _status = 'stopped';
7
+ stopping = false;
8
+ restartTimer;
9
+ attempt = 0;
10
+ generation = 0;
11
+ queue = Promise.resolve();
12
+ constructor(opts) {
13
+ this.opts = opts;
14
+ }
15
+ get status() {
16
+ return this._status;
17
+ }
18
+ /** The live process for RPC calls. Throws unless the instance is ready. */
19
+ get client() {
20
+ if (this._status !== 'ready' || !this.proc?.running)
21
+ throw new PluginUnavailableError();
22
+ return this.proc;
23
+ }
24
+ async start() {
25
+ return this.serial(async () => {
26
+ this.stopping = false;
27
+ if (this.proc)
28
+ return; // already running or starting
29
+ await this.spawn();
30
+ });
31
+ }
32
+ async stop() {
33
+ this.halt();
34
+ return this.serial(() => this.shutdown());
35
+ }
36
+ /** Restart now (e.g. after the admin changed the connection config or secrets). */
37
+ async restart() {
38
+ this.halt();
39
+ return this.serial(async () => {
40
+ await this.shutdown();
41
+ this.attempt = 0;
42
+ this.stopping = false;
43
+ await this.spawn();
44
+ });
45
+ }
46
+ /**
47
+ * Lifecycle changes run one at a time, so concurrent restarts can't leave two children alive and a
48
+ * stop can't be undone by a start that was already under way.
49
+ */
50
+ serial(fn) {
51
+ const run = this.queue.then(fn);
52
+ this.queue = run.catch(() => undefined);
53
+ return run;
54
+ }
55
+ /** Supersedes whatever is in flight at once: a child still in `init` is killed, not waited for. */
56
+ halt() {
57
+ this.stopping = true;
58
+ this.generation++;
59
+ clearTimeout(this.restartTimer);
60
+ if (this._status === 'starting')
61
+ this.proc?.kill();
62
+ }
63
+ async shutdown() {
64
+ this.stopping = true;
65
+ this.generation++;
66
+ clearTimeout(this.restartTimer);
67
+ const proc = this.proc;
68
+ this.proc = undefined;
69
+ await proc?.stop();
70
+ this.setStatus('stopped');
71
+ }
72
+ setStatus(status, error) {
73
+ this._status = status;
74
+ this.opts.onStatus?.(status, error);
75
+ }
76
+ async spawn() {
77
+ const generation = ++this.generation;
78
+ this.setStatus('starting');
79
+ const proc = new PluginProcess(this.opts);
80
+ this.proc = proc;
81
+ proc.on('log', (level, message) => this.opts.onLog?.(level, message));
82
+ proc.on('catalogChanged', (reason) => this.opts.onCatalogChanged?.(reason));
83
+ proc.on('exit', (code, signal) => {
84
+ if (this.stopping || generation !== this.generation)
85
+ return;
86
+ this.scheduleRestart(`plugin exited unexpectedly (${signal ?? `code ${code}`})`);
87
+ });
88
+ try {
89
+ proc.start();
90
+ await proc.call('init', { ...this.opts.loadInit(), instanceId: this.opts.instanceId, sdkVersion: SDK_VERSION }, this.opts.initTimeoutMs ?? 30_000);
91
+ }
92
+ catch (err) {
93
+ proc.kill();
94
+ if (generation !== this.generation)
95
+ return;
96
+ this.scheduleRestart(`init failed: ${err instanceof Error ? err.message : String(err)}`);
97
+ return;
98
+ }
99
+ if (generation !== this.generation) {
100
+ // Superseded (stopped or restarted) during init: this child must not outlive its turn.
101
+ proc.kill();
102
+ return;
103
+ }
104
+ this.attempt = 0;
105
+ this.setStatus('ready');
106
+ }
107
+ scheduleRestart(reason) {
108
+ if (this.stopping)
109
+ return;
110
+ this.generation++;
111
+ this.proc = undefined;
112
+ this.setStatus('error', reason);
113
+ const { initialMs, maxMs } = this.opts.backoff ?? { initialMs: 1000, maxMs: 60_000 };
114
+ const delay = Math.min(maxMs, initialMs * 2 ** this.attempt++);
115
+ clearTimeout(this.restartTimer);
116
+ const generation = this.generation;
117
+ this.restartTimer = setTimeout(() => {
118
+ void this.serial(async () => {
119
+ if (!this.stopping && generation === this.generation)
120
+ await this.spawn();
121
+ });
122
+ }, delay);
123
+ }
124
+ }
125
+ //# sourceMappingURL=supervisor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../../src/plugins/supervisor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { CallerContext, GateDeps, InstanceRuntime } from '../gate/pipeline.js';
2
+ import type { SandboxResult } from '../sandbox/index.js';
3
+ /**
4
+ * The two MCP tools' engines (design §5.1–§5.2). The MCP transport layer (phase 14) only has to
5
+ * turn a tool call into `executeCode` / `searchCode` and the result back into tool output.
6
+ */
7
+ /**
8
+ * Caps on sandbox work (design §5.2): `executePerMinute` counts `execute` and `search` runs per
9
+ * principal and instance, and only so many isolates (up to `memoryMb` each) run at once per instance
10
+ * and in total. Over either cap the run is refused before an isolate is created.
11
+ */
12
+ export declare const SANDBOX_CONCURRENCY: {
13
+ perInstance: number;
14
+ total: number;
15
+ };
16
+ /** Scripts running now, across all instances (shutdown waits for cancelled ones to return). */
17
+ export declare const sandboxesRunning: () => number;
18
+ export declare function executeCode(deps: GateDeps, rt: InstanceRuntime, caller: CallerContext, code: string, signal?: AbortSignal): Promise<SandboxResult>;
19
+ export interface CatalogFindQuery {
20
+ text?: string;
21
+ group?: string;
22
+ tag?: string;
23
+ kind?: string;
24
+ classification?: 'read' | 'write' | 'locked';
25
+ includeDisabled?: boolean;
26
+ limit?: number;
27
+ }
28
+ export declare function searchCode(deps: GateDeps, rt: InstanceRuntime, caller: CallerContext, code: string, _signal?: AbortSignal): Promise<SandboxResult>;
@@ -0,0 +1,248 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { and, asc, eq } from 'drizzle-orm';
3
+ import { writeAudit } from '../audit.js';
4
+ import { accessInput, listGroups } from '../catalog/groups.js';
5
+ import { findRegistryEntries } from '../catalog/registry.js';
6
+ import { guides, operationGroups, operations } from '../db/schema.js';
7
+ import { effectiveAccess } from '../gate/access.js';
8
+ import { currentGuide, issueAttestationKey } from '../gate/attestation.js';
9
+ import { createGateBindings, principalKey } from '../gate/pipeline.js';
10
+ import { BindingError, runInSandbox } from '../sandbox/index.js';
11
+ /**
12
+ * The two MCP tools' engines (design §5.1–§5.2). The MCP transport layer (phase 14) only has to
13
+ * turn a tool call into `executeCode` / `searchCode` and the result back into tool output.
14
+ */
15
+ /**
16
+ * Caps on sandbox work (design §5.2): `executePerMinute` counts `execute` and `search` runs per
17
+ * principal and instance, and only so many isolates (up to `memoryMb` each) run at once per instance
18
+ * and in total. Over either cap the run is refused before an isolate is created.
19
+ */
20
+ export const SANDBOX_CONCURRENCY = { perInstance: 4, total: 16 };
21
+ const running = new Map();
22
+ let runningTotal = 0;
23
+ /** Scripts running now, across all instances (shutdown waits for cancelled ones to return). */
24
+ export const sandboxesRunning = () => runningTotal;
25
+ function admit(deps, rt, caller) {
26
+ const who = principalKey(caller);
27
+ if (!deps.limiter.take(`run:${rt.instanceId}:${who}`, rt.settings.executePerMinute, 60_000)) {
28
+ return {
29
+ ok: false,
30
+ error: { code: 'RATE_LIMITED', message: 'Too many search/execute calls; slow down' },
31
+ logs: [],
32
+ };
33
+ }
34
+ const here = running.get(rt.instanceId) ?? 0;
35
+ if (here >= SANDBOX_CONCURRENCY.perInstance || runningTotal >= SANDBOX_CONCURRENCY.total) {
36
+ return { ok: false, error: { code: 'BUSY', message: 'Too many scripts are running; try again shortly' }, logs: [] };
37
+ }
38
+ running.set(rt.instanceId, here + 1);
39
+ runningTotal++;
40
+ return () => {
41
+ running.set(rt.instanceId, (running.get(rt.instanceId) ?? 1) - 1);
42
+ runningTotal--;
43
+ };
44
+ }
45
+ export async function executeCode(deps, rt, caller, code, signal) {
46
+ const release = admit(deps, rt, caller);
47
+ if (typeof release !== 'function')
48
+ return release;
49
+ // Ends with the sandbox (return, error or timeout) or earlier, when the request or session goes away.
50
+ const run = new AbortController();
51
+ const stop = () => run.abort();
52
+ signal?.addEventListener('abort', stop);
53
+ if (signal?.aborted)
54
+ stop();
55
+ try {
56
+ return await runInSandbox({
57
+ code,
58
+ bindings: createGateBindings(deps, rt, caller, run.signal),
59
+ limits: rt.settings.sandbox,
60
+ // Each gated call's result was already redacted; this covers anything the script derived or logged.
61
+ redact: rt.redact,
62
+ });
63
+ }
64
+ finally {
65
+ stop();
66
+ signal?.removeEventListener('abort', stop);
67
+ release();
68
+ }
69
+ }
70
+ function describeOp(op, groupKey, access) {
71
+ return {
72
+ key: op.key,
73
+ displayName: op.displayName ?? undefined,
74
+ group: groupKey,
75
+ classification: op.locked ? 'locked' : op.classification,
76
+ // Whether a call runs straight away, waits for a human, or is auto-approved at level `write`.
77
+ approval: access.reachable
78
+ ? { run: 'none', approve: 'required', auto: 'auto' }[access.mode]
79
+ : op.locked || op.classification === 'write'
80
+ ? 'required'
81
+ : 'none',
82
+ typedConfirmation: op.typedConfirmation,
83
+ attestationRequired: op.attestationRequired,
84
+ summary: op.docs?.summary,
85
+ ...(access.reachable ? {} : { disabled: true, reason: access.reason }),
86
+ };
87
+ }
88
+ function catalogBindings(db, instanceId, principal) {
89
+ const load = () => {
90
+ const groups = new Map(db
91
+ .select()
92
+ .from(operationGroups)
93
+ .where(eq(operationGroups.instanceId, instanceId))
94
+ .all()
95
+ .map((g) => [g.id, g]));
96
+ const ops = db
97
+ .select()
98
+ .from(operations)
99
+ .where(and(eq(operations.instanceId, instanceId), eq(operations.stale, false)))
100
+ .orderBy(asc(operations.key))
101
+ .all();
102
+ return { groups, ops };
103
+ };
104
+ const accessOf = (op, groups) => effectiveAccess(accessInput(op), groups.get(op.groupId), principal);
105
+ return {
106
+ find: async ([rawQuery]) => {
107
+ const q = (rawQuery ?? {});
108
+ const text = q.text?.toLowerCase();
109
+ const limit = Math.min(Math.max(1, Number(q.limit) || 50), 200);
110
+ const { groups, ops } = load();
111
+ const out = [];
112
+ for (const op of ops) {
113
+ const group = groups.get(op.groupId);
114
+ const access = accessOf(op, groups);
115
+ if (!access.reachable && !q.includeDisabled)
116
+ continue;
117
+ if (q.group && group?.key !== q.group)
118
+ continue;
119
+ if (q.tag && op.tag !== q.tag)
120
+ continue;
121
+ if (q.kind && op.kind !== q.kind)
122
+ continue;
123
+ const cls = op.locked ? 'locked' : op.classification;
124
+ if (q.classification && cls !== q.classification)
125
+ continue;
126
+ if (text) {
127
+ const hay = [op.key, op.displayName, op.docs?.summary]
128
+ .join(' ')
129
+ .toLowerCase();
130
+ if (!hay.includes(text))
131
+ continue;
132
+ }
133
+ out.push(describeOp(op, group?.key, access));
134
+ if (out.length >= limit)
135
+ break;
136
+ }
137
+ return out;
138
+ },
139
+ get: async ([key]) => {
140
+ const { groups, ops } = load();
141
+ const op = ops.find((o) => o.key === key);
142
+ if (!op)
143
+ return null;
144
+ const access = accessOf(op, groups);
145
+ return { ...describeOp(op, groups.get(op.groupId)?.key, access), paramsSchema: op.paramsSchema, docs: op.docs };
146
+ },
147
+ groups: async () => listGroups(db, instanceId).filter((g) => !g.stale),
148
+ };
149
+ }
150
+ /**
151
+ * `guides.get(key)`: fetches the plugin's current best-practice guide, records its version, and
152
+ * hands out the attestation key `execute` must present from the same MCP session (HA §3.6). Revising
153
+ * a guide rotates the key. Every read is audited: the key attests that this session was shown it.
154
+ */
155
+ function guideBindings(deps, rt, caller) {
156
+ return {
157
+ get: async ([key]) => {
158
+ const op = deps.db
159
+ .select()
160
+ .from(operations)
161
+ .where(and(eq(operations.instanceId, rt.instanceId), eq(operations.key, String(key)), eq(operations.stale, false)))
162
+ .get();
163
+ if (!op)
164
+ throw new BindingError('UNKNOWN_OPERATION', `${String(key)} is not in the catalog`);
165
+ if (!op.attestationRequired)
166
+ return { key: op.key, required: false };
167
+ let guide;
168
+ try {
169
+ guide = await rt.plugin().call('getGuide', { key: op.key });
170
+ }
171
+ catch {
172
+ throw new BindingError('PLUGIN_ERROR', `Could not load the guide for ${op.key}`);
173
+ }
174
+ const current = currentGuide(deps.db, rt.instanceId, op.id);
175
+ if (!current || current.version !== guide.version || current.content !== guide.content) {
176
+ deps.db
177
+ .insert(guides)
178
+ .values({
179
+ id: randomUUID(),
180
+ instanceId: rt.instanceId,
181
+ operationId: op.id,
182
+ version: guide.version,
183
+ content: guide.content,
184
+ fetchedAt: deps.now?.() ?? new Date(),
185
+ })
186
+ .run();
187
+ }
188
+ writeAudit(deps.db, {
189
+ kind: 'search',
190
+ instanceId: rt.instanceId,
191
+ operationKey: op.key,
192
+ decision: 'guide_read',
193
+ actorKind: 'mcp_client',
194
+ actorId: caller.client.id ?? null,
195
+ detail: { guideVersion: guide.version, mcpSessionId: caller.mcpSessionId ?? null },
196
+ }, deps.now?.() ?? new Date());
197
+ return {
198
+ key: op.key,
199
+ required: true,
200
+ version: guide.version,
201
+ content: guide.content,
202
+ best_practice_key: issueAttestationKey(deps.attestationKey, rt.instanceId, op.key, guide.version, caller.mcpSessionId),
203
+ };
204
+ },
205
+ };
206
+ }
207
+ function searchBindings(deps, rt, caller) {
208
+ const bindings = {
209
+ catalog: catalogBindings(deps.db, rt.instanceId, caller.principal),
210
+ };
211
+ if (rt.manifest.capabilities.registry) {
212
+ bindings.registry = {
213
+ find: async ([q]) =>
214
+ // Mirrored upstream attributes can hold tokens (HA camera `access_token`): redact at the source.
215
+ rt.redact(findRegistryEntries(deps.db, rt.instanceId, (q ?? {}))),
216
+ };
217
+ }
218
+ if (rt.manifest.capabilities.attestation)
219
+ bindings.guides = guideBindings(deps, rt, caller);
220
+ return bindings;
221
+ }
222
+ export async function searchCode(deps, rt, caller, code, _signal) {
223
+ const release = admit(deps, rt, caller);
224
+ if (typeof release !== 'function')
225
+ return release;
226
+ let result;
227
+ try {
228
+ result = await runInSandbox({
229
+ code,
230
+ bindings: searchBindings(deps, rt, caller),
231
+ limits: rt.settings.sandbox,
232
+ redact: rt.redact,
233
+ });
234
+ }
235
+ finally {
236
+ release();
237
+ }
238
+ writeAudit(deps.db, {
239
+ kind: 'search',
240
+ instanceId: rt.instanceId,
241
+ actorKind: 'mcp_client',
242
+ actorId: caller.client.id,
243
+ resultStatus: result.ok ? 'ok' : 'error',
244
+ detail: result.ok ? { truncated: result.truncated } : { error: result.error.code },
245
+ }, deps.now?.() ?? new Date());
246
+ return result;
247
+ }
248
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Runs model-authored code in a fresh V8 isolate (design §5.4). The isolate has no Node APIs at all:
3
+ * the only way out is the host functions passed in `bindings`. Values cross the boundary as JSON.
4
+ *
5
+ * `code` is the body of an async function: it may `await` bindings and should `return` its result.
6
+ */
7
+ export interface SandboxLimits {
8
+ /** Wall-clock budget for sandbox activity. Time a binding spends paused (human approval) is excluded. */
9
+ timeoutMs: number;
10
+ memoryMb: number;
11
+ /** Maximum size of the JSON-encoded result; larger results are truncated with a marker. */
12
+ maxResultBytes: number;
13
+ maxLogBytes: number;
14
+ }
15
+ export declare const DEFAULT_LIMITS: SandboxLimits;
16
+ /** Thrown by a binding to surface a structured error (`err.code`) inside the sandbox. */
17
+ export declare class BindingError extends Error {
18
+ readonly code: string;
19
+ constructor(code: string, message: string);
20
+ }
21
+ /** Lets a binding stop the sandbox clock while it waits on something slow and human, like approval. */
22
+ export interface BudgetControl {
23
+ pause(): void;
24
+ resume(): void;
25
+ /** Milliseconds of sandbox budget left. */
26
+ remainingMs(): number;
27
+ }
28
+ export type Binding = (args: unknown[], budget: BudgetControl) => Promise<unknown>;
29
+ export interface SandboxRun {
30
+ code: string;
31
+ /** `{ truenas: { call: fn } }` becomes `truenas.call(...)` inside the sandbox. */
32
+ bindings: Record<string, Record<string, Binding>>;
33
+ limits?: Partial<SandboxLimits>;
34
+ /**
35
+ * Applied to everything that leaves the sandbox — the result (before it is size-capped), every
36
+ * `console.log` argument and error messages — so nothing the code derives or prints escapes redaction.
37
+ */
38
+ redact?: (value: unknown) => unknown;
39
+ }
40
+ export type SandboxResult = {
41
+ ok: true;
42
+ value: unknown;
43
+ truncated: boolean;
44
+ logs: string[];
45
+ } | {
46
+ ok: false;
47
+ error: {
48
+ code: string;
49
+ message: string;
50
+ };
51
+ logs: string[];
52
+ };
53
+ export declare function runInSandbox(run: SandboxRun): Promise<SandboxResult>;