approval-md 0.0.1 → 0.2.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.
- package/LICENSE +176 -0
- package/NOTICE +5 -0
- package/README.md +940 -4
- package/SPEC.md +476 -34
- package/cli.js +29 -3
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +1200 -0
- package/dist/src/adapters/agentmail.js.map +1 -0
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/conformance.js +461 -0
- package/dist/src/adapters/conformance.js.map +1 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +1035 -0
- package/dist/src/adapters/contract.js.map +1 -0
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/email.js +749 -0
- package/dist/src/adapters/email.js.map +1 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -0
- package/dist/src/adapters/env-passphrase.js +132 -0
- package/dist/src/adapters/env-passphrase.js.map +1 -0
- package/dist/src/adapters/public.d.ts +11 -0
- package/dist/src/adapters/public.js +11 -0
- package/dist/src/adapters/public.js.map +1 -0
- package/dist/src/adapters/registry.d.ts +59 -0
- package/dist/src/adapters/registry.js +77 -0
- package/dist/src/adapters/registry.js.map +1 -0
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/smtp.js +499 -0
- package/dist/src/adapters/smtp.js.map +1 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +161 -0
- package/dist/src/adapters/vault-provider.js.map +1 -0
- package/dist/src/adapters/zzz.d.ts +66 -0
- package/dist/src/adapters/zzz.js +299 -0
- package/dist/src/adapters/zzz.js.map +1 -0
- package/dist/src/channels/batch.d.ts +109 -0
- package/dist/src/channels/batch.js +121 -0
- package/dist/src/channels/batch.js.map +1 -0
- package/dist/src/channels/cli.d.ts +193 -0
- package/dist/src/channels/cli.js +468 -0
- package/dist/src/channels/cli.js.map +1 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/conformance.js +445 -0
- package/dist/src/channels/conformance.js.map +1 -0
- package/dist/src/channels/contract.d.ts +623 -0
- package/dist/src/channels/contract.js +494 -0
- package/dist/src/channels/contract.js.map +1 -0
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/payload-view.js +43 -0
- package/dist/src/channels/payload-view.js.map +1 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/render-queue.js +564 -0
- package/dist/src/channels/render-queue.js.map +1 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/tagging.js +723 -0
- package/dist/src/channels/tagging.js.map +1 -0
- package/dist/src/channels/telegram.d.ts +1832 -0
- package/dist/src/channels/telegram.js +3190 -0
- package/dist/src/channels/telegram.js.map +1 -0
- package/dist/src/channels/web.d.ts +341 -0
- package/dist/src/channels/web.js +903 -0
- package/dist/src/channels/web.js.map +1 -0
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +288 -0
- package/dist/src/cli/adapter.js.map +1 -0
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/amend.js +2171 -0
- package/dist/src/cli/amend.js.map +1 -0
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/args.js +86 -0
- package/dist/src/cli/args.js.map +1 -0
- package/dist/src/cli/attest.d.ts +41 -0
- package/dist/src/cli/attest.js +307 -0
- package/dist/src/cli/attest.js.map +1 -0
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit-card.js +201 -0
- package/dist/src/cli/audit-card.js.map +1 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/audit.js +460 -0
- package/dist/src/cli/audit.js.map +1 -0
- package/dist/src/cli/channel-telegram.d.ts +806 -0
- package/dist/src/cli/channel-telegram.js +2063 -0
- package/dist/src/cli/channel-telegram.js.map +1 -0
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel-web.js +357 -0
- package/dist/src/cli/channel-web.js.map +1 -0
- package/dist/src/cli/channel.d.ts +71 -0
- package/dist/src/cli/channel.js +438 -0
- package/dist/src/cli/channel.js.map +1 -0
- package/dist/src/cli/checkpoint-tap.d.ts +169 -0
- package/dist/src/cli/checkpoint-tap.js +238 -0
- package/dist/src/cli/checkpoint-tap.js.map +1 -0
- package/dist/src/cli/codex.d.ts +2 -0
- package/dist/src/cli/codex.js +172 -0
- package/dist/src/cli/codex.js.map +1 -0
- package/dist/src/cli/coverage.d.ts +61 -0
- package/dist/src/cli/coverage.js +343 -0
- package/dist/src/cli/coverage.js.map +1 -0
- package/dist/src/cli/daemon.d.ts +120 -0
- package/dist/src/cli/daemon.js +631 -0
- package/dist/src/cli/daemon.js.map +1 -0
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +2762 -0
- package/dist/src/cli/doctor.js.map +1 -0
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/env.js +302 -0
- package/dist/src/cli/env.js.map +1 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/execute.js +1682 -0
- package/dist/src/cli/execute.js.map +1 -0
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/exit-codes.js +82 -0
- package/dist/src/cli/exit-codes.js.map +1 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/feedback.js +205 -0
- package/dist/src/cli/feedback.js.map +1 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate-window.js +294 -0
- package/dist/src/cli/gate-window.js.map +1 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/gate.js +557 -0
- package/dist/src/cli/gate.js.map +1 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/git-scope.js +295 -0
- package/dist/src/cli/git-scope.js.map +1 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-attach.js +107 -0
- package/dist/src/cli/gloss-attach.js.map +1 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex-child.js +149 -0
- package/dist/src/cli/gloss-codex-child.js.map +1 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-codex.js +255 -0
- package/dist/src/cli/gloss-codex.js.map +1 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss-options.js +79 -0
- package/dist/src/cli/gloss-options.js.map +1 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/gloss.js +362 -0
- package/dist/src/cli/gloss.js.map +1 -0
- package/dist/src/cli/help.d.ts +103 -0
- package/dist/src/cli/help.js +2339 -0
- package/dist/src/cli/help.js.map +1 -0
- package/dist/src/cli/hook-codex.d.ts +78 -0
- package/dist/src/cli/hook-codex.js +167 -0
- package/dist/src/cli/hook-codex.js.map +1 -0
- package/dist/src/cli/hook.d.ts +331 -0
- package/dist/src/cli/hook.js +2849 -0
- package/dist/src/cli/hook.js.map +1 -0
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/import.js +175 -0
- package/dist/src/cli/import.js.map +1 -0
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +336 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/instructions.js +262 -0
- package/dist/src/cli/instructions.js.map +1 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/journal.js +238 -0
- package/dist/src/cli/journal.js.map +1 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +840 -0
- package/dist/src/cli/log-advance.js.map +1 -0
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-anchor.js +387 -0
- package/dist/src/cli/log-anchor.js.map +1 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-checkpoint.js +128 -0
- package/dist/src/cli/log-checkpoint.js.map +1 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-sync.js +849 -0
- package/dist/src/cli/log-sync.js.map +1 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +360 -0
- package/dist/src/cli/log-verbs.js.map +1 -0
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/long-help.js +148 -0
- package/dist/src/cli/long-help.js.map +1 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +1206 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/mcp.js +306 -0
- package/dist/src/cli/mcp.js.map +1 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/paths.js +79 -0
- package/dist/src/cli/paths.js.map +1 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/payload.js +253 -0
- package/dist/src/cli/payload.js.map +1 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/policy.js +229 -0
- package/dist/src/cli/policy.js.map +1 -0
- package/dist/src/cli/preflight.d.ts +363 -0
- package/dist/src/cli/preflight.js +1175 -0
- package/dist/src/cli/preflight.js.map +1 -0
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/progress.js +112 -0
- package/dist/src/cli/progress.js.map +1 -0
- package/dist/src/cli/prompt.d.ts +209 -0
- package/dist/src/cli/prompt.js +312 -0
- package/dist/src/cli/prompt.js.map +1 -0
- package/dist/src/cli/quickstart.d.ts +46 -0
- package/dist/src/cli/quickstart.js +297 -0
- package/dist/src/cli/quickstart.js.map +1 -0
- package/dist/src/cli/records.d.ts +34 -0
- package/dist/src/cli/records.js +66 -0
- package/dist/src/cli/records.js.map +1 -0
- package/dist/src/cli/render.d.ts +22 -0
- package/dist/src/cli/render.js +132 -0
- package/dist/src/cli/render.js.map +1 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/sandbox.js +150 -0
- package/dist/src/cli/sandbox.js.map +1 -0
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/scaffold.js +137 -0
- package/dist/src/cli/scaffold.js.map +1 -0
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +509 -0
- package/dist/src/cli/setup-adapter.js.map +1 -0
- package/dist/src/cli/setup-channel.d.ts +117 -0
- package/dist/src/cli/setup-channel.js +635 -0
- package/dist/src/cli/setup-channel.js.map +1 -0
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-checkpoint.js +196 -0
- package/dist/src/cli/setup-checkpoint.js.map +1 -0
- package/dist/src/cli/setup-common.d.ts +275 -0
- package/dist/src/cli/setup-common.js +376 -0
- package/dist/src/cli/setup-common.js.map +1 -0
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-flow.js +476 -0
- package/dist/src/cli/setup-flow.js.map +1 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup-service.js +308 -0
- package/dist/src/cli/setup-service.js.map +1 -0
- package/dist/src/cli/setup.d.ts +202 -0
- package/dist/src/cli/setup.js +473 -0
- package/dist/src/cli/setup.js.map +1 -0
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/style.js +469 -0
- package/dist/src/cli/style.js.map +1 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/token.js +274 -0
- package/dist/src/cli/token.js.map +1 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +849 -0
- package/dist/src/cli/up.js.map +1 -0
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/usage.js +91 -0
- package/dist/src/cli/usage.js.map +1 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/values.js +189 -0
- package/dist/src/cli/values.js.map +1 -0
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +362 -0
- package/dist/src/cli/vault.js.map +1 -0
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +2341 -0
- package/dist/src/cli/verb-registry.js.map +1 -0
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +52 -0
- package/dist/src/cli/wordmark.js.map +1 -0
- package/dist/src/codex/doctor.d.ts +13 -0
- package/dist/src/codex/doctor.js +41 -0
- package/dist/src/codex/doctor.js.map +1 -0
- package/dist/src/codex/manifest.d.ts +49 -0
- package/dist/src/codex/manifest.js +103 -0
- package/dist/src/codex/manifest.js.map +1 -0
- package/dist/src/codex/templates.d.ts +41 -0
- package/dist/src/codex/templates.js +319 -0
- package/dist/src/codex/templates.js.map +1 -0
- package/dist/src/codex/trust.d.ts +19 -0
- package/dist/src/codex/trust.js +183 -0
- package/dist/src/codex/trust.js.map +1 -0
- package/dist/src/codex/workspace-plan.d.ts +131 -0
- package/dist/src/codex/workspace-plan.js +561 -0
- package/dist/src/codex/workspace-plan.js.map +1 -0
- package/dist/src/core/actor.d.ts +2 -0
- package/dist/src/core/actor.js +5 -0
- package/dist/src/core/actor.js.map +1 -0
- package/dist/src/core/advance-cycle.d.ts +170 -0
- package/dist/src/core/advance-cycle.js +200 -0
- package/dist/src/core/advance-cycle.js.map +1 -0
- package/dist/src/core/agents-md.d.ts +276 -0
- package/dist/src/core/agents-md.js +747 -0
- package/dist/src/core/agents-md.js.map +1 -0
- package/dist/src/core/apply-patch.d.ts +49 -0
- package/dist/src/core/apply-patch.js +266 -0
- package/dist/src/core/apply-patch.js.map +1 -0
- package/dist/src/core/attest.d.ts +420 -0
- package/dist/src/core/attest.js +589 -0
- package/dist/src/core/attest.js.map +1 -0
- package/dist/src/core/audit.d.ts +492 -0
- package/dist/src/core/audit.js +882 -0
- package/dist/src/core/audit.js.map +1 -0
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/budgets.js +449 -0
- package/dist/src/core/budgets.js.map +1 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/checkpoint.js +738 -0
- package/dist/src/core/checkpoint.js.map +1 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/child-env.js +86 -0
- package/dist/src/core/child-env.js.map +1 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/clock.js +43 -0
- package/dist/src/core/clock.js.map +1 -0
- package/dist/src/core/command-class.d.ts +543 -0
- package/dist/src/core/command-class.js +2356 -0
- package/dist/src/core/command-class.js.map +1 -0
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/adapter.js +71 -0
- package/dist/src/core/coverage-sources/adapter.js.map +1 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/gh.js +136 -0
- package/dist/src/core/coverage-sources/gh.js.map +1 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage-sources/git.js +269 -0
- package/dist/src/core/coverage-sources/git.js.map +1 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/coverage.js +337 -0
- package/dist/src/core/coverage.js.map +1 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/credential-spec.js +23 -0
- package/dist/src/core/credential-spec.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +331 -0
- package/dist/src/core/dark-session.js +714 -0
- package/dist/src/core/dark-session.js.map +1 -0
- package/dist/src/core/decision-refusal.d.ts +185 -0
- package/dist/src/core/decision-refusal.js +265 -0
- package/dist/src/core/decision-refusal.js.map +1 -0
- package/dist/src/core/env-file.d.ts +450 -0
- package/dist/src/core/env-file.js +837 -0
- package/dist/src/core/env-file.js.map +1 -0
- package/dist/src/core/execute.d.ts +858 -0
- package/dist/src/core/execute.js +1271 -0
- package/dist/src/core/execute.js.map +1 -0
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/frontmatter.js +100 -0
- package/dist/src/core/frontmatter.js.map +1 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate-window.js +506 -0
- package/dist/src/core/gate-window.js.map +1 -0
- package/dist/src/core/gate.d.ts +1364 -0
- package/dist/src/core/gate.js +3002 -0
- package/dist/src/core/gate.js.map +1 -0
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/git-run.js +93 -0
- package/dist/src/core/git-run.js.map +1 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +211 -0
- package/dist/src/core/harness-version.js.map +1 -0
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/harness-wait.js +58 -0
- package/dist/src/core/harness-wait.js.map +1 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/head-retry.js +121 -0
- package/dist/src/core/head-retry.js.map +1 -0
- package/dist/src/core/instance.d.ts +253 -0
- package/dist/src/core/instance.js +319 -0
- package/dist/src/core/instance.js.map +1 -0
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/intake-limits.js +350 -0
- package/dist/src/core/intake-limits.js.map +1 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/jcs.js +132 -0
- package/dist/src/core/jcs.js.map +1 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/journal.js +200 -0
- package/dist/src/core/journal.js.map +1 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/live-draw.js +703 -0
- package/dist/src/core/live-draw.js.map +1 -0
- package/dist/src/core/log-reconcile.d.ts +89 -0
- package/dist/src/core/log-reconcile.js +136 -0
- package/dist/src/core/log-reconcile.js.map +1 -0
- package/dist/src/core/log-subscribe.d.ts +36 -0
- package/dist/src/core/log-subscribe.js +162 -0
- package/dist/src/core/log-subscribe.js.map +1 -0
- package/dist/src/core/log.d.ts +278 -0
- package/dist/src/core/log.js +546 -0
- package/dist/src/core/log.js.map +1 -0
- package/dist/src/core/loop.d.ts +274 -0
- package/dist/src/core/loop.js +487 -0
- package/dist/src/core/loop.js.map +1 -0
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/md-fence.js +74 -0
- package/dist/src/core/md-fence.js.map +1 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/money.js +195 -0
- package/dist/src/core/money.js.map +1 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-census.js +146 -0
- package/dist/src/core/payload-census.js.map +1 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload-store.js +340 -0
- package/dist/src/core/payload-store.js.map +1 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/payload.js +80 -0
- package/dist/src/core/payload.js.map +1 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +588 -0
- package/dist/src/core/policy-diff.js.map +1 -0
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-expectations.js +394 -0
- package/dist/src/core/policy-expectations.js.map +1 -0
- package/dist/src/core/policy-explain.d.ts +150 -0
- package/dist/src/core/policy-explain.js +258 -0
- package/dist/src/core/policy-explain.js.map +1 -0
- package/dist/src/core/policy-load.d.ts +527 -0
- package/dist/src/core/policy-load.js +536 -0
- package/dist/src/core/policy-load.js.map +1 -0
- package/dist/src/core/policy-match.d.ts +281 -0
- package/dist/src/core/policy-match.js +478 -0
- package/dist/src/core/policy-match.js.map +1 -0
- package/dist/src/core/policy-proposal.d.ts +265 -0
- package/dist/src/core/policy-proposal.js +458 -0
- package/dist/src/core/policy-proposal.js.map +1 -0
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/prompt-layout.js +422 -0
- package/dist/src/core/prompt-layout.js.map +1 -0
- package/dist/src/core/protected-path-guard.d.ts +453 -0
- package/dist/src/core/protected-path-guard.js +1566 -0
- package/dist/src/core/protected-path-guard.js.map +1 -0
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/registration.js +39 -0
- package/dist/src/core/registration.js.map +1 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/reindex.js +336 -0
- package/dist/src/core/reindex.js.map +1 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sampler.js +388 -0
- package/dist/src/core/sampler.js.map +1 -0
- package/dist/src/core/sandbox.d.ts +290 -0
- package/dist/src/core/sandbox.js +424 -0
- package/dist/src/core/sandbox.js.map +1 -0
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/seal.js +290 -0
- package/dist/src/core/seal.js.map +1 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/state.js +1009 -0
- package/dist/src/core/state.js.map +1 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/task-file.js +464 -0
- package/dist/src/core/task-file.js.map +1 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/telegram-config.js +114 -0
- package/dist/src/core/telegram-config.js.map +1 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +561 -0
- package/dist/src/core/token.js.map +1 -0
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/validate.js +0 -0
- package/dist/src/core/validate.js.map +1 -0
- package/dist/src/core/values.d.ts +137 -0
- package/dist/src/core/values.js +153 -0
- package/dist/src/core/values.js.map +1 -0
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/vault.js +612 -0
- package/dist/src/core/vault.js.map +1 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verified-snapshot.js +506 -0
- package/dist/src/core/verified-snapshot.js.map +1 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/verify.js +549 -0
- package/dist/src/core/verify.js.map +1 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/version.js +9 -0
- package/dist/src/core/version.js.map +1 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/core/wysiwys.js +728 -0
- package/dist/src/core/wysiwys.js.map +1 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance-child.js +78 -0
- package/dist/src/daemon/advance-child.js.map +1 -0
- package/dist/src/daemon/advance.d.ts +466 -0
- package/dist/src/daemon/advance.js +849 -0
- package/dist/src/daemon/advance.js.map +1 -0
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/audit.js +90 -0
- package/dist/src/daemon/audit.js.map +1 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/daemon.js +1988 -0
- package/dist/src/daemon/daemon.js.map +1 -0
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/dark-session.js +119 -0
- package/dist/src/daemon/dark-session.js.map +1 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw-child.js +132 -0
- package/dist/src/daemon/draw-child.js.map +1 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/draw.js +458 -0
- package/dist/src/daemon/draw.js.map +1 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +345 -0
- package/dist/src/daemon/git-evidence.js.map +1 -0
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/projection.js +233 -0
- package/dist/src/daemon/projection.js.map +1 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/daemon/prune.js +376 -0
- package/dist/src/daemon/prune.js.map +1 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/http.js +343 -0
- package/dist/src/mcp/http.js.map +1 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +602 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +5716 -0
- package/docs/codex-enforced-session.md +30 -0
- package/package.json +53 -4
- package/schema/.gitkeep +0 -0
- package/schema/LICENSE +117 -0
- package/schema/codex-instance.schema.json +82 -0
- package/schema/envelope.schema.json +137 -0
- package/schema/event.schema.json +1811 -0
- package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
- package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
- package/schema/fixtures/envelope/invalid/action-missing-idempotency-key.json +15 -0
- package/schema/fixtures/envelope/invalid/action-unknown-class-format.json +14 -0
- package/schema/fixtures/envelope/invalid/confidence-out-of-range.json +11 -0
- package/schema/fixtures/envelope/invalid/est-cost-bare-number.json +14 -0
- package/schema/fixtures/envelope/invalid/est-cost-noncanonical-string.json +14 -0
- package/schema/fixtures/envelope/invalid/malformed-assignee.json +10 -0
- package/schema/fixtures/envelope/invalid/malformed-created-by.json +7 -0
- package/schema/fixtures/envelope/invalid/malformed-max-latency.json +11 -0
- package/schema/fixtures/envelope/invalid/malformed-payload-hash.json +14 -0
- package/schema/fixtures/envelope/invalid/max-cost-bare-number.json +11 -0
- package/schema/fixtures/envelope/invalid/missing-origin.json +3 -0
- package/schema/fixtures/envelope/invalid/negative-est-cost.json +14 -0
- package/schema/fixtures/envelope/invalid/unknown-state.json +7 -0
- package/schema/fixtures/envelope/invalid/unknown-top-level-field.json +8 -0
- package/schema/fixtures/envelope/valid/action-payload-hash.json +17 -0
- package/schema/fixtures/envelope/valid/actions-without-budget.json +18 -0
- package/schema/fixtures/envelope/valid/canonical.json +25 -0
- package/schema/fixtures/envelope/valid/minimal.json +7 -0
- package/schema/fixtures/envelope/valid/multi-action-executed.json +30 -0
- package/schema/fixtures/envelope/valid/record-write-stage.json +22 -0
- package/schema/fixtures/event/invalid/approval-granted-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/approval-granted-empty-batch-delivery-id.json +17 -0
- package/schema/fixtures/event/invalid/approval-granted-fifth-reaction.json +16 -0
- package/schema/fixtures/event/invalid/approval-granted-missing-actor.json +14 -0
- package/schema/fixtures/event/invalid/approval-requested-missing-action-key.json +14 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-agent-policy-drift.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-missing-reason.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-human-actor.json +17 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-missing-code.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/bad-actor-prefix.json +15 -0
- package/schema/fixtures/event/invalid/est-cost-bare-number.json +17 -0
- package/schema/fixtures/event/invalid/execution-completed-fabricated-exit-code.json +16 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-empty-id.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-extra-field.json +19 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-id-not-string.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-missing-adapter.json +17 -0
- package/schema/fixtures/event/invalid/execution-failed-open-reported-by.json +16 -0
- package/schema/fixtures/event/invalid/execution-indeterminate-open-reason.json +14 -0
- package/schema/fixtures/event/invalid/execution-reconciled-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/execution-started-negative-env-stripped.json +16 -0
- package/schema/fixtures/event/invalid/gate-bypassed-missing-opened-seq.json +15 -0
- package/schema/fixtures/event/invalid/gate-closed-non-integer-opened-seq.json +13 -0
- package/schema/fixtures/event/invalid/gate-opened-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-missing-organ-path.json +13 -0
- package/schema/fixtures/event/invalid/harness-unknown-kind.json +18 -0
- package/schema/fixtures/event/invalid/harness-version-multiline.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-missing-signature.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-short-signed-hash.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-unknown-signature-alg.json +17 -0
- package/schema/fixtures/event/invalid/malformed-ts.json +15 -0
- package/schema/fixtures/event/invalid/missing-alg.json +14 -0
- package/schema/fixtures/event/invalid/missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/non-integer-seq.json +15 -0
- package/schema/fixtures/event/invalid/payload-pruned-human-actor.json +14 -0
- package/schema/fixtures/event/invalid/payload-pruned-missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/policy-declined-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/policy-proposed-missing-diff.json +19 -0
- package/schema/fixtures/event/invalid/policy-proposed-system-actor.json +26 -0
- package/schema/fixtures/event/invalid/short-hash.json +15 -0
- package/schema/fixtures/event/invalid/unknown-alg.json +15 -0
- package/schema/fixtures/event/invalid/unknown-event-type.json +15 -0
- package/schema/fixtures/event/invalid/unknown-top-level-field.json +16 -0
- package/schema/fixtures/event/valid/approval-expired.json +15 -0
- package/schema/fixtures/event/valid/approval-granted-batch.json +19 -0
- package/schema/fixtures/event/valid/approval-granted-reaction.json +16 -0
- package/schema/fixtures/event/valid/approval-granted.json +15 -0
- package/schema/fixtures/event/valid/approval-rejected.json +15 -0
- package/schema/fixtures/event/valid/approval-requested.json +19 -0
- package/schema/fixtures/event/valid/approval-revoked.json +15 -0
- package/schema/fixtures/event/valid/approval-withdrawn-policy-drift.json +17 -0
- package/schema/fixtures/event/valid/approval-withdrawn.json +16 -0
- package/schema/fixtures/event/valid/audit-decision-refused.json +20 -0
- package/schema/fixtures/event/valid/audit-reviewed-reaction.json +17 -0
- package/schema/fixtures/event/valid/audit-reviewed.json +15 -0
- package/schema/fixtures/event/valid/audit-sampled.json +14 -0
- package/schema/fixtures/event/valid/budget-exceeded.json +21 -0
- package/schema/fixtures/event/valid/envelope-drift.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-provider-ref.json +18 -0
- package/schema/fixtures/event/valid/execution-completed.json +15 -0
- package/schema/fixtures/event/valid/execution-failed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-failed.json +15 -0
- package/schema/fixtures/event/valid/execution-indeterminate.json +15 -0
- package/schema/fixtures/event/valid/execution-reconciled.json +17 -0
- package/schema/fixtures/event/valid/execution-started-env-stripped.json +17 -0
- package/schema/fixtures/event/valid/execution-started.json +14 -0
- package/schema/fixtures/event/valid/gate-bypassed-harness-version.json +18 -0
- package/schema/fixtures/event/valid/gate-bypassed.json +19 -0
- package/schema/fixtures/event/valid/gate-closed.json +14 -0
- package/schema/fixtures/event/valid/gate-opened.json +16 -0
- package/schema/fixtures/event/valid/gate-organ-attested.json +14 -0
- package/schema/fixtures/event/valid/genesis-null-prev.json +14 -0
- package/schema/fixtures/event/valid/log-checkpoint.json +17 -0
- package/schema/fixtures/event/valid/payload-pruned-orphan.json +13 -0
- package/schema/fixtures/event/valid/payload-pruned.json +17 -0
- package/schema/fixtures/event/valid/policy-declined.json +16 -0
- package/schema/fixtures/event/valid/policy-proposed.json +35 -0
- package/schema/fixtures/event/valid/policy-updated.json +14 -0
- package/schema/fixtures/event/valid/reconciliation-required.json +18 -0
- package/schema/fixtures/event/valid/reconciliation-satisfied.json +17 -0
- package/schema/fixtures/event/valid/route-accepted.json +15 -0
- package/schema/fixtures/event/valid/route-proposed.json +16 -0
- package/schema/fixtures/event/valid/spec-example.json +15 -0
- package/schema/fixtures/event/valid/task-registered-harness-version.json +23 -0
- package/schema/fixtures/event/valid/task-registered.json +14 -0
- package/schema/fixtures/hash/known-answer-pre-121.json +74 -0
- package/schema/fixtures/hash/known-answer.json +74 -0
- package/schema/fixtures/policy/invalid/bad-approval-ttl.json +7 -0
- package/schema/fixtures/policy/invalid/bad-web-port.json +4 -0
- package/schema/fixtures/policy/invalid/checkpoint-key-not-base64.json +6 -0
- package/schema/fixtures/policy/invalid/class-rule-missing-autonomy.json +9 -0
- package/schema/fixtures/policy/invalid/empty-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/live-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/malformed-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/missing-version.json +8 -0
- package/schema/fixtures/policy/invalid/negative-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-numeric-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-positive-max-pending.json +9 -0
- package/schema/fixtures/policy/invalid/on-expiry-grant.json +8 -0
- package/schema/fixtures/policy/invalid/payload-retention-bare-number.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-compound.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-fractional.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-zero.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-escape.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-glob.json +4 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-manual.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-zero.json +7 -0
- package/schema/fixtures/policy/invalid/sample-rate-too-high.json +5 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-empty.json +7 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-not-string.json +6 -0
- package/schema/fixtures/policy/invalid/skew-tolerance-compound.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-autonomy.json +7 -0
- package/schema/fixtures/policy/invalid/unknown-class-rule-key.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-top-level-key.json +7 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-env-empty.json +6 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-literal.json +6 -0
- package/schema/fixtures/policy/invalid/version-not-string.json +4 -0
- package/schema/fixtures/policy/valid/canonical.json +47 -0
- package/schema/fixtures/policy/valid/checkpoint-keys.json +18 -0
- package/schema/fixtures/policy/valid/class-approvers-limits.json +25 -0
- package/schema/fixtures/policy/valid/class-retro-rate.json +17 -0
- package/schema/fixtures/policy/valid/global-budgets.json +19 -0
- package/schema/fixtures/policy/valid/human-only.json +9 -0
- package/schema/fixtures/policy/valid/minimal.json +6 -0
- package/schema/fixtures/policy/valid/protected-paths.json +10 -0
- package/schema/fixtures/policy/valid/record-namespace.json +13 -0
- package/schema/fixtures/policy/valid/request-volume-limits.json +26 -0
- package/schema/fixtures/policy/valid/retention-and-sampling-secret.json +16 -0
- package/schema/fixtures/policy/valid/skew-tolerance.json +15 -0
- package/schema/fixtures/policy/valid/vault-passphrase-env.json +14 -0
- package/schema/fixtures/policy/valid/wildcards.json +15 -0
- package/schema/fixtures/policy-md/invalid/alias-bomb.md +15 -0
- package/schema/fixtures/policy-md/invalid/no-fence.md +7 -0
- package/schema/fixtures/policy-md/invalid/protected-route-not-a-subclass.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-autonomy.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-read-proof.md +17 -0
- package/schema/fixtures/policy-md/invalid/two-fences.md +19 -0
- package/schema/fixtures/policy-md/invalid/unclosed-fence.md +11 -0
- package/schema/fixtures/policy-md/invalid/wrong-info-string.md +11 -0
- package/schema/fixtures/policy-md/invalid/yaml-syntax-error.md +13 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVAL.md +7 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/precedence/fallback-only/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/valid/canonical.md +50 -0
- package/schema/fixtures/policy-md/valid/daemon-read-proof.md +18 -0
- package/schema/fixtures/policy-md/valid/minimal.md +3 -0
- package/schema/fixtures/policy-md/valid/prose-lookalikes.md +54 -0
- package/schema/fixtures/policy-md/valid/routed-protected-paths.md +49 -0
- package/schema/fixtures/policy-md/valid/with-values.md +79 -0
- package/schema/fixtures/sample-record/invalid/bad-date-time.json +4 -0
- package/schema/fixtures/sample-record/invalid/missing-required-field.json +3 -0
- package/schema/fixtures/sample-record/invalid/unknown-top-level-field.json +5 -0
- package/schema/fixtures/sample-record/invalid/wrong-type.json +4 -0
- package/schema/fixtures/sample-record/valid/minimal.json +4 -0
- package/schema/fixtures/sample-record/valid/with-note.json +5 -0
- package/schema/fixtures/values/invalid/class-shaped.json +9 -0
- package/schema/fixtures/values/invalid/duplicate-entry.json +4 -0
- package/schema/fixtures/values/invalid/non-string-item.json +4 -0
- package/schema/fixtures/values/invalid/over-cap.json +26 -0
- package/schema/fixtures/values/invalid/unknown-key.json +5 -0
- package/schema/fixtures/values/invalid/version-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +7 -0
- package/schema/fixtures/values/valid/full.json +20 -0
- package/schema/fixtures/values/valid/minimal.json +1 -0
- package/schema/fixtures/values-md/invalid/schema-invalid.md +62 -0
- package/schema/fixtures/values-md/invalid/two-blocks.md +69 -0
- package/schema/fixtures/values-md/invalid/unterminated.md +61 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +63 -0
- package/schema/fixtures/values-md/valid/absent.md +50 -0
- package/schema/fixtures/values-md/valid/with-values.md +79 -0
- package/schema/policy.schema.json +501 -0
- package/schema/sample-record.schema.json +26 -0
- package/schema/values.schema.json +55 -0
- package/templates/codex/README.md +9 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The payload store: one source of bytes for the gate, the render, and every
|
|
3
|
+
* channel (SPEC.md §6.2, §9, §10.4; APRV-28).
|
|
4
|
+
*
|
|
5
|
+
* SPEC.md §6.2 already asks for this in one clause — "The payload itself is
|
|
6
|
+
* stored or referenced by the request so channels can display it; the hash is
|
|
7
|
+
* what approval binds to" — and until now nothing implemented it. The bytes a
|
|
8
|
+
* human had to see before deciding were carried around the runtime by hand:
|
|
9
|
+
* `--payload-dir` for the CLI and web channels, `--payloads` for Telegram, and
|
|
10
|
+
* nothing at all for `approval render`, which is why QUEUE.md's pending count
|
|
11
|
+
* disagreed with the queue every channel showed (the APRV-27 friction case).
|
|
12
|
+
*
|
|
13
|
+
* ## What this is
|
|
14
|
+
*
|
|
15
|
+
* A content-addressed directory, `.approval/payloads/` beside the log, holding
|
|
16
|
+
* one file per bound payload: `<payload_hash>.json`, whose contents are the
|
|
17
|
+
* exact material whose RFC 8785 canonical serialization hashes to the filename.
|
|
18
|
+
* The name *is* the checksum, so the store needs no index, no manifest and no
|
|
19
|
+
* lock: two writers writing the same payload write the same bytes to the same
|
|
20
|
+
* path, and a file either verifies or it is refused.
|
|
21
|
+
*
|
|
22
|
+
* ## Verified on every read, without exception
|
|
23
|
+
*
|
|
24
|
+
* {@link loadPayload} re-canonicalizes and re-hashes what it read and compares
|
|
25
|
+
* that against the filename before returning anything. A file whose contents
|
|
26
|
+
* were edited after it was written refuses `hash-mismatch` and its value is
|
|
27
|
+
* **never returned**, so tampering with the store cannot put different bytes in
|
|
28
|
+
* front of an approver than the ones the request bound to. The recorded binding
|
|
29
|
+
* on `approval.requested` stays authoritative in both directions: the store is a
|
|
30
|
+
* cache of bytes that the log already committed to, and when the two disagree
|
|
31
|
+
* the log is right and the store is refused.
|
|
32
|
+
*
|
|
33
|
+
* This is the same rule the rest of the codebase keeps — files are the
|
|
34
|
+
* interface, the log is the truth, the database (and now the store) is a cache.
|
|
35
|
+
*
|
|
36
|
+
* ## External references
|
|
37
|
+
*
|
|
38
|
+
* A request may name material this runtime cannot hold: a message body in a
|
|
39
|
+
* vault, an object in a bucket. The store records that as `{"$ref": "<uri>"}` in
|
|
40
|
+
* the same `<hash>.json` file, and {@link loadPayload} reports it as a
|
|
41
|
+
* `reference` — deliberately **not** resolved. Resolving a URI would mean
|
|
42
|
+
* fetching bytes at render time from a source this module cannot verify, and a
|
|
43
|
+
* channel that displayed them would be showing an approver something no hash
|
|
44
|
+
* ever bound. So a reference renders as a reference: the channel says where the
|
|
45
|
+
* payload lives and refuses to pretend it has seen it.
|
|
46
|
+
*
|
|
47
|
+
* ## Writes never touch the log
|
|
48
|
+
*
|
|
49
|
+
* Nothing here opens `events.jsonl`. {@link storePayload} writes exactly one
|
|
50
|
+
* file, atomically (temp + rename in the destination directory), and the store
|
|
51
|
+
* directory is a sibling of the log directory rather than a child, so no
|
|
52
|
+
* conceivable store path collides with the log's.
|
|
53
|
+
*/
|
|
54
|
+
/** The directory name, beside the log's home: `.approval/payloads/`. */
|
|
55
|
+
export declare const PAYLOAD_STORE_DIRNAME = "payloads";
|
|
56
|
+
/**
|
|
57
|
+
* The store directory for a given log path — the convention every caller uses
|
|
58
|
+
* when no explicit directory is supplied.
|
|
59
|
+
*
|
|
60
|
+
* SPEC.md §9 fixes the log at `<home>/log/events.jsonl`, so the store is
|
|
61
|
+
* `<home>/payloads/`: a sibling of the log directory, never inside it. When a
|
|
62
|
+
* caller points `--log` at some other layout the store lands beside that file
|
|
63
|
+
* instead, which keeps the two together for the ad-hoc case without ever
|
|
64
|
+
* placing payload files in the directory the log is walked from.
|
|
65
|
+
*/
|
|
66
|
+
export declare function payloadStoreDirFor(logPath: string): string;
|
|
67
|
+
/** The file one payload hash addresses. */
|
|
68
|
+
export declare function payloadPath(storeDir: string, hash: string): string;
|
|
69
|
+
export type StorePayloadResult = {
|
|
70
|
+
ok: true;
|
|
71
|
+
hash: string;
|
|
72
|
+
path: string;
|
|
73
|
+
} | {
|
|
74
|
+
ok: false;
|
|
75
|
+
code: "unserializable" | "write-failed";
|
|
76
|
+
message: string;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Store payload material and return the hash it is addressed by.
|
|
80
|
+
*
|
|
81
|
+
* The bytes written are the RFC 8785 canonical serialization itself — not
|
|
82
|
+
* `JSON.stringify` of the value — so the file's own bytes hash to its own name
|
|
83
|
+
* and the store is verifiable byte for byte as well as semantically.
|
|
84
|
+
*
|
|
85
|
+
* **Idempotent.** Writing the same value twice writes the same bytes to the same
|
|
86
|
+
* path and reports the same hash. The write is unconditional rather than
|
|
87
|
+
* skipped-if-present, which also means a corrupted file is repaired by the next
|
|
88
|
+
* store of the same payload: content addressing makes "overwrite" and "leave
|
|
89
|
+
* alone" the same operation for anything that was ever valid.
|
|
90
|
+
*
|
|
91
|
+
* Refuses `unserializable` for material RFC 8785 cannot serialize (a cycle, a
|
|
92
|
+
* NaN, a function). A payload that cannot be canonicalized cannot be bound to,
|
|
93
|
+
* so it must not be stored under a hash that would then name something else.
|
|
94
|
+
*/
|
|
95
|
+
export declare function storePayload(storeDir: string, value: unknown): StorePayloadResult;
|
|
96
|
+
/**
|
|
97
|
+
* Record an external reference for a payload this runtime does not hold.
|
|
98
|
+
*
|
|
99
|
+
* The hash is supplied by the caller because a reference does not determine one:
|
|
100
|
+
* the binding is a fact about the *payload*, which lives elsewhere, and the
|
|
101
|
+
* reference is only a pointer to it. {@link loadPayload} reports the pointer and
|
|
102
|
+
* resolves nothing — see the module header.
|
|
103
|
+
*/
|
|
104
|
+
export declare function storeReference(storeDir: string, hash: string, reference: string): StorePayloadResult;
|
|
105
|
+
/**
|
|
106
|
+
* Every payload hash the store currently holds, sorted.
|
|
107
|
+
*
|
|
108
|
+
* Only `<hash>.json` names whose stem is a well-formed `payload_hash` are
|
|
109
|
+
* reported. A directory, a dotfile (the temp name an interrupted
|
|
110
|
+
* {@link writeAtomic} leaves behind), and anything else a human dropped in the
|
|
111
|
+
* directory are not payloads the store addresses, so no caller — the daemon's
|
|
112
|
+
* pruner least of all — is ever handed one as if it were.
|
|
113
|
+
*
|
|
114
|
+
* An unreadable or absent directory yields the empty list rather than throwing:
|
|
115
|
+
* a store that cannot be listed holds nothing this process can act on, which is
|
|
116
|
+
* the fail-closed reading for every caller here (reporting counts, deciding what
|
|
117
|
+
* to prune).
|
|
118
|
+
*/
|
|
119
|
+
export declare function listStoredPayloadHashes(storeDir: string): string[];
|
|
120
|
+
/** Why a removal did not happen. `absent` is a success for every caller here. */
|
|
121
|
+
export type RemovePayloadResult = {
|
|
122
|
+
ok: true;
|
|
123
|
+
path: string;
|
|
124
|
+
existed: boolean;
|
|
125
|
+
} | {
|
|
126
|
+
ok: false;
|
|
127
|
+
code: "unlink-failed";
|
|
128
|
+
message: string;
|
|
129
|
+
path: string;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Delete the bytes stored under `hash`.
|
|
133
|
+
*
|
|
134
|
+
* **Daemon-only.** SPEC.md §5.2: "Pruning is performed by the daemon and by
|
|
135
|
+
* nothing else." Nothing in the CLI calls this, and the retention rule that
|
|
136
|
+
* decides *which* hash reaches it lives in `daemon/prune.ts`, which appends the
|
|
137
|
+
* `payload.pruned` event before the file goes. This function is the unlink and
|
|
138
|
+
* nothing more: it judges no retention, reads no policy, and touches no log.
|
|
139
|
+
*
|
|
140
|
+
* A file that is already gone reports `existed: false` and `ok`. The caller's
|
|
141
|
+
* intent is that the bytes not be there, and a removal that raced another
|
|
142
|
+
* removal (or completed a crash-interrupted one) achieved exactly that.
|
|
143
|
+
*/
|
|
144
|
+
export declare function removeStoredPayload(storeDir: string, hash: string): RemovePayloadResult;
|
|
145
|
+
/** Why a read did not produce material. */
|
|
146
|
+
export type LoadPayloadRefusalCode =
|
|
147
|
+
/** No file is stored under this hash. */
|
|
148
|
+
"absent"
|
|
149
|
+
/** A file exists but its contents do not hash to its name. */
|
|
150
|
+
| "hash-mismatch"
|
|
151
|
+
/** The file could not be read, or is not JSON. */
|
|
152
|
+
| "unreadable"
|
|
153
|
+
/** The file holds a `{"$ref": …}` pointer, which is not resolved here. */
|
|
154
|
+
| "reference";
|
|
155
|
+
export type LoadPayloadResult = {
|
|
156
|
+
ok: true;
|
|
157
|
+
value: unknown;
|
|
158
|
+
path: string;
|
|
159
|
+
} | {
|
|
160
|
+
ok: false;
|
|
161
|
+
code: LoadPayloadRefusalCode;
|
|
162
|
+
message: string;
|
|
163
|
+
path: string;
|
|
164
|
+
/** The pointer, when `code` is `reference`. */
|
|
165
|
+
reference?: string;
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Load the material stored under `hash`, verifying it against `hash`.
|
|
169
|
+
*
|
|
170
|
+
* The verification is unconditional and the value is withheld when it fails:
|
|
171
|
+
* `hash-mismatch` says the file on disk is not the payload the request bound to,
|
|
172
|
+
* and returning it "so the caller can decide" would hand a channel exactly the
|
|
173
|
+
* bytes the binding exists to keep away from an approver.
|
|
174
|
+
*/
|
|
175
|
+
export declare function loadPayload(storeDir: string, hash: string): LoadPayloadResult;
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The payload store: one source of bytes for the gate, the render, and every
|
|
3
|
+
* channel (SPEC.md §6.2, §9, §10.4; APRV-28).
|
|
4
|
+
*
|
|
5
|
+
* SPEC.md §6.2 already asks for this in one clause — "The payload itself is
|
|
6
|
+
* stored or referenced by the request so channels can display it; the hash is
|
|
7
|
+
* what approval binds to" — and until now nothing implemented it. The bytes a
|
|
8
|
+
* human had to see before deciding were carried around the runtime by hand:
|
|
9
|
+
* `--payload-dir` for the CLI and web channels, `--payloads` for Telegram, and
|
|
10
|
+
* nothing at all for `approval render`, which is why QUEUE.md's pending count
|
|
11
|
+
* disagreed with the queue every channel showed (the APRV-27 friction case).
|
|
12
|
+
*
|
|
13
|
+
* ## What this is
|
|
14
|
+
*
|
|
15
|
+
* A content-addressed directory, `.approval/payloads/` beside the log, holding
|
|
16
|
+
* one file per bound payload: `<payload_hash>.json`, whose contents are the
|
|
17
|
+
* exact material whose RFC 8785 canonical serialization hashes to the filename.
|
|
18
|
+
* The name *is* the checksum, so the store needs no index, no manifest and no
|
|
19
|
+
* lock: two writers writing the same payload write the same bytes to the same
|
|
20
|
+
* path, and a file either verifies or it is refused.
|
|
21
|
+
*
|
|
22
|
+
* ## Verified on every read, without exception
|
|
23
|
+
*
|
|
24
|
+
* {@link loadPayload} re-canonicalizes and re-hashes what it read and compares
|
|
25
|
+
* that against the filename before returning anything. A file whose contents
|
|
26
|
+
* were edited after it was written refuses `hash-mismatch` and its value is
|
|
27
|
+
* **never returned**, so tampering with the store cannot put different bytes in
|
|
28
|
+
* front of an approver than the ones the request bound to. The recorded binding
|
|
29
|
+
* on `approval.requested` stays authoritative in both directions: the store is a
|
|
30
|
+
* cache of bytes that the log already committed to, and when the two disagree
|
|
31
|
+
* the log is right and the store is refused.
|
|
32
|
+
*
|
|
33
|
+
* This is the same rule the rest of the codebase keeps — files are the
|
|
34
|
+
* interface, the log is the truth, the database (and now the store) is a cache.
|
|
35
|
+
*
|
|
36
|
+
* ## External references
|
|
37
|
+
*
|
|
38
|
+
* A request may name material this runtime cannot hold: a message body in a
|
|
39
|
+
* vault, an object in a bucket. The store records that as `{"$ref": "<uri>"}` in
|
|
40
|
+
* the same `<hash>.json` file, and {@link loadPayload} reports it as a
|
|
41
|
+
* `reference` — deliberately **not** resolved. Resolving a URI would mean
|
|
42
|
+
* fetching bytes at render time from a source this module cannot verify, and a
|
|
43
|
+
* channel that displayed them would be showing an approver something no hash
|
|
44
|
+
* ever bound. So a reference renders as a reference: the channel says where the
|
|
45
|
+
* payload lives and refuses to pretend it has seen it.
|
|
46
|
+
*
|
|
47
|
+
* ## Writes never touch the log
|
|
48
|
+
*
|
|
49
|
+
* Nothing here opens `events.jsonl`. {@link storePayload} writes exactly one
|
|
50
|
+
* file, atomically (temp + rename in the destination directory), and the store
|
|
51
|
+
* directory is a sibling of the log directory rather than a child, so no
|
|
52
|
+
* conceivable store path collides with the log's.
|
|
53
|
+
*/
|
|
54
|
+
import { closeSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, unlinkSync, writeSync, } from "node:fs";
|
|
55
|
+
import { basename, dirname, join } from "node:path";
|
|
56
|
+
import { canonicalize } from "./jcs.js";
|
|
57
|
+
import { isPayloadHash, payloadHash } from "./payload.js";
|
|
58
|
+
/** The directory name, beside the log's home: `.approval/payloads/`. */
|
|
59
|
+
export const PAYLOAD_STORE_DIRNAME = "payloads";
|
|
60
|
+
/**
|
|
61
|
+
* The store directory for a given log path — the convention every caller uses
|
|
62
|
+
* when no explicit directory is supplied.
|
|
63
|
+
*
|
|
64
|
+
* SPEC.md §9 fixes the log at `<home>/log/events.jsonl`, so the store is
|
|
65
|
+
* `<home>/payloads/`: a sibling of the log directory, never inside it. When a
|
|
66
|
+
* caller points `--log` at some other layout the store lands beside that file
|
|
67
|
+
* instead, which keeps the two together for the ad-hoc case without ever
|
|
68
|
+
* placing payload files in the directory the log is walked from.
|
|
69
|
+
*/
|
|
70
|
+
export function payloadStoreDirFor(logPath) {
|
|
71
|
+
const logDir = dirname(logPath);
|
|
72
|
+
const home = basename(logDir) === "log" ? dirname(logDir) : logDir;
|
|
73
|
+
return join(home, PAYLOAD_STORE_DIRNAME);
|
|
74
|
+
}
|
|
75
|
+
/** The file one payload hash addresses. */
|
|
76
|
+
export function payloadPath(storeDir, hash) {
|
|
77
|
+
return join(storeDir, `${hash}.json`);
|
|
78
|
+
}
|
|
79
|
+
function detail(cause) {
|
|
80
|
+
return cause instanceof Error ? cause.message : String(cause);
|
|
81
|
+
}
|
|
82
|
+
/** Distinguishes concurrent writers' temp files within one process. */
|
|
83
|
+
let tempCounter = 0;
|
|
84
|
+
function writeAtomic(path, bytes) {
|
|
85
|
+
const directory = dirname(path);
|
|
86
|
+
tempCounter += 1;
|
|
87
|
+
const temp = join(directory, `.${basename(path)}.tmp-${String(process.pid)}-${String(tempCounter)}`);
|
|
88
|
+
try {
|
|
89
|
+
mkdirSync(directory, { recursive: true });
|
|
90
|
+
const handle = openSync(temp, "wx");
|
|
91
|
+
try {
|
|
92
|
+
writeSync(handle, bytes, 0, "utf8");
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
closeSync(handle);
|
|
96
|
+
}
|
|
97
|
+
renameSync(temp, path);
|
|
98
|
+
}
|
|
99
|
+
catch (cause) {
|
|
100
|
+
try {
|
|
101
|
+
unlinkSync(temp);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// The temp file may never have been created; nothing to clean up.
|
|
105
|
+
}
|
|
106
|
+
return { ok: false, message: detail(cause) };
|
|
107
|
+
}
|
|
108
|
+
return { ok: true };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Store payload material and return the hash it is addressed by.
|
|
112
|
+
*
|
|
113
|
+
* The bytes written are the RFC 8785 canonical serialization itself — not
|
|
114
|
+
* `JSON.stringify` of the value — so the file's own bytes hash to its own name
|
|
115
|
+
* and the store is verifiable byte for byte as well as semantically.
|
|
116
|
+
*
|
|
117
|
+
* **Idempotent.** Writing the same value twice writes the same bytes to the same
|
|
118
|
+
* path and reports the same hash. The write is unconditional rather than
|
|
119
|
+
* skipped-if-present, which also means a corrupted file is repaired by the next
|
|
120
|
+
* store of the same payload: content addressing makes "overwrite" and "leave
|
|
121
|
+
* alone" the same operation for anything that was ever valid.
|
|
122
|
+
*
|
|
123
|
+
* Refuses `unserializable` for material RFC 8785 cannot serialize (a cycle, a
|
|
124
|
+
* NaN, a function). A payload that cannot be canonicalized cannot be bound to,
|
|
125
|
+
* so it must not be stored under a hash that would then name something else.
|
|
126
|
+
*/
|
|
127
|
+
export function storePayload(storeDir, value) {
|
|
128
|
+
let hash;
|
|
129
|
+
let bytes;
|
|
130
|
+
try {
|
|
131
|
+
bytes = canonicalize(value);
|
|
132
|
+
hash = payloadHash(value);
|
|
133
|
+
}
|
|
134
|
+
catch (cause) {
|
|
135
|
+
return {
|
|
136
|
+
ok: false,
|
|
137
|
+
code: "unserializable",
|
|
138
|
+
message: `the payload material could not be canonicalized: ${detail(cause)}. A payload that cannot be serialized cannot be bound to, so nothing was stored.`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const path = payloadPath(storeDir, hash);
|
|
142
|
+
const written = writeAtomic(path, bytes);
|
|
143
|
+
if (!written.ok) {
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
code: "write-failed",
|
|
147
|
+
message: `payload ${hash} could not be written to ${path}: ${written.message}. Nothing was stored and the log was not touched.`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return { ok: true, hash, path };
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Record an external reference for a payload this runtime does not hold.
|
|
154
|
+
*
|
|
155
|
+
* The hash is supplied by the caller because a reference does not determine one:
|
|
156
|
+
* the binding is a fact about the *payload*, which lives elsewhere, and the
|
|
157
|
+
* reference is only a pointer to it. {@link loadPayload} reports the pointer and
|
|
158
|
+
* resolves nothing — see the module header.
|
|
159
|
+
*/
|
|
160
|
+
export function storeReference(storeDir, hash, reference) {
|
|
161
|
+
if (!isPayloadHash(hash)) {
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
code: "unserializable",
|
|
165
|
+
message: `${JSON.stringify(hash)} is not a payload hash (SHA-256, lowercase hex); a reference must be filed under the hash the request bound to`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const path = payloadPath(storeDir, hash);
|
|
169
|
+
const written = writeAtomic(path, canonicalize({ $ref: reference }));
|
|
170
|
+
if (!written.ok) {
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
code: "write-failed",
|
|
174
|
+
message: `reference for ${hash} could not be written to ${path}: ${written.message}`,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return { ok: true, hash, path };
|
|
178
|
+
}
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
// Enumeration and removal
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
/**
|
|
183
|
+
* Every payload hash the store currently holds, sorted.
|
|
184
|
+
*
|
|
185
|
+
* Only `<hash>.json` names whose stem is a well-formed `payload_hash` are
|
|
186
|
+
* reported. A directory, a dotfile (the temp name an interrupted
|
|
187
|
+
* {@link writeAtomic} leaves behind), and anything else a human dropped in the
|
|
188
|
+
* directory are not payloads the store addresses, so no caller — the daemon's
|
|
189
|
+
* pruner least of all — is ever handed one as if it were.
|
|
190
|
+
*
|
|
191
|
+
* An unreadable or absent directory yields the empty list rather than throwing:
|
|
192
|
+
* a store that cannot be listed holds nothing this process can act on, which is
|
|
193
|
+
* the fail-closed reading for every caller here (reporting counts, deciding what
|
|
194
|
+
* to prune).
|
|
195
|
+
*/
|
|
196
|
+
export function listStoredPayloadHashes(storeDir) {
|
|
197
|
+
let entries;
|
|
198
|
+
try {
|
|
199
|
+
entries = readdirSync(storeDir, { withFileTypes: true });
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
const hashes = [];
|
|
205
|
+
for (const entry of entries) {
|
|
206
|
+
if (!entry.isFile() || entry.name.startsWith(".") || !entry.name.endsWith(".json"))
|
|
207
|
+
continue;
|
|
208
|
+
const stem = entry.name.slice(0, -".json".length);
|
|
209
|
+
if (isPayloadHash(stem))
|
|
210
|
+
hashes.push(stem);
|
|
211
|
+
}
|
|
212
|
+
return hashes.sort();
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Delete the bytes stored under `hash`.
|
|
216
|
+
*
|
|
217
|
+
* **Daemon-only.** SPEC.md §5.2: "Pruning is performed by the daemon and by
|
|
218
|
+
* nothing else." Nothing in the CLI calls this, and the retention rule that
|
|
219
|
+
* decides *which* hash reaches it lives in `daemon/prune.ts`, which appends the
|
|
220
|
+
* `payload.pruned` event before the file goes. This function is the unlink and
|
|
221
|
+
* nothing more: it judges no retention, reads no policy, and touches no log.
|
|
222
|
+
*
|
|
223
|
+
* A file that is already gone reports `existed: false` and `ok`. The caller's
|
|
224
|
+
* intent is that the bytes not be there, and a removal that raced another
|
|
225
|
+
* removal (or completed a crash-interrupted one) achieved exactly that.
|
|
226
|
+
*/
|
|
227
|
+
export function removeStoredPayload(storeDir, hash) {
|
|
228
|
+
const path = payloadPath(storeDir, hash);
|
|
229
|
+
if (!isPayloadHash(hash)) {
|
|
230
|
+
return {
|
|
231
|
+
ok: false,
|
|
232
|
+
code: "unlink-failed",
|
|
233
|
+
message: `${JSON.stringify(hash)} is not a payload hash; the store is addressed by hash and nothing else, so nothing was removed`,
|
|
234
|
+
path,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
unlinkSync(path);
|
|
239
|
+
}
|
|
240
|
+
catch (cause) {
|
|
241
|
+
if (cause.code === "ENOENT") {
|
|
242
|
+
return { ok: true, path, existed: false };
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
ok: false,
|
|
246
|
+
code: "unlink-failed",
|
|
247
|
+
message: `payload ${path} could not be removed: ${detail(cause)}`,
|
|
248
|
+
path,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return { ok: true, path, existed: true };
|
|
252
|
+
}
|
|
253
|
+
/** Is this the `{"$ref": "<uri>"}` form, and nothing else? */
|
|
254
|
+
function referenceOf(value) {
|
|
255
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
256
|
+
return null;
|
|
257
|
+
const keys = Object.keys(value);
|
|
258
|
+
if (keys.length !== 1 || keys[0] !== "$ref")
|
|
259
|
+
return null;
|
|
260
|
+
const ref = value.$ref;
|
|
261
|
+
return typeof ref === "string" ? ref : null;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Load the material stored under `hash`, verifying it against `hash`.
|
|
265
|
+
*
|
|
266
|
+
* The verification is unconditional and the value is withheld when it fails:
|
|
267
|
+
* `hash-mismatch` says the file on disk is not the payload the request bound to,
|
|
268
|
+
* and returning it "so the caller can decide" would hand a channel exactly the
|
|
269
|
+
* bytes the binding exists to keep away from an approver.
|
|
270
|
+
*/
|
|
271
|
+
export function loadPayload(storeDir, hash) {
|
|
272
|
+
const path = payloadPath(storeDir, hash);
|
|
273
|
+
if (!isPayloadHash(hash)) {
|
|
274
|
+
return {
|
|
275
|
+
ok: false,
|
|
276
|
+
code: "unreadable",
|
|
277
|
+
message: `${JSON.stringify(hash)} is not a payload hash (SHA-256, lowercase hex); the store is addressed by hash and nothing else`,
|
|
278
|
+
path,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
let raw;
|
|
282
|
+
try {
|
|
283
|
+
raw = readFileSync(path, "utf8");
|
|
284
|
+
}
|
|
285
|
+
catch (cause) {
|
|
286
|
+
if (cause.code === "ENOENT") {
|
|
287
|
+
return { ok: false, code: "absent", message: `no payload is stored at ${path}`, path };
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
ok: false,
|
|
291
|
+
code: "unreadable",
|
|
292
|
+
message: `payload ${path} could not be read: ${detail(cause)}`,
|
|
293
|
+
path,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
let value;
|
|
297
|
+
try {
|
|
298
|
+
value = JSON.parse(raw);
|
|
299
|
+
}
|
|
300
|
+
catch (cause) {
|
|
301
|
+
return {
|
|
302
|
+
ok: false,
|
|
303
|
+
code: "unreadable",
|
|
304
|
+
message: `payload ${path} is not valid JSON: ${detail(cause)}`,
|
|
305
|
+
path,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
const reference = referenceOf(value);
|
|
309
|
+
if (reference !== null) {
|
|
310
|
+
return {
|
|
311
|
+
ok: false,
|
|
312
|
+
code: "reference",
|
|
313
|
+
message: `payload ${hash} is stored as an external reference (${reference}) and is not resolved here; a channel may show the reference but must not present bytes no hash bound`,
|
|
314
|
+
path,
|
|
315
|
+
reference,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
let actual;
|
|
319
|
+
try {
|
|
320
|
+
actual = payloadHash(value);
|
|
321
|
+
}
|
|
322
|
+
catch (cause) {
|
|
323
|
+
return {
|
|
324
|
+
ok: false,
|
|
325
|
+
code: "unreadable",
|
|
326
|
+
message: `payload ${path} could not be canonicalized for verification: ${detail(cause)}`,
|
|
327
|
+
path,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
if (actual !== hash) {
|
|
331
|
+
return {
|
|
332
|
+
ok: false,
|
|
333
|
+
code: "hash-mismatch",
|
|
334
|
+
message: `payload ${path} hashes to ${actual}, not to the ${hash} it is filed under; the stored bytes are not the bytes the request bound to and will not be rendered`,
|
|
335
|
+
path,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
return { ok: true, value, path };
|
|
339
|
+
}
|
|
340
|
+
//# sourceMappingURL=payload-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-store.js","sourceRoot":"","sources":["../../../src/core/payload-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content binding: the hash an approval is bound to (amended SPEC.md §6.2, §10,
|
|
3
|
+
* APRV-20 pass two / amendment A1).
|
|
4
|
+
*
|
|
5
|
+
* ## What this buys
|
|
6
|
+
*
|
|
7
|
+
* Before content binding, a grant approved an *action key*. The bytes that key
|
|
8
|
+
* would ultimately execute were whatever the executor happened to hold at
|
|
9
|
+
* execution time, which is to say: whatever the agent decided after the human
|
|
10
|
+
* said yes. The amended §10 closes that:
|
|
11
|
+
*
|
|
12
|
+
* > An execution token is bound to the request, its `idempotency_key`, AND its
|
|
13
|
+
* > `payload_hash`. Adapters and `approval run` MUST recompute the hash of the
|
|
14
|
+
* > payload they are about to execute and MUST refuse, with a distinct
|
|
15
|
+
* > machine-readable reason (`payload-mismatch`), when it differs from the hash
|
|
16
|
+
* > the grant recorded. A grant therefore approves specific bytes.
|
|
17
|
+
*
|
|
18
|
+
* So the hash is the *whole* mechanism: `core/gate.ts` refuses to admit a manual
|
|
19
|
+
* action whose declaration carries none (`payload-hash-required`), copies it
|
|
20
|
+
* onto `approval.requested` and then onto `approval.granted`, and
|
|
21
|
+
* `core/token.ts` refuses to spend a token against different bytes
|
|
22
|
+
* (`payload-mismatch`).
|
|
23
|
+
*
|
|
24
|
+
* ## Why JCS, and why not "just hash the string"
|
|
25
|
+
*
|
|
26
|
+
* §6.2 says "SHA-256 over the RFC 8785 canonical serialization of the action's
|
|
27
|
+
* concrete payload". Canonicalization is what makes the hash reproducible across
|
|
28
|
+
* two implementations that agree about the *payload* but not about key order,
|
|
29
|
+
* whitespace, or number formatting — which is exactly the situation an adapter
|
|
30
|
+
* written in another language is in. The same canonicalizer the log's hash chain
|
|
31
|
+
* uses (`core/jcs.ts`) is used here, so there is one serialization rule in this
|
|
32
|
+
* codebase and not two.
|
|
33
|
+
*
|
|
34
|
+
* ## The `approval run` payload
|
|
35
|
+
*
|
|
36
|
+
* §6.2 names it: "for `approval run`, the argv array and cwd". {@link
|
|
37
|
+
* runPayloadHash} is that definition made executable — the hash of
|
|
38
|
+
* `{"argv": [...], "cwd": "..."}` — and `approval run` computes it
|
|
39
|
+
* automatically from the command it is about to spawn. An adapter whose payload
|
|
40
|
+
* is something else (a message body and its recipients, a proposed record)
|
|
41
|
+
* hashes that with {@link payloadHash} and presents the result explicitly.
|
|
42
|
+
*
|
|
43
|
+
* The cwd is inside the hash on purpose: `rm -rf build` means two different
|
|
44
|
+
* things in two different directories, and a binding that ignored the working
|
|
45
|
+
* directory would approve one of them and execute the other.
|
|
46
|
+
*
|
|
47
|
+
* Pure: no I/O, no clock, no randomness.
|
|
48
|
+
*/
|
|
49
|
+
/** The form a `payload_hash` takes everywhere: SHA-256, lowercase hex. */
|
|
50
|
+
export declare const PAYLOAD_HASH_PATTERN: RegExp;
|
|
51
|
+
/** Is this a well-formed `payload_hash`? The schema says the same thing. */
|
|
52
|
+
export declare function isPayloadHash(value: unknown): value is string;
|
|
53
|
+
/**
|
|
54
|
+
* SHA-256 (lowercase hex) over the RFC 8785 canonical serialization of a
|
|
55
|
+
* payload — the definition amended SPEC.md §6.2 gives for `payload_hash`.
|
|
56
|
+
*
|
|
57
|
+
* Throws `JcsError` for a value RFC 8785 cannot serialize (a cycle, a NaN, a
|
|
58
|
+
* function). That is deliberate: a payload that cannot be canonicalized cannot
|
|
59
|
+
* be bound to, and returning a plausible-looking digest for it would be worse
|
|
60
|
+
* than failing.
|
|
61
|
+
*/
|
|
62
|
+
export declare function payloadHash(value: unknown): string;
|
|
63
|
+
/**
|
|
64
|
+
* The `payload_hash` of an `approval run` invocation: the argv array and cwd.
|
|
65
|
+
*
|
|
66
|
+
* `argv` is the child's argv as it will actually be spawned — the command name
|
|
67
|
+
* first, then its arguments — not the `approval` wrapper's own flags, which
|
|
68
|
+
* have no side effect to bind. `cwd` is the absolute directory the child will
|
|
69
|
+
* run in.
|
|
70
|
+
*/
|
|
71
|
+
export declare function runPayloadHash(argv: readonly string[], cwd: string): string;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content binding: the hash an approval is bound to (amended SPEC.md §6.2, §10,
|
|
3
|
+
* APRV-20 pass two / amendment A1).
|
|
4
|
+
*
|
|
5
|
+
* ## What this buys
|
|
6
|
+
*
|
|
7
|
+
* Before content binding, a grant approved an *action key*. The bytes that key
|
|
8
|
+
* would ultimately execute were whatever the executor happened to hold at
|
|
9
|
+
* execution time, which is to say: whatever the agent decided after the human
|
|
10
|
+
* said yes. The amended §10 closes that:
|
|
11
|
+
*
|
|
12
|
+
* > An execution token is bound to the request, its `idempotency_key`, AND its
|
|
13
|
+
* > `payload_hash`. Adapters and `approval run` MUST recompute the hash of the
|
|
14
|
+
* > payload they are about to execute and MUST refuse, with a distinct
|
|
15
|
+
* > machine-readable reason (`payload-mismatch`), when it differs from the hash
|
|
16
|
+
* > the grant recorded. A grant therefore approves specific bytes.
|
|
17
|
+
*
|
|
18
|
+
* So the hash is the *whole* mechanism: `core/gate.ts` refuses to admit a manual
|
|
19
|
+
* action whose declaration carries none (`payload-hash-required`), copies it
|
|
20
|
+
* onto `approval.requested` and then onto `approval.granted`, and
|
|
21
|
+
* `core/token.ts` refuses to spend a token against different bytes
|
|
22
|
+
* (`payload-mismatch`).
|
|
23
|
+
*
|
|
24
|
+
* ## Why JCS, and why not "just hash the string"
|
|
25
|
+
*
|
|
26
|
+
* §6.2 says "SHA-256 over the RFC 8785 canonical serialization of the action's
|
|
27
|
+
* concrete payload". Canonicalization is what makes the hash reproducible across
|
|
28
|
+
* two implementations that agree about the *payload* but not about key order,
|
|
29
|
+
* whitespace, or number formatting — which is exactly the situation an adapter
|
|
30
|
+
* written in another language is in. The same canonicalizer the log's hash chain
|
|
31
|
+
* uses (`core/jcs.ts`) is used here, so there is one serialization rule in this
|
|
32
|
+
* codebase and not two.
|
|
33
|
+
*
|
|
34
|
+
* ## The `approval run` payload
|
|
35
|
+
*
|
|
36
|
+
* §6.2 names it: "for `approval run`, the argv array and cwd". {@link
|
|
37
|
+
* runPayloadHash} is that definition made executable — the hash of
|
|
38
|
+
* `{"argv": [...], "cwd": "..."}` — and `approval run` computes it
|
|
39
|
+
* automatically from the command it is about to spawn. An adapter whose payload
|
|
40
|
+
* is something else (a message body and its recipients, a proposed record)
|
|
41
|
+
* hashes that with {@link payloadHash} and presents the result explicitly.
|
|
42
|
+
*
|
|
43
|
+
* The cwd is inside the hash on purpose: `rm -rf build` means two different
|
|
44
|
+
* things in two different directories, and a binding that ignored the working
|
|
45
|
+
* directory would approve one of them and execute the other.
|
|
46
|
+
*
|
|
47
|
+
* Pure: no I/O, no clock, no randomness.
|
|
48
|
+
*/
|
|
49
|
+
import { createHash } from "node:crypto";
|
|
50
|
+
import { canonicalize } from "./jcs.js";
|
|
51
|
+
/** The form a `payload_hash` takes everywhere: SHA-256, lowercase hex. */
|
|
52
|
+
export const PAYLOAD_HASH_PATTERN = /^[a-f0-9]{64}$/u;
|
|
53
|
+
/** Is this a well-formed `payload_hash`? The schema says the same thing. */
|
|
54
|
+
export function isPayloadHash(value) {
|
|
55
|
+
return typeof value === "string" && PAYLOAD_HASH_PATTERN.test(value);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* SHA-256 (lowercase hex) over the RFC 8785 canonical serialization of a
|
|
59
|
+
* payload — the definition amended SPEC.md §6.2 gives for `payload_hash`.
|
|
60
|
+
*
|
|
61
|
+
* Throws `JcsError` for a value RFC 8785 cannot serialize (a cycle, a NaN, a
|
|
62
|
+
* function). That is deliberate: a payload that cannot be canonicalized cannot
|
|
63
|
+
* be bound to, and returning a plausible-looking digest for it would be worse
|
|
64
|
+
* than failing.
|
|
65
|
+
*/
|
|
66
|
+
export function payloadHash(value) {
|
|
67
|
+
return createHash("sha256").update(canonicalize(value), "utf8").digest("hex");
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The `payload_hash` of an `approval run` invocation: the argv array and cwd.
|
|
71
|
+
*
|
|
72
|
+
* `argv` is the child's argv as it will actually be spawned — the command name
|
|
73
|
+
* first, then its arguments — not the `approval` wrapper's own flags, which
|
|
74
|
+
* have no side effect to bind. `cwd` is the absolute directory the child will
|
|
75
|
+
* run in.
|
|
76
|
+
*/
|
|
77
|
+
export function runPayloadHash(argv, cwd) {
|
|
78
|
+
return payloadHash({ argv: [...argv], cwd });
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=payload.js.map
|