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.
Files changed (270) hide show
  1. package/README.md +83 -914
  2. package/SPEC.md +101 -13
  3. package/cli.js +134 -7
  4. package/dist/src/adapters/smtp.d.ts +18 -0
  5. package/dist/src/adapters/smtp.js +24 -3
  6. package/dist/src/adapters/smtp.js.map +1 -1
  7. package/dist/src/channels/contract.d.ts +72 -0
  8. package/dist/src/channels/contract.js +161 -1
  9. package/dist/src/channels/contract.js.map +1 -1
  10. package/dist/src/channels/relay-server.d.ts +90 -0
  11. package/dist/src/channels/relay-server.js +295 -0
  12. package/dist/src/channels/relay-server.js.map +1 -0
  13. package/dist/src/channels/relay.d.ts +273 -0
  14. package/dist/src/channels/relay.js +564 -0
  15. package/dist/src/channels/relay.js.map +1 -0
  16. package/dist/src/channels/tagging.d.ts +25 -0
  17. package/dist/src/channels/tagging.js +63 -6
  18. package/dist/src/channels/tagging.js.map +1 -1
  19. package/dist/src/channels/telegram-minimal.d.ts +251 -0
  20. package/dist/src/channels/telegram-minimal.js +618 -0
  21. package/dist/src/channels/telegram-minimal.js.map +1 -0
  22. package/dist/src/channels/telegram-webhook.d.ts +273 -0
  23. package/dist/src/channels/telegram-webhook.js +536 -0
  24. package/dist/src/channels/telegram-webhook.js.map +1 -0
  25. package/dist/src/channels/telegram.d.ts +725 -20
  26. package/dist/src/channels/telegram.js +1332 -123
  27. package/dist/src/channels/telegram.js.map +1 -1
  28. package/dist/src/cli/amend.js +358 -5
  29. package/dist/src/cli/amend.js.map +1 -1
  30. package/dist/src/cli/attest.js +43 -6
  31. package/dist/src/cli/attest.js.map +1 -1
  32. package/dist/src/cli/audit-card.d.ts +1 -1
  33. package/dist/src/cli/audit-card.js +40 -1
  34. package/dist/src/cli/audit-card.js.map +1 -1
  35. package/dist/src/cli/audit.d.ts +7 -5
  36. package/dist/src/cli/audit.js +22 -10
  37. package/dist/src/cli/audit.js.map +1 -1
  38. package/dist/src/cli/channel-relay.d.ts +108 -0
  39. package/dist/src/cli/channel-relay.js +334 -0
  40. package/dist/src/cli/channel-relay.js.map +1 -0
  41. package/dist/src/cli/channel-telegram-webhook.d.ts +275 -0
  42. package/dist/src/cli/channel-telegram-webhook.js +716 -0
  43. package/dist/src/cli/channel-telegram-webhook.js.map +1 -0
  44. package/dist/src/cli/channel-telegram.d.ts +347 -28
  45. package/dist/src/cli/channel-telegram.js +648 -56
  46. package/dist/src/cli/channel-telegram.js.map +1 -1
  47. package/dist/src/cli/channel.d.ts +1 -1
  48. package/dist/src/cli/channel.js +5 -1
  49. package/dist/src/cli/channel.js.map +1 -1
  50. package/dist/src/cli/daemon.js +37 -5
  51. package/dist/src/cli/daemon.js.map +1 -1
  52. package/dist/src/cli/doctor.d.ts +63 -0
  53. package/dist/src/cli/doctor.js +856 -111
  54. package/dist/src/cli/doctor.js.map +1 -1
  55. package/dist/src/cli/execute.d.ts +29 -1
  56. package/dist/src/cli/execute.js +387 -21
  57. package/dist/src/cli/execute.js.map +1 -1
  58. package/dist/src/cli/exit-codes.d.ts +12 -0
  59. package/dist/src/cli/exit-codes.js +13 -0
  60. package/dist/src/cli/exit-codes.js.map +1 -1
  61. package/dist/src/cli/gate.d.ts +2 -0
  62. package/dist/src/cli/gate.js +233 -2
  63. package/dist/src/cli/gate.js.map +1 -1
  64. package/dist/src/cli/help.d.ts +10 -1
  65. package/dist/src/cli/help.js +283 -54
  66. package/dist/src/cli/help.js.map +1 -1
  67. package/dist/src/cli/hook.d.ts +485 -6
  68. package/dist/src/cli/hook.js +2370 -70
  69. package/dist/src/cli/hook.js.map +1 -1
  70. package/dist/src/cli/init.js +16 -7
  71. package/dist/src/cli/init.js.map +1 -1
  72. package/dist/src/cli/instructions.js +24 -3
  73. package/dist/src/cli/instructions.js.map +1 -1
  74. package/dist/src/cli/log-anchor.d.ts +55 -0
  75. package/dist/src/cli/log-anchor.js +199 -60
  76. package/dist/src/cli/log-anchor.js.map +1 -1
  77. package/dist/src/cli/log-unlock.d.ts +23 -0
  78. package/dist/src/cli/log-unlock.js +109 -0
  79. package/dist/src/cli/log-unlock.js.map +1 -0
  80. package/dist/src/cli/main.d.ts +0 -12
  81. package/dist/src/cli/main.js +72 -4
  82. package/dist/src/cli/main.js.map +1 -1
  83. package/dist/src/cli/muse.d.ts +2 -0
  84. package/dist/src/cli/muse.js +62 -0
  85. package/dist/src/cli/muse.js.map +1 -0
  86. package/dist/src/cli/payload.js +79 -1
  87. package/dist/src/cli/payload.js.map +1 -1
  88. package/dist/src/cli/scaffold.js +6 -0
  89. package/dist/src/cli/scaffold.js.map +1 -1
  90. package/dist/src/cli/serve.d.ts +53 -0
  91. package/dist/src/cli/serve.js +270 -0
  92. package/dist/src/cli/serve.js.map +1 -0
  93. package/dist/src/cli/up.js +26 -2
  94. package/dist/src/cli/up.js.map +1 -1
  95. package/dist/src/cli/verb-registry.d.ts +2 -0
  96. package/dist/src/cli/verb-registry.js +338 -39
  97. package/dist/src/cli/verb-registry.js.map +1 -1
  98. package/dist/src/cli/wordmark.d.ts +2 -2
  99. package/dist/src/cli/wordmark.js +2 -2
  100. package/dist/src/core/attest-bootstrap.d.ts +95 -0
  101. package/dist/src/core/attest-bootstrap.js +135 -0
  102. package/dist/src/core/attest-bootstrap.js.map +1 -0
  103. package/dist/src/core/attest.d.ts +20 -3
  104. package/dist/src/core/attest.js +25 -0
  105. package/dist/src/core/attest.js.map +1 -1
  106. package/dist/src/core/audit.d.ts +153 -12
  107. package/dist/src/core/audit.js +415 -20
  108. package/dist/src/core/audit.js.map +1 -1
  109. package/dist/src/core/channel-lease.d.ts +322 -0
  110. package/dist/src/core/channel-lease.js +663 -0
  111. package/dist/src/core/channel-lease.js.map +1 -0
  112. package/dist/src/core/command-class.d.ts +51 -0
  113. package/dist/src/core/command-class.js +766 -8
  114. package/dist/src/core/command-class.js.map +1 -1
  115. package/dist/src/core/daemon-host.d.ts +178 -0
  116. package/dist/src/core/daemon-host.js +228 -0
  117. package/dist/src/core/daemon-host.js.map +1 -0
  118. package/dist/src/core/daemon-identity.d.ts +238 -0
  119. package/dist/src/core/daemon-identity.js +252 -0
  120. package/dist/src/core/daemon-identity.js.map +1 -0
  121. package/dist/src/core/gate.d.ts +364 -6
  122. package/dist/src/core/gate.js +802 -27
  123. package/dist/src/core/gate.js.map +1 -1
  124. package/dist/src/core/harness-version.d.ts +128 -4
  125. package/dist/src/core/harness-version.js +179 -12
  126. package/dist/src/core/harness-version.js.map +1 -1
  127. package/dist/src/core/harness-wait.d.ts +82 -3
  128. package/dist/src/core/harness-wait.js +89 -3
  129. package/dist/src/core/harness-wait.js.map +1 -1
  130. package/dist/src/core/log-lock.d.ts +435 -0
  131. package/dist/src/core/log-lock.js +1093 -0
  132. package/dist/src/core/log-lock.js.map +1 -0
  133. package/dist/src/core/log-subscribe.d.ts +21 -0
  134. package/dist/src/core/log-subscribe.js +16 -6
  135. package/dist/src/core/log-subscribe.js.map +1 -1
  136. package/dist/src/core/log-write-layer.d.ts +55 -0
  137. package/dist/src/core/log-write-layer.js +91 -0
  138. package/dist/src/core/log-write-layer.js.map +1 -0
  139. package/dist/src/core/log.d.ts +130 -22
  140. package/dist/src/core/log.js +473 -50
  141. package/dist/src/core/log.js.map +1 -1
  142. package/dist/src/core/payload-census.d.ts +51 -0
  143. package/dist/src/core/payload-census.js +125 -2
  144. package/dist/src/core/payload-census.js.map +1 -1
  145. package/dist/src/core/payload-store.d.ts +31 -0
  146. package/dist/src/core/payload-store.js +113 -8
  147. package/dist/src/core/payload-store.js.map +1 -1
  148. package/dist/src/core/payload.d.ts +18 -3
  149. package/dist/src/core/payload.js +20 -4
  150. package/dist/src/core/payload.js.map +1 -1
  151. package/dist/src/core/policy-diff.d.ts +10 -0
  152. package/dist/src/core/policy-diff.js +10 -2
  153. package/dist/src/core/policy-diff.js.map +1 -1
  154. package/dist/src/core/policy-explain.d.ts +7 -1
  155. package/dist/src/core/policy-explain.js +16 -1
  156. package/dist/src/core/policy-explain.js.map +1 -1
  157. package/dist/src/core/policy-load.d.ts +24 -0
  158. package/dist/src/core/policy-load.js.map +1 -1
  159. package/dist/src/core/policy-match.d.ts +38 -0
  160. package/dist/src/core/policy-match.js +35 -0
  161. package/dist/src/core/policy-match.js.map +1 -1
  162. package/dist/src/core/policy-proposal.d.ts +40 -0
  163. package/dist/src/core/policy-proposal.js +37 -16
  164. package/dist/src/core/policy-proposal.js.map +1 -1
  165. package/dist/src/core/prompt-layout.d.ts +186 -1
  166. package/dist/src/core/prompt-layout.js +345 -3
  167. package/dist/src/core/prompt-layout.js.map +1 -1
  168. package/dist/src/core/read-scope.d.ts +10 -0
  169. package/dist/src/core/read-scope.js +4 -0
  170. package/dist/src/core/read-scope.js.map +1 -1
  171. package/dist/src/core/run-payload.d.ts +160 -0
  172. package/dist/src/core/run-payload.js +304 -0
  173. package/dist/src/core/run-payload.js.map +1 -0
  174. package/dist/src/core/sender-identity.d.ts +59 -11
  175. package/dist/src/core/sender-identity.js +79 -11
  176. package/dist/src/core/sender-identity.js.map +1 -1
  177. package/dist/src/core/state.d.ts +52 -1
  178. package/dist/src/core/state.js +52 -6
  179. package/dist/src/core/state.js.map +1 -1
  180. package/dist/src/core/telegram-config.d.ts +79 -0
  181. package/dist/src/core/telegram-config.js +108 -0
  182. package/dist/src/core/telegram-config.js.map +1 -1
  183. package/dist/src/core/validate.d.ts +20 -6
  184. package/dist/src/core/validate.js +0 -0
  185. package/dist/src/core/validate.js.map +1 -1
  186. package/dist/src/core/verify.d.ts +23 -1
  187. package/dist/src/core/verify.js +20 -2
  188. package/dist/src/core/verify.js.map +1 -1
  189. package/dist/src/daemon/audit.d.ts +80 -5
  190. package/dist/src/daemon/audit.js +126 -6
  191. package/dist/src/daemon/audit.js.map +1 -1
  192. package/dist/src/daemon/daemon.d.ts +174 -2
  193. package/dist/src/daemon/daemon.js +298 -7
  194. package/dist/src/daemon/daemon.js.map +1 -1
  195. package/dist/src/daemon/git-evidence.d.ts +2 -2
  196. package/dist/src/daemon/git-evidence.js +1 -1
  197. package/dist/src/daemon/projection.d.ts +13 -2
  198. package/dist/src/daemon/projection.js +39 -6
  199. package/dist/src/daemon/projection.js.map +1 -1
  200. package/dist/src/mcp/server.d.ts +39 -3
  201. package/dist/src/mcp/server.js +22 -3
  202. package/dist/src/mcp/server.js.map +1 -1
  203. package/dist/src/muse/server.d.ts +16 -0
  204. package/dist/src/muse/server.js +238 -0
  205. package/dist/src/muse/server.js.map +1 -0
  206. package/dist/src/serve/archive.d.ts +166 -0
  207. package/dist/src/serve/archive.js +363 -0
  208. package/dist/src/serve/archive.js.map +1 -0
  209. package/dist/src/serve/arguments.d.ts +93 -0
  210. package/dist/src/serve/arguments.js +228 -0
  211. package/dist/src/serve/arguments.js.map +1 -0
  212. package/dist/src/serve/credentials.d.ts +91 -0
  213. package/dist/src/serve/credentials.js +143 -0
  214. package/dist/src/serve/credentials.js.map +1 -0
  215. package/dist/src/serve/follow.d.ts +86 -0
  216. package/dist/src/serve/follow.js +101 -0
  217. package/dist/src/serve/follow.js.map +1 -0
  218. package/dist/src/serve/hook-thread.d.ts +134 -0
  219. package/dist/src/serve/hook-thread.js +333 -0
  220. package/dist/src/serve/hook-thread.js.map +1 -0
  221. package/dist/src/serve/hook-worker.d.ts +134 -0
  222. package/dist/src/serve/hook-worker.js +173 -0
  223. package/dist/src/serve/hook-worker.js.map +1 -0
  224. package/dist/src/serve/server.d.ts +412 -0
  225. package/dist/src/serve/server.js +1251 -0
  226. package/dist/src/serve/server.js.map +1 -0
  227. package/docs/README-extended.md +768 -0
  228. package/docs/cli-reference.md +2139 -67
  229. package/package.json +4 -2
  230. package/schema/event.schema.json +413 -30
  231. package/schema/fixtures/event/invalid/audit-lock-reclaimed-age-without-created.json +19 -0
  232. package/schema/fixtures/event/invalid/audit-lock-reclaimed-agent-actor.json +14 -0
  233. package/schema/fixtures/event/invalid/audit-lock-reclaimed-lockfile-path.json +14 -0
  234. package/schema/fixtures/event/invalid/audit-lock-reclaimed-operator-cleared-system-actor.json +14 -0
  235. package/schema/fixtures/event/invalid/audit-lock-reclaimed-unknown-reason.json +14 -0
  236. package/schema/fixtures/event/invalid/audit-lock-reclaimed-without-age.json +13 -0
  237. package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +6 -1
  238. package/schema/fixtures/event/invalid/audit-reviewed-bad-payload-hash.json +21 -0
  239. package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +5 -0
  240. package/schema/fixtures/event/invalid/audit-reviewed-no-subject-hash.json +19 -0
  241. package/schema/fixtures/event/invalid/audit-reviewed-no-subject-seq.json +19 -0
  242. package/schema/fixtures/event/invalid/audit-reviewed-no-verdict-source.json +19 -0
  243. package/schema/fixtures/event/invalid/audit-reviewed-no-verdict.json +19 -0
  244. package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +6 -1
  245. package/schema/fixtures/event/invalid/audit-sampled-no-policy-hash.json +14 -0
  246. package/schema/fixtures/event/invalid/daemon-id-malformed.json +15 -0
  247. package/schema/fixtures/event/invalid/execution-started-truncated-policy-sha256.json +18 -0
  248. package/schema/fixtures/event/invalid/harness-cap-at-margin.json +16 -0
  249. package/schema/fixtures/event/invalid/harness-cap-fractional.json +16 -0
  250. package/schema/fixtures/event/invalid/harness-cap-negative.json +16 -0
  251. package/schema/fixtures/event/invalid/harness-cap-string.json +16 -0
  252. package/schema/fixtures/event/invalid/harness-cap-without-harness-execution.json +16 -0
  253. package/schema/fixtures/event/invalid/harness-cap-zero.json +16 -0
  254. package/schema/fixtures/event/valid/approval-requested-harness-cap.json +18 -0
  255. package/schema/fixtures/event/valid/audit-lock-reclaimed-implausible-created.json +18 -0
  256. package/schema/fixtures/event/valid/audit-lock-reclaimed-legacy.json +14 -0
  257. package/schema/fixtures/event/valid/audit-lock-reclaimed-unlock.json +19 -0
  258. package/schema/fixtures/event/valid/audit-lock-reclaimed.json +19 -0
  259. package/schema/fixtures/event/valid/audit-reviewed-reaction.json +5 -0
  260. package/schema/fixtures/event/valid/audit-reviewed.json +6 -1
  261. package/schema/fixtures/event/valid/audit-sampled.json +2 -1
  262. package/schema/fixtures/event/valid/daemon-identity-declared.json +17 -0
  263. package/schema/fixtures/event/valid/daemon-identity.json +16 -0
  264. package/schema/fixtures/event/valid/execution-started-policy-sha256.json +18 -0
  265. package/schema/fixtures/event/valid/harness-kind-hermes.json +23 -0
  266. package/schema/fixtures/policy/invalid/daemons-malformed-id.json +10 -0
  267. package/schema/fixtures/policy/valid/daemons-allowlist.json +19 -0
  268. package/schema/fixtures/policy-md/invalid/schema-invalid-agent-may-request.md +21 -0
  269. package/schema/fixtures/policy-md/valid/agent-may-request.md +24 -0
  270. package/schema/policy.schema.json +102 -6
