approval-md 0.3.0 → 0.4.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.
- package/README.md +83 -914
- package/SPEC.md +101 -13
- package/cli.js +134 -7
- package/dist/src/adapters/smtp.d.ts +18 -0
- package/dist/src/adapters/smtp.js +24 -3
- package/dist/src/adapters/smtp.js.map +1 -1
- package/dist/src/channels/contract.d.ts +72 -0
- package/dist/src/channels/contract.js +161 -1
- package/dist/src/channels/contract.js.map +1 -1
- package/dist/src/channels/relay-server.d.ts +90 -0
- package/dist/src/channels/relay-server.js +295 -0
- package/dist/src/channels/relay-server.js.map +1 -0
- package/dist/src/channels/relay.d.ts +273 -0
- package/dist/src/channels/relay.js +564 -0
- package/dist/src/channels/relay.js.map +1 -0
- package/dist/src/channels/tagging.d.ts +25 -0
- package/dist/src/channels/tagging.js +63 -6
- package/dist/src/channels/tagging.js.map +1 -1
- package/dist/src/channels/telegram-minimal.d.ts +251 -0
- package/dist/src/channels/telegram-minimal.js +618 -0
- package/dist/src/channels/telegram-minimal.js.map +1 -0
- package/dist/src/channels/telegram-webhook.d.ts +273 -0
- package/dist/src/channels/telegram-webhook.js +536 -0
- package/dist/src/channels/telegram-webhook.js.map +1 -0
- package/dist/src/channels/telegram.d.ts +725 -20
- package/dist/src/channels/telegram.js +1332 -123
- package/dist/src/channels/telegram.js.map +1 -1
- package/dist/src/cli/amend.js +358 -5
- package/dist/src/cli/amend.js.map +1 -1
- package/dist/src/cli/attest.js +43 -6
- package/dist/src/cli/attest.js.map +1 -1
- package/dist/src/cli/audit-card.d.ts +1 -1
- package/dist/src/cli/audit-card.js +40 -1
- package/dist/src/cli/audit-card.js.map +1 -1
- package/dist/src/cli/audit.d.ts +7 -5
- package/dist/src/cli/audit.js +22 -10
- package/dist/src/cli/audit.js.map +1 -1
- package/dist/src/cli/channel-relay.d.ts +108 -0
- package/dist/src/cli/channel-relay.js +334 -0
- package/dist/src/cli/channel-relay.js.map +1 -0
- package/dist/src/cli/channel-telegram-webhook.d.ts +275 -0
- package/dist/src/cli/channel-telegram-webhook.js +716 -0
- package/dist/src/cli/channel-telegram-webhook.js.map +1 -0
- package/dist/src/cli/channel-telegram.d.ts +347 -28
- package/dist/src/cli/channel-telegram.js +648 -56
- package/dist/src/cli/channel-telegram.js.map +1 -1
- package/dist/src/cli/channel.d.ts +1 -1
- package/dist/src/cli/channel.js +5 -1
- package/dist/src/cli/channel.js.map +1 -1
- package/dist/src/cli/daemon.js +37 -5
- package/dist/src/cli/daemon.js.map +1 -1
- package/dist/src/cli/doctor.d.ts +63 -0
- package/dist/src/cli/doctor.js +856 -111
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/execute.d.ts +29 -1
- package/dist/src/cli/execute.js +387 -21
- package/dist/src/cli/execute.js.map +1 -1
- package/dist/src/cli/exit-codes.d.ts +12 -0
- package/dist/src/cli/exit-codes.js +13 -0
- package/dist/src/cli/exit-codes.js.map +1 -1
- package/dist/src/cli/gate.d.ts +2 -0
- package/dist/src/cli/gate.js +233 -2
- package/dist/src/cli/gate.js.map +1 -1
- package/dist/src/cli/help.d.ts +10 -1
- package/dist/src/cli/help.js +283 -54
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hook.d.ts +485 -6
- package/dist/src/cli/hook.js +2370 -70
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/init.js +16 -7
- package/dist/src/cli/init.js.map +1 -1
- package/dist/src/cli/instructions.js +24 -3
- package/dist/src/cli/instructions.js.map +1 -1
- package/dist/src/cli/log-anchor.d.ts +55 -0
- package/dist/src/cli/log-anchor.js +199 -60
- package/dist/src/cli/log-anchor.js.map +1 -1
- package/dist/src/cli/log-unlock.d.ts +23 -0
- package/dist/src/cli/log-unlock.js +109 -0
- package/dist/src/cli/log-unlock.js.map +1 -0
- package/dist/src/cli/main.d.ts +0 -12
- package/dist/src/cli/main.js +72 -4
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/muse.d.ts +2 -0
- package/dist/src/cli/muse.js +62 -0
- package/dist/src/cli/muse.js.map +1 -0
- package/dist/src/cli/payload.js +79 -1
- package/dist/src/cli/payload.js.map +1 -1
- package/dist/src/cli/scaffold.js +6 -0
- package/dist/src/cli/scaffold.js.map +1 -1
- package/dist/src/cli/serve.d.ts +53 -0
- package/dist/src/cli/serve.js +270 -0
- package/dist/src/cli/serve.js.map +1 -0
- package/dist/src/cli/up.js +26 -2
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/verb-registry.d.ts +2 -0
- package/dist/src/cli/verb-registry.js +338 -39
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +2 -2
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/core/attest-bootstrap.d.ts +95 -0
- package/dist/src/core/attest-bootstrap.js +135 -0
- package/dist/src/core/attest-bootstrap.js.map +1 -0
- package/dist/src/core/attest.d.ts +20 -3
- package/dist/src/core/attest.js +25 -0
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +153 -12
- package/dist/src/core/audit.js +415 -20
- package/dist/src/core/audit.js.map +1 -1
- package/dist/src/core/channel-lease.d.ts +322 -0
- package/dist/src/core/channel-lease.js +663 -0
- package/dist/src/core/channel-lease.js.map +1 -0
- package/dist/src/core/command-class.d.ts +51 -0
- package/dist/src/core/command-class.js +766 -8
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/daemon-host.d.ts +178 -0
- package/dist/src/core/daemon-host.js +228 -0
- package/dist/src/core/daemon-host.js.map +1 -0
- package/dist/src/core/daemon-identity.d.ts +238 -0
- package/dist/src/core/daemon-identity.js +252 -0
- package/dist/src/core/daemon-identity.js.map +1 -0
- package/dist/src/core/gate.d.ts +364 -6
- package/dist/src/core/gate.js +802 -27
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/harness-version.d.ts +128 -4
- package/dist/src/core/harness-version.js +179 -12
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/harness-wait.d.ts +82 -3
- package/dist/src/core/harness-wait.js +89 -3
- package/dist/src/core/harness-wait.js.map +1 -1
- package/dist/src/core/log-lock.d.ts +435 -0
- package/dist/src/core/log-lock.js +1093 -0
- package/dist/src/core/log-lock.js.map +1 -0
- package/dist/src/core/log-subscribe.d.ts +21 -0
- package/dist/src/core/log-subscribe.js +16 -6
- package/dist/src/core/log-subscribe.js.map +1 -1
- package/dist/src/core/log-write-layer.d.ts +55 -0
- package/dist/src/core/log-write-layer.js +91 -0
- package/dist/src/core/log-write-layer.js.map +1 -0
- package/dist/src/core/log.d.ts +130 -22
- package/dist/src/core/log.js +473 -50
- package/dist/src/core/log.js.map +1 -1
- package/dist/src/core/payload-census.d.ts +51 -0
- package/dist/src/core/payload-census.js +125 -2
- package/dist/src/core/payload-census.js.map +1 -1
- package/dist/src/core/payload-store.d.ts +31 -0
- package/dist/src/core/payload-store.js +113 -8
- package/dist/src/core/payload-store.js.map +1 -1
- package/dist/src/core/payload.d.ts +18 -3
- package/dist/src/core/payload.js +20 -4
- package/dist/src/core/payload.js.map +1 -1
- package/dist/src/core/policy-diff.d.ts +10 -0
- package/dist/src/core/policy-diff.js +10 -2
- package/dist/src/core/policy-diff.js.map +1 -1
- package/dist/src/core/policy-explain.d.ts +7 -1
- package/dist/src/core/policy-explain.js +16 -1
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +24 -0
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +38 -0
- package/dist/src/core/policy-match.js +35 -0
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +40 -0
- package/dist/src/core/policy-proposal.js +37 -16
- package/dist/src/core/policy-proposal.js.map +1 -1
- package/dist/src/core/prompt-layout.d.ts +186 -1
- package/dist/src/core/prompt-layout.js +345 -3
- package/dist/src/core/prompt-layout.js.map +1 -1
- package/dist/src/core/read-scope.d.ts +10 -0
- package/dist/src/core/read-scope.js +4 -0
- package/dist/src/core/read-scope.js.map +1 -1
- package/dist/src/core/run-payload.d.ts +160 -0
- package/dist/src/core/run-payload.js +304 -0
- package/dist/src/core/run-payload.js.map +1 -0
- package/dist/src/core/sender-identity.d.ts +59 -11
- package/dist/src/core/sender-identity.js +79 -11
- package/dist/src/core/sender-identity.js.map +1 -1
- package/dist/src/core/state.d.ts +52 -1
- package/dist/src/core/state.js +52 -6
- package/dist/src/core/state.js.map +1 -1
- package/dist/src/core/telegram-config.d.ts +79 -0
- package/dist/src/core/telegram-config.js +108 -0
- package/dist/src/core/telegram-config.js.map +1 -1
- package/dist/src/core/validate.d.ts +20 -6
- package/dist/src/core/validate.js +0 -0
- package/dist/src/core/validate.js.map +1 -1
- package/dist/src/core/verify.d.ts +23 -1
- package/dist/src/core/verify.js +20 -2
- package/dist/src/core/verify.js.map +1 -1
- package/dist/src/daemon/audit.d.ts +80 -5
- package/dist/src/daemon/audit.js +126 -6
- package/dist/src/daemon/audit.js.map +1 -1
- package/dist/src/daemon/daemon.d.ts +174 -2
- package/dist/src/daemon/daemon.js +298 -7
- package/dist/src/daemon/daemon.js.map +1 -1
- package/dist/src/daemon/git-evidence.d.ts +2 -2
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/daemon/projection.d.ts +13 -2
- package/dist/src/daemon/projection.js +39 -6
- package/dist/src/daemon/projection.js.map +1 -1
- package/dist/src/mcp/server.d.ts +39 -3
- package/dist/src/mcp/server.js +22 -3
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/muse/server.d.ts +16 -0
- package/dist/src/muse/server.js +238 -0
- package/dist/src/muse/server.js.map +1 -0
- package/dist/src/serve/archive.d.ts +166 -0
- package/dist/src/serve/archive.js +363 -0
- package/dist/src/serve/archive.js.map +1 -0
- package/dist/src/serve/arguments.d.ts +93 -0
- package/dist/src/serve/arguments.js +228 -0
- package/dist/src/serve/arguments.js.map +1 -0
- package/dist/src/serve/credentials.d.ts +91 -0
- package/dist/src/serve/credentials.js +143 -0
- package/dist/src/serve/credentials.js.map +1 -0
- package/dist/src/serve/follow.d.ts +86 -0
- package/dist/src/serve/follow.js +101 -0
- package/dist/src/serve/follow.js.map +1 -0
- package/dist/src/serve/hook-thread.d.ts +134 -0
- package/dist/src/serve/hook-thread.js +333 -0
- package/dist/src/serve/hook-thread.js.map +1 -0
- package/dist/src/serve/hook-worker.d.ts +134 -0
- package/dist/src/serve/hook-worker.js +173 -0
- package/dist/src/serve/hook-worker.js.map +1 -0
- package/dist/src/serve/server.d.ts +412 -0
- package/dist/src/serve/server.js +1251 -0
- package/dist/src/serve/server.js.map +1 -0
- package/docs/README-extended.md +768 -0
- package/docs/cli-reference.md +2139 -67
- package/package.json +4 -2
- package/schema/event.schema.json +413 -30
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-age-without-created.json +19 -0
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-lockfile-path.json +14 -0
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-operator-cleared-system-actor.json +14 -0
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-unknown-reason.json +14 -0
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-without-age.json +13 -0
- package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +6 -1
- package/schema/fixtures/event/invalid/audit-reviewed-bad-payload-hash.json +21 -0
- package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +5 -0
- package/schema/fixtures/event/invalid/audit-reviewed-no-subject-hash.json +19 -0
- package/schema/fixtures/event/invalid/audit-reviewed-no-subject-seq.json +19 -0
- package/schema/fixtures/event/invalid/audit-reviewed-no-verdict-source.json +19 -0
- package/schema/fixtures/event/invalid/audit-reviewed-no-verdict.json +19 -0
- package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +6 -1
- package/schema/fixtures/event/invalid/audit-sampled-no-policy-hash.json +14 -0
- package/schema/fixtures/event/invalid/daemon-id-malformed.json +15 -0
- package/schema/fixtures/event/invalid/execution-started-truncated-policy-sha256.json +18 -0
- package/schema/fixtures/event/invalid/harness-cap-at-margin.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-fractional.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-negative.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-string.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-without-harness-execution.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-zero.json +16 -0
- package/schema/fixtures/event/valid/approval-requested-harness-cap.json +18 -0
- package/schema/fixtures/event/valid/audit-lock-reclaimed-implausible-created.json +18 -0
- package/schema/fixtures/event/valid/audit-lock-reclaimed-legacy.json +14 -0
- package/schema/fixtures/event/valid/audit-lock-reclaimed-unlock.json +19 -0
- package/schema/fixtures/event/valid/audit-lock-reclaimed.json +19 -0
- package/schema/fixtures/event/valid/audit-reviewed-reaction.json +5 -0
- package/schema/fixtures/event/valid/audit-reviewed.json +6 -1
- package/schema/fixtures/event/valid/audit-sampled.json +2 -1
- package/schema/fixtures/event/valid/daemon-identity-declared.json +17 -0
- package/schema/fixtures/event/valid/daemon-identity.json +16 -0
- package/schema/fixtures/event/valid/execution-started-policy-sha256.json +18 -0
- package/schema/fixtures/event/valid/harness-kind-hermes.json +23 -0
- package/schema/fixtures/policy/invalid/daemons-malformed-id.json +10 -0
- package/schema/fixtures/policy/valid/daemons-allowlist.json +19 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-agent-may-request.md +21 -0
- package/schema/fixtures/policy-md/valid/agent-may-request.md +24 -0
- package/schema/policy.schema.json +102 -6
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/** Local synthetic Muse consumer facade. No native Muse protocol is claimed. */
|
|
2
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { buildPendingQueue } from "../channels/tagging.js";
|
|
5
|
+
import { readGateRecords, register, registeredAction, request } from "../core/gate.js";
|
|
6
|
+
import { payloadHash } from "../core/payload.js";
|
|
7
|
+
import { loadPolicy } from "../core/policy-load.js";
|
|
8
|
+
import { requestState } from "../core/state.js";
|
|
9
|
+
import { canonicalRender } from "../core/wysiwys.js";
|
|
10
|
+
export const MUSE_TOKEN_MIN_LENGTH = 24;
|
|
11
|
+
const MAX_BODY = 64 * 1024;
|
|
12
|
+
const KEY = /^[A-Za-z0-9][A-Za-z0-9_.:/-]{0,159}$/u;
|
|
13
|
+
/** Facade-local refusals. Core gate/tagger codes pass through separately. */
|
|
14
|
+
export const MUSE_LOCAL_REFUSAL_CODES = [
|
|
15
|
+
"unauthorized", "scope-forbidden", "unsupported-query", "not-found",
|
|
16
|
+
"invalid-registration", "invalid-request", "invalid-lookup", "invalid-payload",
|
|
17
|
+
"payload-mismatch", "not-awaiting", "policy-unavailable", "sensitive-payload",
|
|
18
|
+
"body-too-large", "malformed-body", "internal-error",
|
|
19
|
+
];
|
|
20
|
+
class MuseBodyError extends Error {
|
|
21
|
+
code;
|
|
22
|
+
constructor(code) {
|
|
23
|
+
super(code);
|
|
24
|
+
this.code = code;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function validMuseOptions(o) {
|
|
28
|
+
return KEY.test(o.tenant) && /^agent:[A-Za-z0-9][A-Za-z0-9_.:/-]{0,159}$/u.test(o.actor)
|
|
29
|
+
&& o.readToken.length >= MUSE_TOKEN_MIN_LENGTH && o.proposeToken.length >= MUSE_TOKEN_MIN_LENGTH
|
|
30
|
+
&& o.readToken !== o.proposeToken && Number.isInteger(o.port) && o.port >= 0 && o.port <= 65535;
|
|
31
|
+
}
|
|
32
|
+
function equalToken(a, b) {
|
|
33
|
+
const left = createHash("sha256").update(a).digest();
|
|
34
|
+
const right = createHash("sha256").update(b).digest();
|
|
35
|
+
return timingSafeEqual(left, right);
|
|
36
|
+
}
|
|
37
|
+
function respond(res, status, value) {
|
|
38
|
+
const bytes = JSON.stringify(value);
|
|
39
|
+
res.writeHead(status, {
|
|
40
|
+
"content-type": "application/json; charset=utf-8",
|
|
41
|
+
"cache-control": "no-store",
|
|
42
|
+
"content-security-policy": "default-src 'none'",
|
|
43
|
+
"x-content-type-options": "nosniff",
|
|
44
|
+
});
|
|
45
|
+
res.end(bytes);
|
|
46
|
+
}
|
|
47
|
+
function fail(res, status, code) {
|
|
48
|
+
respond(res, status, { ok: false, error: { code } });
|
|
49
|
+
}
|
|
50
|
+
function object(value) {
|
|
51
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
52
|
+
}
|
|
53
|
+
function exact(value, fields) {
|
|
54
|
+
return Object.keys(value).every((name) => fields.includes(name));
|
|
55
|
+
}
|
|
56
|
+
async function body(req) {
|
|
57
|
+
const parts = [];
|
|
58
|
+
let size = 0;
|
|
59
|
+
for await (const part of req) {
|
|
60
|
+
const chunk = Buffer.isBuffer(part) ? part : Buffer.from(part);
|
|
61
|
+
size += chunk.length;
|
|
62
|
+
if (size > MAX_BODY)
|
|
63
|
+
throw new MuseBodyError("body-too-large");
|
|
64
|
+
parts.push(chunk);
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(Buffer.concat(parts).toString("utf8"));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
throw new MuseBodyError("malformed-body");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** No caller-supplied object may smuggle bearer values into a durable payload. */
|
|
74
|
+
function secretLike(value) {
|
|
75
|
+
if (typeof value === "string")
|
|
76
|
+
return /(?:\bbearer\s+[A-Za-z0-9._~+/-]{12,}|\b(?:sk|ghp|xox[baprs])-\S{12,}|-----BEGIN [A-Z ]*PRIVATE KEY-----)/iu.test(value);
|
|
77
|
+
if (Array.isArray(value))
|
|
78
|
+
return value.some(secretLike);
|
|
79
|
+
if (object(value))
|
|
80
|
+
return Object.entries(value).some(([key, item]) => /(?:secret|password|credential|api[_-]?key|access[_-]?token|refresh[_-]?token|private[_-]?key)/iu.test(key) || secretLike(item));
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
function sensitive(value, o) {
|
|
84
|
+
if (secretLike(value))
|
|
85
|
+
return true;
|
|
86
|
+
if (typeof value === "string")
|
|
87
|
+
return value.includes(o.readToken) || value.includes(o.proposeToken);
|
|
88
|
+
if (Array.isArray(value))
|
|
89
|
+
return value.some((item) => sensitive(item, o));
|
|
90
|
+
if (object(value))
|
|
91
|
+
return Object.entries(value).some(([key, item]) => key.includes(o.readToken) || key.includes(o.proposeToken) || sensitive(item, o));
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
function safeRequest(item, detail) {
|
|
95
|
+
const canonical = item.fullPayload.value === null ? null : canonicalRender(item.fullPayload.value.value, item.class.value);
|
|
96
|
+
const result = {
|
|
97
|
+
task: item.task.value,
|
|
98
|
+
action_key: item.action_key.value,
|
|
99
|
+
class: item.class.value,
|
|
100
|
+
autonomy: item.autonomy.value,
|
|
101
|
+
state: item.state.value,
|
|
102
|
+
payload_hash: item.payload_hash.value,
|
|
103
|
+
display_hash: canonical?.display_hash ?? null,
|
|
104
|
+
requested_ts: item.requested_ts.value,
|
|
105
|
+
ttl_remaining_ms: item.ttl_remaining_ms.value,
|
|
106
|
+
chain_seq: item.chain.value.seq,
|
|
107
|
+
decision_channel: "telegram",
|
|
108
|
+
telegram_delivery_observed: false,
|
|
109
|
+
native_human_confirmation: false,
|
|
110
|
+
};
|
|
111
|
+
if (detail) {
|
|
112
|
+
// A canonical block is returned only on the read scope, and only when the
|
|
113
|
+
// verified payload store produced it. Never return claimed summaries or
|
|
114
|
+
// free-form gate errors, which may echo caller-supplied sensitive text.
|
|
115
|
+
result["canonical_rendering"] = canonical?.text ?? null;
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
function identity(req, o) {
|
|
120
|
+
let authorizationCount = 0;
|
|
121
|
+
for (let i = 0; i < req.rawHeaders.length; i += 2) {
|
|
122
|
+
if (req.rawHeaders[i]?.toLowerCase() === "authorization")
|
|
123
|
+
authorizationCount += 1;
|
|
124
|
+
}
|
|
125
|
+
if (authorizationCount !== 1)
|
|
126
|
+
return null;
|
|
127
|
+
const header = req.headers.authorization;
|
|
128
|
+
if (typeof header !== "string" || !header.startsWith("Bearer "))
|
|
129
|
+
return null;
|
|
130
|
+
const token = header.slice(7);
|
|
131
|
+
if (equalToken(token, o.readToken))
|
|
132
|
+
return "read";
|
|
133
|
+
if (equalToken(token, o.proposeToken))
|
|
134
|
+
return "propose";
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
export function createMuseServer(o) {
|
|
138
|
+
if (!validMuseOptions(o))
|
|
139
|
+
throw new Error("invalid Muse launch configuration");
|
|
140
|
+
return createServer((req, res) => {
|
|
141
|
+
void (async () => {
|
|
142
|
+
const role = identity(req, o);
|
|
143
|
+
if (role === null)
|
|
144
|
+
return fail(res, 401, "unauthorized");
|
|
145
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
146
|
+
if (url.search !== "" || url.hash !== "")
|
|
147
|
+
return fail(res, 400, "unsupported-query");
|
|
148
|
+
if (req.method === "GET" && url.pathname === "/v1/pending") {
|
|
149
|
+
if (role !== "read")
|
|
150
|
+
return fail(res, 403, "scope-forbidden");
|
|
151
|
+
const queue = buildPendingQueue(o.log, { policy: { dir: o.root } }, new Date().toISOString());
|
|
152
|
+
if (!queue.ok)
|
|
153
|
+
return fail(res, 409, queue.code);
|
|
154
|
+
const visible = queue.requests.filter((item) => !sensitive(item.fullPayload.value?.value, o));
|
|
155
|
+
return respond(res, 200, { ok: true, tenant: o.tenant, requests: visible.map((item) => safeRequest(item, false)), skipped: [...queue.skipped.map((item) => ({ action_key: item.action_key, code: item.code })), ...queue.requests.filter((item) => sensitive(item.fullPayload.value?.value, o)).map((item) => ({ action_key: item.action_key.value, code: "sensitive-payload" }))] });
|
|
156
|
+
}
|
|
157
|
+
if (req.method === "POST" && url.pathname === "/v1/registrations") {
|
|
158
|
+
if (role !== "propose")
|
|
159
|
+
return fail(res, 403, "scope-forbidden");
|
|
160
|
+
const input = await body(req);
|
|
161
|
+
if (!object(input) || !exact(input, ["task", "envelope"]) || typeof input["task"] !== "string" || !KEY.test(input["task"]) || !object(input["envelope"]) || sensitive(input, o))
|
|
162
|
+
return fail(res, 400, "invalid-registration");
|
|
163
|
+
const result = register(o.log, { task: input["task"], envelope: input["envelope"] }, o.actor);
|
|
164
|
+
if (!result.ok)
|
|
165
|
+
return fail(res, 409, result.code);
|
|
166
|
+
return respond(res, 201, { ok: true, tenant: o.tenant, task: result.task, seq: result.record.seq });
|
|
167
|
+
}
|
|
168
|
+
if (req.method === "POST" && url.pathname === "/v1/requests") {
|
|
169
|
+
if (role !== "propose")
|
|
170
|
+
return fail(res, 403, "scope-forbidden");
|
|
171
|
+
const input = await body(req);
|
|
172
|
+
if (!object(input) || !exact(input, ["task", "action_key", "payload"]) || typeof input["task"] !== "string" || !KEY.test(input["task"]) || typeof input["action_key"] !== "string" || !KEY.test(input["action_key"]) || !("payload" in input) || sensitive(input["payload"], o))
|
|
173
|
+
return fail(res, 400, "invalid-request");
|
|
174
|
+
let hash;
|
|
175
|
+
try {
|
|
176
|
+
hash = payloadHash(input["payload"]);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return fail(res, 400, "invalid-payload");
|
|
180
|
+
}
|
|
181
|
+
const read = readGateRecords(o.log);
|
|
182
|
+
if (!read.ok)
|
|
183
|
+
return fail(res, 409, read.code);
|
|
184
|
+
const declared = registeredAction(read.records, input["task"], input["action_key"]);
|
|
185
|
+
if (!declared.ok)
|
|
186
|
+
return fail(res, 409, declared.code);
|
|
187
|
+
if (declared.action.payload_hash !== hash)
|
|
188
|
+
return fail(res, 409, "payload-mismatch");
|
|
189
|
+
const result = request(o.log, {
|
|
190
|
+
task: input["task"], actionKey: input["action_key"], cls: declared.action.class,
|
|
191
|
+
...(declared.action.est_cost_usd === undefined ? {} : { est_cost_usd: declared.action.est_cost_usd }),
|
|
192
|
+
...(declared.action.reversible === undefined ? {} : { reversible: declared.action.reversible }),
|
|
193
|
+
...(declared.action.summary === undefined ? {} : { summary: declared.action.summary }),
|
|
194
|
+
payload: { value: input["payload"] },
|
|
195
|
+
}, o.actor, { policy: { dir: o.root } });
|
|
196
|
+
if (!result.ok)
|
|
197
|
+
return fail(res, 409, result.code);
|
|
198
|
+
return respond(res, 200, { ok: true, tenant: o.tenant, task: input["task"], action_key: input["action_key"], payload_hash: hash, autonomy: result.autonomy, requested: result.record !== null, seq: result.record?.seq ?? null, decision_channel: "telegram", telegram_delivery_observed: false, native_human_confirmation: false });
|
|
199
|
+
}
|
|
200
|
+
if (req.method === "POST" && (url.pathname === "/v1/status" || url.pathname === "/v1/request-detail")) {
|
|
201
|
+
if (role !== "read")
|
|
202
|
+
return fail(res, 403, "scope-forbidden");
|
|
203
|
+
const input = await body(req);
|
|
204
|
+
if (!object(input) || !exact(input, ["task", "action_key"]) || typeof input["task"] !== "string" || !KEY.test(input["task"]) || typeof input["action_key"] !== "string" || !KEY.test(input["action_key"]))
|
|
205
|
+
return fail(res, 400, "invalid-lookup");
|
|
206
|
+
const read = readGateRecords(o.log);
|
|
207
|
+
if (!read.ok)
|
|
208
|
+
return fail(res, 409, read.code);
|
|
209
|
+
const declared = registeredAction(read.records, input["task"], input["action_key"]);
|
|
210
|
+
if (!declared.ok)
|
|
211
|
+
return fail(res, 404, "not-found");
|
|
212
|
+
const load = loadPolicy({ dir: o.root });
|
|
213
|
+
if (!load.ok)
|
|
214
|
+
return fail(res, 409, "policy-unavailable");
|
|
215
|
+
const state = requestState(read.records, input["action_key"], new Date().toISOString(), load.ok ? load.durations.approvalTtlMs : null);
|
|
216
|
+
if (url.pathname === "/v1/status")
|
|
217
|
+
return respond(res, 200, { ok: true, tenant: o.tenant, task: input["task"], action_key: input["action_key"], payload_hash: declared.action.payload_hash ?? null, state: state.state, decision_channel: "telegram", telegram_delivery_observed: false });
|
|
218
|
+
const queue = buildPendingQueue(o.log, { policy: { dir: o.root } }, new Date().toISOString());
|
|
219
|
+
if (!queue.ok)
|
|
220
|
+
return fail(res, 409, queue.code);
|
|
221
|
+
const item = queue.requests.find((entry) => entry.task.value === input["task"] && entry.action_key.value === input["action_key"]);
|
|
222
|
+
if (item === undefined)
|
|
223
|
+
return fail(res, 409, "not-awaiting");
|
|
224
|
+
if (sensitive(item.fullPayload.value?.value, o))
|
|
225
|
+
return fail(res, 409, "sensitive-payload");
|
|
226
|
+
return respond(res, 200, { ok: true, tenant: o.tenant, request: safeRequest(item, true) });
|
|
227
|
+
}
|
|
228
|
+
return fail(res, 404, "not-found");
|
|
229
|
+
})().catch((cause) => {
|
|
230
|
+
if (res.headersSent)
|
|
231
|
+
return;
|
|
232
|
+
if (cause instanceof MuseBodyError)
|
|
233
|
+
return fail(res, cause.code === "body-too-large" ? 413 : 400, cause.code);
|
|
234
|
+
return fail(res, 500, "internal-error");
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/muse/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The store as an archive (APRV-421): a POSIX ustar tar, gzipped, written here
|
|
3
|
+
* rather than by a library.
|
|
4
|
+
*
|
|
5
|
+
* ## Why a writer and not a dependency
|
|
6
|
+
*
|
|
7
|
+
* The repository rule is minimal dependencies, justified per addition, and the
|
|
8
|
+
* justification for a tar library would have to survive one question: what does
|
|
9
|
+
* it do that ninety lines here do not? A ustar header is a fixed 512-byte
|
|
10
|
+
* record with octal fields and a checksum, `node:zlib` already ships the
|
|
11
|
+
* compression, and the archive this verb produces contains a handful of files
|
|
12
|
+
* whose paths are short and known. A dependency would add a supply-chain
|
|
13
|
+
* surface to a process that hands a tenant their own evidence, in exchange for
|
|
14
|
+
* code this file can state completely.
|
|
15
|
+
*
|
|
16
|
+
* ## The contents are a POSITIVE allowlist
|
|
17
|
+
*
|
|
18
|
+
* {@link EXPORTED_PATHS} names what goes in. Everything else is out by not
|
|
19
|
+
* being named, which is the fail-closed direction SPEC.md §11 asks for
|
|
20
|
+
* everywhere else: `.approval/keys`, `.approval/env`, `.approval/daemon` and
|
|
21
|
+
* any `vault.enc` are excluded because nothing admits them, not because a
|
|
22
|
+
* denylist remembered them. A file added to the store next month is absent
|
|
23
|
+
* from this archive until somebody decides it belongs, and that decision is a
|
|
24
|
+
* diff here.
|
|
25
|
+
*
|
|
26
|
+
* `.approval/payloads/` IS on the list, and it is the entry the reasoning
|
|
27
|
+
* turns on. The log records a `payload_hash` for every action a human was
|
|
28
|
+
* shown, and the bytes those hashes name live in that directory. An archive
|
|
29
|
+
* carrying the hashes without the bytes would hand a tenant a chain of
|
|
30
|
+
* references to evidence they no longer hold: a receipt for an exit rather
|
|
31
|
+
* than an exit. Payload bytes are the tenant's own and are not credential
|
|
32
|
+
* material — the vault, the keys and the environment source map are, and
|
|
33
|
+
* those are the things excluded below.
|
|
34
|
+
*
|
|
35
|
+
* ## What the reader is for
|
|
36
|
+
*
|
|
37
|
+
* {@link readTarEntries} exists so that a test, and a conformance reader after
|
|
38
|
+
* it, can assert what an archive CONTAINS rather than trusting the writer that
|
|
39
|
+
* produced it. A test that checked the writer against itself would pass on the
|
|
40
|
+
* day the writer put the vault in.
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* What an export carries, relative to the store root, in archive order.
|
|
44
|
+
*
|
|
45
|
+
* A trailing `/` means "this directory, recursively". Everything else is one
|
|
46
|
+
* file, included when it exists and silently absent when it does not: a store
|
|
47
|
+
* whose index has never been built is an ordinary store, not a broken one.
|
|
48
|
+
*/
|
|
49
|
+
export declare const EXPORTED_PATHS: readonly string[];
|
|
50
|
+
/**
|
|
51
|
+
* Paths an export MUST NOT carry, checked as well as excluded.
|
|
52
|
+
*
|
|
53
|
+
* The allowlist above is what decides; this list is what a test asserts
|
|
54
|
+
* against, so the property "no credential material left the store" is written
|
|
55
|
+
* down as a property and not merely implied by the absence of an entry.
|
|
56
|
+
*/
|
|
57
|
+
export declare const EXCLUDED_PREFIXES: readonly string[];
|
|
58
|
+
/** Any file with this name is credential material, wherever it sits. */
|
|
59
|
+
export declare const EXCLUDED_BASENAMES: readonly string[];
|
|
60
|
+
export interface TarEntry {
|
|
61
|
+
/** POSIX path inside the archive, relative to the store root. */
|
|
62
|
+
path: string;
|
|
63
|
+
/** File contents. */
|
|
64
|
+
data: Buffer;
|
|
65
|
+
/** Modification time, seconds since the epoch. */
|
|
66
|
+
mtime: number;
|
|
67
|
+
}
|
|
68
|
+
type NameSplit = {
|
|
69
|
+
name: string;
|
|
70
|
+
prefix: string;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Split a path into ustar's `prefix` and `name` fields.
|
|
74
|
+
*
|
|
75
|
+
* Refused rather than truncated when it does not fit: a truncated path in an
|
|
76
|
+
* archive is a file restored to the wrong place, which is worse than an export
|
|
77
|
+
* that says plainly it cannot represent this store.
|
|
78
|
+
*/
|
|
79
|
+
export declare function splitTarName(path: string): NameSplit;
|
|
80
|
+
/** Write `entries` as an uncompressed ustar archive. */
|
|
81
|
+
export declare function writeTar(entries: readonly TarEntry[]): Buffer;
|
|
82
|
+
/**
|
|
83
|
+
* Read an uncompressed ustar archive back.
|
|
84
|
+
*
|
|
85
|
+
* Deliberately strict about the checksum: the point of this function is to be
|
|
86
|
+
* an independent reader of what {@link writeTar} produced, and a reader that
|
|
87
|
+
* ignored the field the writer computes would not be independent of it.
|
|
88
|
+
*/
|
|
89
|
+
export declare function readTarEntries(archive: Buffer): TarEntry[];
|
|
90
|
+
/**
|
|
91
|
+
* Is this store-relative path one an export may never carry?
|
|
92
|
+
*
|
|
93
|
+
* `excludedLock` is the ONE lockfile this server itself creates, passed in as
|
|
94
|
+
* an exact path rather than matched as a class. An earlier pass excluded
|
|
95
|
+
* `*.lock` by suffix, which quietly deleted a tenant's own
|
|
96
|
+
* `.approval/payloads/x.lock` from their archive: the tenant's bytes are the
|
|
97
|
+
* tenant's, and a name this runtime happens to use for its own bookkeeping is
|
|
98
|
+
* not a reason to drop somebody else's file. Only the path derived from the
|
|
99
|
+
* log is dropped, and only because the export holds that lock while it walks,
|
|
100
|
+
* so the file exists for exactly the span of the copy and means nothing but
|
|
101
|
+
* "somebody was reading when this was made".
|
|
102
|
+
*
|
|
103
|
+
* The same holds for the names the lock's reclaim derives from that exact path
|
|
104
|
+
* (APRV-479), every one `<lockfile>.<something>`: the claim on a dead holder's
|
|
105
|
+
* lockfile (`.stale.<pid>.<ms>`) and a taker's own lockfile before its rename
|
|
106
|
+
* (`.take.<pid>.<nonce>`), which a writer killed part way leaves behind. They
|
|
107
|
+
* are the lock's bookkeeping, they carry the writing machine's hostname and
|
|
108
|
+
* boot id (which the log itself deliberately does not), and nothing in them is
|
|
109
|
+
* the tenant's.
|
|
110
|
+
*/
|
|
111
|
+
export declare function isExcludedPath(path: string, excludedLock?: string | null): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* A symlink the export walk reached.
|
|
114
|
+
*
|
|
115
|
+
* Thrown rather than returned because it must abort the WHOLE archive from
|
|
116
|
+
* wherever it is found, and an error is the one control flow that cannot be
|
|
117
|
+
* accidentally ignored by a caller that forgot to check a return value.
|
|
118
|
+
*/
|
|
119
|
+
export declare class ExportSymlinkError extends Error {
|
|
120
|
+
/** The store-relative path of the link itself, never its target. */
|
|
121
|
+
readonly path: string;
|
|
122
|
+
constructor(path: string);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A HARD link, which a name-based exclusion cannot see at all (APRV-421,
|
|
126
|
+
* second review pass).
|
|
127
|
+
*
|
|
128
|
+
* `ln .approval/keys/sender.key .approval/log/note.jsonl` produces a second
|
|
129
|
+
* name for the same inode. There is no link to refuse to follow and no target
|
|
130
|
+
* to notice: the excluded path and the admitted path ARE one file, and the
|
|
131
|
+
* admitted name is a perfectly ordinary regular file by every test a walker
|
|
132
|
+
* can apply to it except one — its link count.
|
|
133
|
+
*
|
|
134
|
+
* So `nlink > 1` on a regular file under the store is refused, fail-closed and
|
|
135
|
+
* whole, exactly as a symlink is. The false positive is a tenant who
|
|
136
|
+
* deliberately hard-linked something into their own store, which is rare, is
|
|
137
|
+
* visible in the refusal, and is repaired by copying the file.
|
|
138
|
+
*/
|
|
139
|
+
export declare class ExportHardLinkError extends Error {
|
|
140
|
+
/** The store-relative path of the link, which is also the file. */
|
|
141
|
+
readonly path: string;
|
|
142
|
+
/** How many names this inode has. */
|
|
143
|
+
readonly links: number;
|
|
144
|
+
constructor(path: string, links: number);
|
|
145
|
+
}
|
|
146
|
+
export interface StoreArchive {
|
|
147
|
+
/** The gzipped ustar bytes. */
|
|
148
|
+
bytes: Buffer;
|
|
149
|
+
/** The paths it carries, in archive order. Reported, and asserted by tests. */
|
|
150
|
+
paths: string[];
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Build the export archive for the store rooted at `root`.
|
|
154
|
+
*
|
|
155
|
+
* Reads only; writes nothing anywhere, appends no record, and touches no
|
|
156
|
+
* credential. A path outside the allowlist cannot be reached from here, because
|
|
157
|
+
* the walk starts at the allowlist and nowhere else, and no path is followed
|
|
158
|
+
* through a link.
|
|
159
|
+
*
|
|
160
|
+
* Throws {@link ExportSymlinkError} when the walk meets a symbolic link. The
|
|
161
|
+
* caller turns that into a refusal naming the link's own relative path.
|
|
162
|
+
*/
|
|
163
|
+
export declare function buildStoreArchive(root: string, logPath: string): StoreArchive;
|
|
164
|
+
/** `relative()` in POSIX spelling, for callers that hold absolute paths. */
|
|
165
|
+
export declare function storeRelative(root: string, path: string): string;
|
|
166
|
+
export {};
|