@@ -0,0 +1,564 @@
1
+ /**
2
+ * The authenticated relay channel: its protocol (SPEC.md §10.3, APRV-455).
3
+ *
4
+ * Agent Village residents review their approval.md settings in the Edge City
5
+ * onboarding app, and that review is meant to be the attestation of the policy
6
+ * in force. The app signs the resident in through EdgeOS, and the operator's
7
+ * control plane is what holds that session. This module is the daemon's side
8
+ * of the hop from there: a gesture the control plane carries, attributed to the
9
+ * EdgeOS `/humans/me` id of the session that made it, arriving over an
10
+ * authenticated HTTP post (`channels/relay-server.ts`) and turned into a log
11
+ * event by the SAME function a Telegram tap goes through.
12
+ *
13
+ * ## What the relay is trusted for, and what it is not
14
+ *
15
+ * The daemon trusts the relay's report of WHO acted, exactly as it trusts the
16
+ * Telegram Bot API's report of `callback_query.from.id`. That is operator
17
+ * trust (APRV-422: no hosted service has authority over decisions), and it is
18
+ * the whole of what the shared secret buys: a post that carries it is a report
19
+ * from the operator's relay, and a post that does not is nothing. Every
20
+ * question of AUTHORITY is answered here, by the runtime, from the verified log
21
+ * and the attested policy:
22
+ *
23
+ * - which approver the account is, by `core/sender-identity.ts` against the
24
+ * attested mapping, exactly as a tap is resolved;
25
+ * - whether that approver may decide this request, attest this policy, or
26
+ * neither, by `core/gate.ts` and `core/policy-proposal.ts`, unchanged;
27
+ * - whether the request is still answerable (expiry, policy drift, an earlier
28
+ * answer), by the same gate, with the same codes.
29
+ *
30
+ * The relay holds NO human identity of its own. A Telegram listener launched
31
+ * `--as human:carter` falls back to that identity for a channel the policy maps
32
+ * no sender for; this surface does not, ever (`requireSenderMapping`). A post
33
+ * naming an account the attested policy does not map is refused
34
+ * `sender-unmapped` with one `audit.decision_refused`, and a policy that maps
35
+ * nobody on `edgeos` refuses every gesture the same way.
36
+ *
37
+ * ## The body, closed
38
+ *
39
+ * ```json
40
+ * { "gesture": "attest" | "decline", "policy_sha256": "<64 hex>",
41
+ * "sender": { "channel": "edgeos", "id": "<EdgeOS /humans/me id>" },
42
+ * "nonce": "<16..128 of A-Z a-z 0-9 _ ->", "issued_at": "<RFC 3339 UTC>" }
43
+ * { "gesture": "grant" | "reject", "action_key": "<the request's key>",
44
+ * "sender": { ... }, "nonce": "...", "issued_at": "..." }
45
+ * { "gesture": "propose", "policy_sha256": "<64 hex>",
46
+ * "nonce": "...", "issued_at": "..." }
47
+ * ```
48
+ *
49
+ * Unknown keys are refused rather than ignored, so a field that looks like it
50
+ * carries authority (an `actor`, a second sender, a `ts`) cannot ride along on
51
+ * a valid post and be read by something later. `issued_at` is used for one
52
+ * thing only, the freshness window that bounds how long a nonce must be
53
+ * remembered; it is never recorded, and every record's `ts` is still the
54
+ * runtime's own (SPEC.md §11.1 invariant 2).
55
+ *
56
+ * ## Why `propose` exists here
57
+ *
58
+ * The control plane renders the resident's policy and writes it into the
59
+ * store; somebody then has to append the `policy.proposed` an acceptance
60
+ * answers. The existing route for that is `approval policy amend`, which is a
61
+ * git-aware terminal ceremony that blocks for its own `--wait`. `propose` is the
62
+ * same `core/policy-proposal.ts` call, under the same secret, recorded under the
63
+ * actor the operator started the relay with (`--proposer`), and it returns at
64
+ * once. It binds the hash the control plane rendered: bytes on disk that are not
65
+ * the bytes it names are refused, so the prompt the resident answers is the file
66
+ * the control plane showed them.
67
+ *
68
+ * It also admits the one proposal `policy amend` refuses: bytes that are
69
+ * ALREADY the policy in force (`ProposeInput.reaffirm`). The onboarding review
70
+ * most residents make is "accept, unchanged", and the operator attested those
71
+ * bytes at provisioning (APRV-449). The reaffirmation changes the attester of
72
+ * record and nothing else: same hash, a diff that reads "no semantic change",
73
+ * and an acceptance resolved against the policy in force like any other.
74
+ *
75
+ * ## No token leaves this surface
76
+ *
77
+ * A grant may mint a single-use execution token for the deciding surface
78
+ * (manual delivery). A relay is not a place a token can go: the control plane
79
+ * is not the requester and the HTTP response is not a secrets channel. The
80
+ * token `recordChannelDecision` returns is dropped here, unread, and the
81
+ * response says only whether one was minted. The deployments this exists for
82
+ * mint none (a harness-executed request) or seal it to the requester's own
83
+ * key, so nothing is lost in practice, and the residual is stated rather than
84
+ * hidden: a manual-delivery request granted through the relay has a token
85
+ * nobody holds, and its requester asks again.
86
+ */
87
+ import { createHash } from "node:crypto";
88
+ import { linkSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
89
+ import { join } from "node:path";
90
+ import { POLICY_HASH_FIELD, policyBytesHash } from "../core/attest.js";
91
+ import { payloadStoreDirFor } from "../core/payload-store.js";
92
+ import { attestationActionKey, inForcePolicyText, isAttestationActionKey, openProposalFor, proposalPolicyPath, proposeAttestation, } from "../core/policy-proposal.js";
93
+ import { isEdgeosSenderId } from "../core/sender-identity.js";
94
+ import { readVerifiedRecords } from "../core/state.js";
95
+ import { tick } from "../core/clock.js";
96
+ import { recordChannelDecision } from "./contract.js";
97
+ /** The channel name every relay gesture is recorded under, and its senders' namespace. */
98
+ export const RELAY_CHANNEL = "edgeos";
99
+ /** The header the shared secret travels in, lowercased as Node presents it. */
100
+ export const RELAY_SECRET_HEADER = "x-approval-relay-secret";
101
+ /** The one path this transport answers. */
102
+ export const RELAY_PATH = "/relay/gesture";
103
+ /** The interface it binds when the operator names none. Loopback, always. */
104
+ export const RELAY_DEFAULT_HOST = "127.0.0.1";
105
+ /** The port it binds when the operator names none: `serve` is 4682, the webhook 4683. */
106
+ export const RELAY_DEFAULT_PORT = 4684;
107
+ /** Largest body accepted. A gesture is a handful of short strings. */
108
+ export const RELAY_MAX_BODY_BYTES = 16 * 1024;
109
+ /** How much of an oversized body is drained before the socket is dropped. */
110
+ export const RELAY_DRAIN_LIMIT_BYTES = 1024 * 1024;
111
+ /**
112
+ * How far `issued_at` may sit from this process's clock, either side.
113
+ *
114
+ * Five minutes: a control plane and a sandbox on the same platform agree to
115
+ * well under a second, and a gesture older than this was not made by a person
116
+ * who is still looking at the screen that made it.
117
+ */
118
+ export const RELAY_GESTURE_WINDOW_MS = 5 * 60_000;
119
+ /**
120
+ * How long a claimed nonce is remembered.
121
+ *
122
+ * Longer than the whole window a fresh gesture can occupy (issued up to one
123
+ * window in the future, accepted up to one window late), with a minute of
124
+ * margin, so a replay is refused by the nonce while it is still fresh and by
125
+ * the window once the nonce is forgotten. There is no instant at which neither
126
+ * holds.
127
+ */
128
+ export const RELAY_NONCE_RETENTION_MS = 2 * RELAY_GESTURE_WINDOW_MS + 60_000;
129
+ /**
130
+ * How long a relay proposal stays open (APRV-455 refuter L2).
131
+ *
132
+ * The onboarding review is answered in the screen that rendered it, so an hour
133
+ * is generous; past it the prompt retires from every channel queue by
134
+ * derivation, and a control plane whose resident came back later proposes
135
+ * again. Until a proposal is attested the bytes on disk are not the policy in
136
+ * force, so the control plane restores the in-force bytes when a proposal is
137
+ * declined or lapses.
138
+ */
139
+ export const RELAY_PROPOSAL_TTL_MS = 60 * 60_000;
140
+ /** The actor a proposal is recorded under when the operator names none. */
141
+ export const RELAY_DEFAULT_PROPOSER = "agent:edgeos-relay";
142
+ /**
143
+ * What {@link recordChannelDecision} is handed as the configured actor.
144
+ *
145
+ * Never recorded. `requireSenderMapping` refuses every path that would fall
146
+ * back to it, and it is an `agent:` id on purpose, so that if any such path
147
+ * existed the gate would refuse it `actor-not-human` rather than record a
148
+ * decision under it.
149
+ */
150
+ const RELAY_UNMAPPED_ACTOR = "agent:edgeos-relay";
151
+ /** The gestures, closed. */
152
+ export const RELAY_GESTURES = ["propose", "attest", "decline", "grant", "reject"];
153
+ /**
154
+ * Every refusal THIS transport makes, and nothing else. Frozen, per SPEC.md
155
+ * §11.1 invariant 6, and pinned by `conformance/vectors/refusal-unions.v1.json`
156
+ * because the control plane on the other side of the hop is a second
157
+ * implementation that branches on these strings.
158
+ *
159
+ * Refusals the GATE or the decision surface makes (`sender-unmapped`,
160
+ * `expired`, `policy-drift`, `already-decided`, `proposal-stale`,
161
+ * `proposal-not-found`, ...) are not here: they arrive verbatim under
162
+ * `refusal` in the response body, from their own unions, exactly as a Telegram
163
+ * tap would surface them.
164
+ *
165
+ * None of these appends to the log. A post the relay refuses is a fact about
166
+ * the transport and is counted and reported on stderr, because an endpoint
167
+ * that wrote its refusals into the approval log would let anyone who can reach
168
+ * it grow the record a human is asked to trust.
169
+ */
170
+ export const RELAY_REFUSAL_CODES = [
171
+ /** No secret header, or one that is not the launch environment's secret. */
172
+ "relay-secret-mismatch",
173
+ /** More than one secret header. Malformed, and not resolvable by choosing. */
174
+ "relay-duplicate-secret-header",
175
+ /** The request line and the Host header do not form a URL. */
176
+ "relay-malformed-request",
177
+ /** A path this transport does not serve. */
178
+ "relay-unknown-path",
179
+ /** The right path, a method other than POST. */
180
+ "relay-method-not-allowed",
181
+ /** Larger than {@link RELAY_MAX_BODY_BYTES}. */
182
+ "relay-body-too-large",
183
+ /** The body could not be read off the socket, or is not JSON. */
184
+ "relay-body-unreadable",
185
+ /** JSON that is not a gesture of the closed shape. */
186
+ "relay-body-invalid",
187
+ /**
188
+ * The sender is not an EdgeOS account this transport can report: a channel
189
+ * other than `edgeos`, or an id outside the grammar `senders.edgeos` pins.
190
+ */
191
+ "relay-sender-invalid",
192
+ /** `issued_at` is further than {@link RELAY_GESTURE_WINDOW_MS} from now. */
193
+ "relay-gesture-stale",
194
+ /** This nonce was already used within {@link RELAY_NONCE_RETENTION_MS}. */
195
+ "relay-nonce-replayed",
196
+ /**
197
+ * The nonce ledger could not be written, so a replay could not be refused.
198
+ * Fails closed: nothing is decided without it.
199
+ */
200
+ "relay-nonce-unavailable",
201
+ /**
202
+ * `propose` named a hash that is not the hash of the policy file on disk.
203
+ * Distinct from the gate's `proposal-stale`, which is the same fact found at
204
+ * ACCEPTANCE: this one is found before anything is proposed.
205
+ */
206
+ "relay-policy-mismatch",
207
+ /** The gesture threw while being applied. Nothing is claimed about the log. */
208
+ "relay-handler-failed",
209
+ ];
210
+ const NONCE_PATTERN = /^[A-Za-z0-9_-]{16,128}$/u;
211
+ const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
212
+ /** RFC 3339 in UTC, `Z` only: one spelling, so two implementations agree on it. */
213
+ const ISSUED_AT_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/u;
214
+ const ACTION_KEY_MAX = 512;
215
+ /** The keys each gesture admits. Anything else is `relay-body-invalid`. */
216
+ const ALLOWED_KEYS = {
217
+ propose: ["gesture", "policy_sha256", "nonce", "issued_at"],
218
+ attest: ["gesture", "policy_sha256", "sender", "nonce", "issued_at"],
219
+ decline: ["gesture", "policy_sha256", "sender", "nonce", "issued_at"],
220
+ grant: ["gesture", "action_key", "sender", "nonce", "issued_at"],
221
+ reject: ["gesture", "action_key", "sender", "nonce", "issued_at"],
222
+ };
223
+ function invalid(message) {
224
+ return { ok: false, code: "relay-body-invalid", message };
225
+ }
226
+ function isGestureName(value) {
227
+ return typeof value === "string" && RELAY_GESTURES.includes(value);
228
+ }
229
+ /**
230
+ * Check one decoded body against the closed shape.
231
+ *
232
+ * Pure: no clock, no file, no log. Freshness and the nonce are the server's,
233
+ * because both need state this function deliberately does not hold.
234
+ */
235
+ export function parseRelayGesture(value) {
236
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
237
+ return invalid("the body is not a JSON object");
238
+ }
239
+ const body = value;
240
+ const name = body["gesture"];
241
+ if (!isGestureName(name)) {
242
+ return invalid(`gesture must be one of ${RELAY_GESTURES.join(", ")}`);
243
+ }
244
+ const allowed = ALLOWED_KEYS[name];
245
+ for (const key of Object.keys(body)) {
246
+ if (!allowed.includes(key)) {
247
+ return invalid(`${JSON.stringify(key)} is not a field of a ${name} gesture (${allowed.join(", ")}). Unknown fields are refused rather than ignored, so nothing that looks like authority can ride along on a valid post`);
248
+ }
249
+ }
250
+ for (const key of allowed) {
251
+ if (!(key in body))
252
+ return invalid(`a ${name} gesture requires ${JSON.stringify(key)}`);
253
+ }
254
+ const nonce = body["nonce"];
255
+ if (typeof nonce !== "string" || !NONCE_PATTERN.test(nonce)) {
256
+ return invalid("nonce must be 16 to 128 characters of A-Z, a-z, 0-9, underscore and hyphen");
257
+ }
258
+ const issuedAt = body["issued_at"];
259
+ const issuedAtMs = typeof issuedAt === "string" && ISSUED_AT_PATTERN.test(issuedAt) ? Date.parse(issuedAt) : Number.NaN;
260
+ if (Number.isNaN(issuedAtMs)) {
261
+ return invalid("issued_at must be an RFC 3339 instant in UTC, ending in Z");
262
+ }
263
+ if (name === "propose") {
264
+ const sha = body["policy_sha256"];
265
+ if (typeof sha !== "string" || !SHA256_PATTERN.test(sha)) {
266
+ return invalid("policy_sha256 must be 64 lowercase hex characters");
267
+ }
268
+ return { ok: true, gesture: { gesture: name, policySha256: sha, nonce, issuedAtMs } };
269
+ }
270
+ const senderValue = body["sender"];
271
+ if (typeof senderValue !== "object" || senderValue === null || Array.isArray(senderValue)) {
272
+ return invalid("sender must be an object {channel, id}");
273
+ }
274
+ const senderBody = senderValue;
275
+ for (const key of Object.keys(senderBody)) {
276
+ if (key !== "channel" && key !== "id") {
277
+ return invalid(`${JSON.stringify(key)} is not a field of sender (channel, id)`);
278
+ }
279
+ }
280
+ if (senderBody["channel"] !== RELAY_CHANNEL) {
281
+ return {
282
+ ok: false,
283
+ code: "relay-sender-invalid",
284
+ message: `sender.channel must be ${JSON.stringify(RELAY_CHANNEL)}: this transport reports EdgeOS sessions and nothing else, and a gesture naming another channel's account would borrow that channel's attribution`,
285
+ };
286
+ }
287
+ const id = senderBody["id"];
288
+ if (typeof id !== "string" || !isEdgeosSenderId(id)) {
289
+ return {
290
+ ok: false,
291
+ code: "relay-sender-invalid",
292
+ message: "sender.id is not an EdgeOS id this runtime compares: an ASCII letter or digit, then up to 127 letters, digits, '.', '_' or '-'. It is the raw /humans/me id, never a digest and never an email address",
293
+ };
294
+ }
295
+ const sender = { channel: RELAY_CHANNEL, id };
296
+ if (name === "attest" || name === "decline") {
297
+ const sha = body["policy_sha256"];
298
+ if (typeof sha !== "string" || !SHA256_PATTERN.test(sha)) {
299
+ return invalid("policy_sha256 must be 64 lowercase hex characters");
300
+ }
301
+ return { ok: true, gesture: { gesture: name, policySha256: sha, sender, nonce, issuedAtMs } };
302
+ }
303
+ const actionKey = body["action_key"];
304
+ if (typeof actionKey !== "string" || actionKey.length === 0 || actionKey.length > ACTION_KEY_MAX) {
305
+ return invalid(`action_key must be a non-empty string of at most ${String(ACTION_KEY_MAX)} characters`);
306
+ }
307
+ if (isAttestationActionKey(actionKey)) {
308
+ // The gate would route it to the attestation path, which is a door this
309
+ // shape does not offer under that name: an attestation is `attest` with a
310
+ // policy hash, so the gesture a person made and the gesture recorded are
311
+ // never two different words.
312
+ return invalid("a policy.attest: key is answered with the attest or decline gesture, not grant or reject");
313
+ }
314
+ return { ok: true, gesture: { gesture: name, actionKey, sender, nonce, issuedAtMs } };
315
+ }
316
+ /** Is `issuedAtMs` within the window of `nowMs`? */
317
+ export function relayGestureFresh(issuedAtMs, nowMs) {
318
+ return Math.abs(nowMs - issuedAtMs) <= RELAY_GESTURE_WINDOW_MS;
319
+ }
320
+ let tempCounter = 0;
321
+ /** How often the ledger prunes, at most. */
322
+ const PRUNE_INTERVAL_MS = 60_000;
323
+ /** The file ledger described at {@link NonceLedger}. */
324
+ export function fileNonceLedger(dir) {
325
+ let lastPrune = Number.NEGATIVE_INFINITY;
326
+ function prune(nowMs) {
327
+ if (nowMs - lastPrune < PRUNE_INTERVAL_MS)
328
+ return;
329
+ lastPrune = nowMs;
330
+ let names;
331
+ try {
332
+ names = readdirSync(dir);
333
+ }
334
+ catch {
335
+ return;
336
+ }
337
+ for (const name of names) {
338
+ const path = join(dir, name);
339
+ try {
340
+ if (/^[a-f0-9]{64}$/u.test(name)) {
341
+ // The claim time is the file's own content rather than its mtime, so
342
+ // a copy or a restore that touched every file does not extend them
343
+ // all. A claim file is linked into place already holding its content
344
+ // (see `claim`), so it is never seen empty; anything that does not
345
+ // parse as a positive time is judged by mtime instead, which is the
346
+ // strict direction for a file this pass did not write.
347
+ const text = readFileSync(path, "utf8").trim();
348
+ const claimed = /^[1-9][0-9]*$/u.test(text) ? Number(text) : Number.NaN;
349
+ const age = Number.isFinite(claimed) ? nowMs - claimed : nowMs - statSync(path).mtimeMs;
350
+ if (age > RELAY_NONCE_RETENTION_MS)
351
+ unlinkSync(path);
352
+ }
353
+ else if (name.endsWith(".tmp") && nowMs - statSync(path).mtimeMs > RELAY_NONCE_RETENTION_MS) {
354
+ // A temp left by a claim that died between write and link.
355
+ unlinkSync(path);
356
+ }
357
+ }
358
+ catch {
359
+ // Another process pruned it first, or it is mid-write: either way it
360
+ // is not this pass's to judge.
361
+ }
362
+ }
363
+ }
364
+ return {
365
+ claim(nonce, nowMs) {
366
+ try {
367
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
368
+ }
369
+ catch (cause) {
370
+ return { error: cause instanceof Error ? cause.message : String(cause) };
371
+ }
372
+ prune(nowMs);
373
+ const path = join(dir, createHash("sha256").update(nonce, "utf8").digest("hex"));
374
+ // Written whole to a private temp, then LINKED into place: `link` fails
375
+ // with EEXIST exactly as `O_EXCL` does, so one claimant wins, and the
376
+ // claim file never exists without its content, so a concurrent prune can
377
+ // never judge a half-made claim (APRV-455 refuter M1).
378
+ tempCounter += 1;
379
+ const temp = `${path}.${String(process.pid)}.${String(tempCounter)}.tmp`;
380
+ try {
381
+ writeFileSync(temp, `${String(nowMs)}\n`, { encoding: "utf8", mode: 0o600, flag: "wx" });
382
+ }
383
+ catch (cause) {
384
+ return { error: cause instanceof Error ? cause.message : String(cause) };
385
+ }
386
+ try {
387
+ linkSync(temp, path);
388
+ return "claimed";
389
+ }
390
+ catch (cause) {
391
+ if (cause.code !== "EEXIST") {
392
+ return { error: cause instanceof Error ? cause.message : String(cause) };
393
+ }
394
+ }
395
+ finally {
396
+ try {
397
+ unlinkSync(temp);
398
+ }
399
+ catch {
400
+ // Already gone; its absence is the desired state.
401
+ }
402
+ }
403
+ // It exists. A file left by a claim older than the retention is a nonce
404
+ // this ledger is no longer obliged to remember, and the window has
405
+ // already refused anything that old, so reaching here with one means
406
+ // the prune has not run yet; it is still refused, which is the strict
407
+ // direction and costs a legitimate caller nothing (nonces are fresh).
408
+ return "replayed";
409
+ },
410
+ };
411
+ }
412
+ /** A transport refusal as a response. Never appended. */
413
+ export function relayRefusal(status, code, message) {
414
+ return { status, body: { error: { code, message } } };
415
+ }
416
+ /** The policy file this process enforces, resolved as the gate resolves it. */
417
+ function relayPolicyPath(options) {
418
+ const policy = options.policy;
419
+ if (policy?.file !== undefined)
420
+ return policy.file;
421
+ return proposalPolicyPath(policy?.dir ?? process.cwd());
422
+ }
423
+ /**
424
+ * Turn one checked gesture into a log event, or into the refusal that replaces
425
+ * one.
426
+ *
427
+ * Decisions and attestations go through {@link recordChannelDecision}, the one
428
+ * place a reported gesture becomes a log event, with `requireSenderMapping` set
429
+ * and the channel named `edgeos`. A proposal goes through `proposeAttestation`.
430
+ * Nothing here decides anything those two do not already decide.
431
+ */
432
+ export function applyRelayGesture(logPath, gesture, options) {
433
+ if (gesture.gesture === "propose")
434
+ return applyPropose(logPath, gesture, options);
435
+ let decision;
436
+ if ("policySha256" in gesture) {
437
+ decision = {
438
+ action_key: attestationActionKey(gesture.policySha256),
439
+ decision: gesture.gesture === "attest" ? "grant" : "reject",
440
+ deliveryId: `relay:${gesture.nonce}`,
441
+ sender: gesture.sender,
442
+ };
443
+ }
444
+ else {
445
+ decision = {
446
+ action_key: gesture.actionKey,
447
+ decision: gesture.gesture,
448
+ deliveryId: `relay:${gesture.nonce}`,
449
+ sender: gesture.sender,
450
+ };
451
+ }
452
+ const result = recordChannelDecision(logPath, decision, { actor: RELAY_UNMAPPED_ACTOR, channel: RELAY_CHANNEL, requireSenderMapping: true }, options.gateOptions);
453
+ // `result.token` is deliberately never read: see the module doc. A raw token
454
+ // has no route off this surface, into a response, a log line or stderr.
455
+ const outcome = result.outcome;
456
+ if (!outcome.ok) {
457
+ return {
458
+ status: 409,
459
+ body: {
460
+ ok: false,
461
+ gesture: gesture.gesture,
462
+ refusal: { code: outcome.code, message: outcome.message },
463
+ },
464
+ };
465
+ }
466
+ return {
467
+ status: 200,
468
+ body: {
469
+ ok: true,
470
+ gesture: gesture.gesture,
471
+ action_key: outcome.action_key,
472
+ event: outcome.record.event,
473
+ seq: outcome.record.seq,
474
+ actor: outcome.record.actor,
475
+ state: outcome.state,
476
+ token_issued: outcome.tokenIssued,
477
+ },
478
+ };
479
+ }
480
+ function applyPropose(logPath, gesture, options) {
481
+ const policyPath = relayPolicyPath(options.gateOptions);
482
+ let bytes;
483
+ try {
484
+ bytes = readFileSync(policyPath);
485
+ }
486
+ catch (cause) {
487
+ return relayRefusal(409, "relay-policy-mismatch", `the policy file ${policyPath} could not be read to confirm it is the bytes this proposal names (${cause instanceof Error ? cause.message : String(cause)}); nothing was proposed`);
488
+ }
489
+ const live = policyBytesHash(bytes);
490
+ if (live !== gesture.policySha256) {
491
+ return relayRefusal(409, "relay-policy-mismatch", `this proposal names ${gesture.policySha256} and the policy file on disk hashes ${live}. A proposal binds the bytes a person will be asked to accept, and these are not the bytes the caller rendered; nothing was proposed. Write the rendered policy first, then propose its hash`);
492
+ }
493
+ const schemaOptions = options.gateOptions.schemaDir === undefined ? {} : { schemaDir: options.gateOptions.schemaDir };
494
+ const read = readVerifiedRecords(logPath, schemaOptions);
495
+ if (!read.ok) {
496
+ return {
497
+ status: 409,
498
+ body: { ok: false, gesture: "propose", refusal: { code: read.code, message: read.message } },
499
+ };
500
+ }
501
+ // Idempotent on a retry: an open proposal of exactly these bytes is the
502
+ // answer, and a second record asking the same question would only make two
503
+ // prompts for one acceptance to answer.
504
+ const existing = openProposalFor(read.records, live, tick(options.gateOptions));
505
+ if (existing !== null) {
506
+ return {
507
+ status: 200,
508
+ body: { ok: true, gesture: "propose", sha256: live, seq: existing.seq, existing: true },
509
+ };
510
+ }
511
+ // The baseline for the semantic diff: the in-force text, recovered from the
512
+ // payload store and verified against the attested digest by the module that
513
+ // recovers it, and verified AGAIN by `summarizeDiff` before it is used. No
514
+ // caller material is a baseline here.
515
+ const storeDir = options.gateOptions.payloadStoreDir ?? payloadStoreDirFor(logPath);
516
+ const inForce = inForcePolicyText(read.records, storeDir);
517
+ const baseline = inForce.ok ? Buffer.from(inForce.text, "utf8") : null;
518
+ const proposed = proposeAttestation(logPath, {
519
+ policyPath,
520
+ baseline,
521
+ reaffirm: true,
522
+ waitUntil: new Date(Date.parse(tick(options.gateOptions)) + RELAY_PROPOSAL_TTL_MS).toISOString(),
523
+ }, options.proposer, {
524
+ ...schemaOptions,
525
+ ...(options.gateOptions.clock === undefined ? {} : { clock: options.gateOptions.clock }),
526
+ ...(options.gateOptions.payloadStoreDir === undefined
527
+ ? {}
528
+ : { payloadStoreDir: options.gateOptions.payloadStoreDir }),
529
+ });
530
+ if (!proposed.ok) {
531
+ return {
532
+ status: 409,
533
+ body: {
534
+ ok: false,
535
+ gesture: "propose",
536
+ refusal: { code: proposed.code, message: proposed.message },
537
+ },
538
+ };
539
+ }
540
+ if (proposed.sha256 !== gesture.policySha256) {
541
+ // The file changed between the relay's hash check and the proposal's own
542
+ // read. The proposal already appended names bytes the caller never
543
+ // rendered; nothing can be attested unseen (acceptance re-hashes, and the
544
+ // caller attests by the hash IT named, which this proposal is not), and
545
+ // the caller is told rather than handed a hash it did not ask for.
546
+ return relayRefusal(409, "relay-policy-mismatch", `the policy file changed while this proposal was being made: it names ${gesture.policySha256} and the proposal recorded at seq ${String(proposed.record.seq)} is of ${proposed.sha256}. Write the rendered policy again and propose its hash; an acceptance of ${gesture.policySha256} finds no open proposal and attests nothing`);
547
+ }
548
+ return {
549
+ status: 200,
550
+ body: {
551
+ ok: true,
552
+ gesture: "propose",
553
+ sha256: proposed.sha256,
554
+ seq: proposed.record.seq,
555
+ existing: false,
556
+ // A reaffirmation is a proposal whose bytes are the attested bytes: the
557
+ // record says so itself, by naming the same hash twice.
558
+ reaffirm: proposed.record.payload?.[POLICY_HASH_FIELD] === proposed.sha256,
559
+ changes: proposed.diff.headline,
560
+ loads: proposed.load.ok,
561
+ },
562
+ };
563
+ }
564
+ //# sourceMappingURL=relay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay.js","sourceRoot":"","sources":["../../../src/channels/relay.ts"],"names":[],"mappings":"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"}
@@ -41,6 +41,7 @@
41
41
  * Determinism: no clock. `now` is a parameter, so a queue rendering is
42
42
  * replayable exactly as it was rendered.
43
43
  */
44
+ import { type PromptSay, type PromptStyle } from "../core/prompt-layout.js";
44
45
  import { type ChannelRequest } from "./contract.js";
45
46
  /**
46
47
  * Where the payload material for an action comes from.
@@ -185,6 +186,30 @@ export type PendingQueueResult = {
185
186
  */
186
187
  skipped: SkippedRequest[];
187
188
  } | ChannelTagRefusal;
189
+ /**
190
+ * The prompt style and `say` map a channel may draw with RIGHT NOW, taken only
191
+ * from attested policy bytes (APRV-489, fix round 2, S4 and its security
192
+ * follow-up).
193
+ *
194
+ * The policy file is read ONCE, and those bytes are both checked and used:
195
+ * they must hash to the latest attestation in the verified log (the digest the
196
+ * gate decides under), and the style and `say` are parsed from the very same
197
+ * buffer. There is no second read for the bytes to change between, so an edit
198
+ * to the file that nobody attested (by the agent the policy governs, in some
199
+ * deployments) can never switch a card to minimal or reword a class's phrase.
200
+ *
201
+ * Anything short of "these bytes are the attested policy" is the technical
202
+ * card: an unreadable file or log, a file that differs from the attestation, a
203
+ * log with no attestation. When the attested policy in force (its stored bytes)
204
+ * does ask for minimal, the answer carries `fallback: "policy-unattested"`, so
205
+ * the decision record says why the approver saw the technical card; under an
206
+ * attested technical policy nothing is added, so its records stay unchanged.
207
+ */
208
+ export declare function attestedPromptOf(logPath: string, options: TagOptions, channel: string): {
209
+ style: PromptStyle;
210
+ say: PromptSay;
211
+ fallback?: "policy-unattested";
212
+ };
188
213
  /**
189
214
  * Build the pending queue: every action key with a live `approval.requested`.
190
215
  *