approval-md 0.1.0 → 0.3.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/README.md +629 -559
- package/SPEC.md +99 -24
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +2 -2
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +110 -16
- package/dist/src/adapters/contract.js.map +1 -1
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -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 +2 -1
- package/dist/src/adapters/registry.js.map +1 -1
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +3 -3
- 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/cli.d.ts +193 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/contract.d.ts +656 -0
- package/dist/src/channels/contract.js +200 -7
- package/dist/src/channels/contract.js.map +1 -1
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/telegram.d.ts +1944 -0
- package/dist/src/channels/telegram.js +218 -23
- package/dist/src/channels/telegram.js.map +1 -1
- package/dist/src/channels/web.d.ts +350 -0
- package/dist/src/channels/web.js +17 -0
- package/dist/src/channels/web.js.map +1 -1
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +25 -15
- package/dist/src/cli/adapter.js.map +1 -1
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/amend.js +214 -30
- package/dist/src/cli/amend.js.map +1 -1
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/attest.d.ts +50 -0
- package/dist/src/cli/attest.js +134 -7
- package/dist/src/cli/attest.js.map +1 -1
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/channel-telegram.d.ts +879 -0
- package/dist/src/cli/channel-telegram.js +311 -13
- package/dist/src/cli/channel-telegram.js.map +1 -1
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel.d.ts +80 -0
- package/dist/src/cli/channel.js +9 -0
- package/dist/src/cli/channel.js.map +1 -1
- package/dist/src/cli/checkpoint-tap.d.ts +169 -0
- package/dist/src/cli/codex-bridge.d.ts +819 -0
- package/dist/src/cli/codex-bridge.js +1607 -0
- package/dist/src/cli/codex-bridge.js.map +1 -0
- package/dist/src/cli/codex.d.ts +2 -0
- package/dist/src/cli/codex.js +469 -0
- package/dist/src/cli/codex.js.map +1 -0
- package/dist/src/cli/coverage.d.ts +61 -0
- package/dist/src/cli/daemon.d.ts +120 -0
- package/dist/src/cli/daemon.js +4 -1
- package/dist/src/cli/daemon.js.map +1 -1
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +586 -17
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/execute.js +25 -2
- package/dist/src/cli/execute.js.map +1 -1
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/help.d.ts +107 -0
- package/dist/src/cli/help.js +320 -93
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hook-codex.d.ts +126 -0
- package/dist/src/cli/hook-codex.js +226 -0
- package/dist/src/cli/hook-codex.js.map +1 -0
- package/dist/src/cli/hook.d.ts +787 -0
- package/dist/src/cli/hook.js +1235 -181
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/import.js +1 -1
- package/dist/src/cli/import.js.map +1 -1
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +2 -2
- package/dist/src/cli/init.js.map +1 -1
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +102 -11
- package/dist/src/cli/log-advance.js.map +1 -1
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +7 -1
- package/dist/src/cli/log-verbs.js.map +1 -1
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +159 -7
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/policy-apply.d.ts +195 -0
- package/dist/src/cli/policy-apply.js +573 -0
- package/dist/src/cli/policy-apply.js.map +1 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/policy.js +14 -1
- package/dist/src/cli/policy.js.map +1 -1
- package/dist/src/cli/preflight.d.ts +501 -0
- package/dist/src/cli/preflight.js +689 -45
- package/dist/src/cli/preflight.js.map +1 -1
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/prompt.d.ts +209 -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/render.d.ts +22 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/sandbox.js +17 -1
- package/dist/src/cli/sandbox.js.map +1 -1
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/scaffold.js +1 -1
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +38 -4
- package/dist/src/cli/setup-adapter.js.map +1 -1
- package/dist/src/cli/setup-channel.d.ts +126 -0
- package/dist/src/cli/setup-channel.js +28 -1
- package/dist/src/cli/setup-channel.js.map +1 -1
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-common.d.ts +277 -0
- package/dist/src/cli/setup-common.js +3 -2
- package/dist/src/cli/setup-common.js.map +1 -1
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup.d.ts +204 -0
- package/dist/src/cli/setup.js +94 -2
- package/dist/src/cli/setup.js.map +1 -1
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +119 -53
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/values.js +3 -4
- package/dist/src/cli/values.js.map +1 -1
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +2 -2
- package/dist/src/cli/vault.js.map +1 -1
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +344 -11
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/codex/broker.d.ts +229 -0
- package/dist/src/codex/broker.js +548 -0
- package/dist/src/codex/broker.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/runner.d.ts +178 -0
- package/dist/src/codex/runner.js +231 -0
- package/dist/src/codex/runner.js.map +1 -0
- package/dist/src/codex/serve.d.ts +56 -0
- package/dist/src/codex/serve.js +98 -0
- package/dist/src/codex/serve.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-commit.d.ts +219 -0
- package/dist/src/codex/workspace-commit.js +549 -0
- package/dist/src/codex/workspace-commit.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 +221 -0
- package/dist/src/core/advance-cycle.js +66 -2
- package/dist/src/core/advance-cycle.js.map +1 -1
- package/dist/src/core/agents-md.d.ts +278 -0
- package/dist/src/core/agents-md.js +33 -31
- package/dist/src/core/agents-md.js.map +1 -1
- 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 +635 -0
- package/dist/src/core/attest.js +326 -4
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +510 -0
- package/dist/src/core/audit.js +13 -0
- package/dist/src/core/audit.js.map +1 -1
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/channel-owner.d.ts +213 -0
- package/dist/src/core/channel-owner.js +358 -0
- package/dist/src/core/channel-owner.js.map +1 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/command-class.d.ts +697 -0
- package/dist/src/core/command-class.js +713 -25
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/commit-guard.d.ts +272 -0
- package/dist/src/core/commit-guard.js +424 -0
- package/dist/src/core/commit-guard.js.map +1 -0
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/daemon-actor.d.ts +45 -0
- package/dist/src/core/daemon-actor.js +54 -0
- package/dist/src/core/daemon-actor.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +432 -0
- package/dist/src/core/dark-session.js +266 -82
- package/dist/src/core/dark-session.js.map +1 -1
- package/dist/src/core/decision-refusal.d.ts +206 -0
- package/dist/src/core/decision-refusal.js +24 -2
- package/dist/src/core/decision-refusal.js.map +1 -1
- package/dist/src/core/env-file.d.ts +455 -0
- package/dist/src/core/env-file.js +60 -1
- package/dist/src/core/env-file.js.map +1 -1
- package/dist/src/core/execute.d.ts +871 -0
- package/dist/src/core/execute.js +59 -8
- package/dist/src/core/execute.js.map +1 -1
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate.d.ts +1449 -0
- package/dist/src/core/gate.js +149 -14
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/gesture-refusal.d.ts +166 -0
- package/dist/src/core/gesture-refusal.js +188 -0
- package/dist/src/core/gesture-refusal.js.map +1 -0
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +4 -1
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/instance.d.ts +310 -0
- package/dist/src/core/instance.js +113 -0
- package/dist/src/core/instance.js.map +1 -1
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/log-reconcile.d.ts +89 -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 +316 -0
- package/dist/src/core/log.js.map +1 -1
- package/dist/src/core/loop.d.ts +274 -0
- package/dist/src/core/loop.js +11 -0
- package/dist/src/core/loop.js.map +1 -1
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +27 -4
- package/dist/src/core/policy-diff.js.map +1 -1
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-explain.d.ts +160 -0
- package/dist/src/core/policy-explain.js +63 -3
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +567 -0
- package/dist/src/core/policy-load.js +36 -6
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +324 -0
- package/dist/src/core/policy-match.js +72 -9
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +317 -0
- package/dist/src/core/policy-proposal.js +102 -2
- package/dist/src/core/policy-proposal.js.map +1 -1
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/protected-path-guard.d.ts +566 -0
- package/dist/src/core/protected-path-guard.js +848 -55
- package/dist/src/core/protected-path-guard.js.map +1 -1
- package/dist/src/core/question-preempted.d.ts +141 -0
- package/dist/src/core/question-preempted.js +152 -0
- package/dist/src/core/question-preempted.js.map +1 -0
- package/dist/src/core/read-scope.d.ts +172 -0
- package/dist/src/core/read-scope.js +252 -0
- package/dist/src/core/read-scope.js.map +1 -0
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sandbox.d.ts +371 -0
- package/dist/src/core/sandbox.js +190 -1
- package/dist/src/core/sandbox.js.map +1 -1
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/sender-identity.d.ts +476 -0
- package/dist/src/core/sender-identity.js +572 -0
- package/dist/src/core/sender-identity.js.map +1 -0
- package/dist/src/core/shlex.d.ts +102 -0
- package/dist/src/core/shlex.js +159 -0
- package/dist/src/core/shlex.js.map +1 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +21 -38
- package/dist/src/core/token.js.map +1 -1
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/values.d.ts +147 -0
- package/dist/src/core/values.js +36 -1
- package/dist/src/core/values.js.map +1 -1
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance.d.ts +476 -0
- package/dist/src/daemon/advance.js +25 -4
- package/dist/src/daemon/advance.js.map +1 -1
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/daemon.js +9 -0
- package/dist/src/daemon/daemon.js.map +1 -1
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +17 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +1316 -63
- package/docs/codex-enforced-session.md +103 -0
- package/docs/codex-workspace-broker.md +118 -0
- package/package.json +14 -2
- package/schema/codex-instance.schema.json +82 -0
- package/schema/event.schema.json +539 -9
- 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/event/invalid/approval-granted-sender-hashed-false.json +20 -0
- package/schema/fixtures/event/invalid/approval-granted-sender-hashed-raw-id.json +20 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-human-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-no-actor-no-sender.json +15 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-unknown-gesture.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-no-question-id.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-unknown-source.json +15 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-missing-path.json +13 -0
- package/schema/fixtures/event/valid/approval-granted-sender-hashed.json +20 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-review-note.json +21 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-sender-key-unavailable.json +19 -0
- package/schema/fixtures/event/valid/audit-gesture-refused.json +19 -0
- package/schema/fixtures/event/valid/audit-question-preempted-no-verdict.json +16 -0
- package/schema/fixtures/event/valid/audit-question-preempted.json +20 -0
- package/schema/fixtures/event/valid/gate-path-signed-off.json +14 -0
- package/schema/fixtures/event/valid/harness-kind-claude-code.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-codex.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-cursor.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-grok.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-muse.json +23 -0
- package/schema/fixtures/policy/invalid/senders-half-keyed.json +20 -0
- package/schema/fixtures/policy/valid/canonical.json +1 -1
- package/schema/fixtures/policy/valid/senders-keyed.json +24 -0
- package/schema/fixtures/policy-md/valid/canonical.md +1 -1
- package/schema/fixtures/policy-md/valid/with-values.md +5 -7
- package/schema/fixtures/values/invalid/class-shaped.json +1 -1
- package/schema/fixtures/values/invalid/duplicate-entry.json +1 -1
- package/schema/fixtures/values/invalid/non-string-item.json +1 -1
- package/schema/fixtures/values/invalid/over-cap.json +1 -1
- package/schema/fixtures/values/invalid/unknown-key.json +1 -1
- package/schema/fixtures/values/invalid/version-float.json +1 -0
- package/schema/fixtures/values/invalid/version-integer.json +1 -0
- package/schema/fixtures/values/invalid/version-wrong-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +2 -3
- package/schema/fixtures/values/valid/full.json +5 -7
- package/schema/fixtures/values/valid/minimal.json +1 -1
- package/schema/fixtures/values-md/invalid/schema-invalid.md +5 -3
- package/schema/fixtures/values-md/invalid/two-blocks.md +3 -3
- package/schema/fixtures/values-md/invalid/unterminated.md +2 -2
- package/schema/fixtures/values-md/invalid/version-1.md +69 -0
- package/schema/fixtures/values-md/invalid/version-unquoted.md +64 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +2 -2
- package/schema/fixtures/values-md/valid/absent.md +1 -1
- package/schema/fixtures/values-md/valid/with-values.md +5 -7
- package/schema/policy.schema.json +75 -3
- package/schema/values.schema.json +7 -11
- package/templates/codex/README.md +9 -0
- package/schema/fixtures/values/invalid/version-string.json +0 -1
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The daemon-answered live draw (APRV-208).
|
|
3
|
+
*
|
|
4
|
+
* ## The hole this closes
|
|
5
|
+
*
|
|
6
|
+
* `supervised-live` (APRV-127) puts a declared fraction of a class through the
|
|
7
|
+
* human gate, chosen by HMAC over the payload hash under an operator-held
|
|
8
|
+
* secret. `core/sampler.ts`'s {@link import("./sampler.js").resolveLiveSelector}
|
|
9
|
+
* reads that secret out of the DECIDING PROCESS's own environment, and the
|
|
10
|
+
* deciding process is usually the harness hook — a short-lived child of an
|
|
11
|
+
* agent session. Nothing sources the secret there, and nothing may: a secret an
|
|
12
|
+
* agent-launched process can read is a secret the agent can read, and an agent
|
|
13
|
+
* that can compute its own luck can grind its payload until the draw comes up
|
|
14
|
+
* unsampled. SPEC.md §5.2 forbids exactly that.
|
|
15
|
+
*
|
|
16
|
+
* So the draw failed closed, correctly and permanently: measured on this
|
|
17
|
+
* repository's own log, 15 of 15 supervised-live actions since the seq 5147
|
|
18
|
+
* ceremony gated to a human (APRV-184). A class an operator asked to sample at
|
|
19
|
+
* 10% was gated at 100%, which is safe and is also the setting never once
|
|
20
|
+
* having been live.
|
|
21
|
+
*
|
|
22
|
+
* The fix is not to hand the hook the secret. It is to move the DRAW to the one
|
|
23
|
+
* process that legitimately holds it — the daemon, started by the operator in a
|
|
24
|
+
* terminal where `eval "$(approval env)"` has run — and let the hook ASK.
|
|
25
|
+
*
|
|
26
|
+
* ## The protocol, and what each part is for
|
|
27
|
+
*
|
|
28
|
+
* One owner-only Unix socket under the approval home
|
|
29
|
+
* (`.approval/daemon/draw.sock`, directory 0700, socket 0600). One JSON line in,
|
|
30
|
+
* one JSON line out, connection closed.
|
|
31
|
+
*
|
|
32
|
+
* ```
|
|
33
|
+
* -> {"v":1,"action_key":"...","payload_hash":"<64hex>","policy_hash":"<64hex>","live_rate":0.1}
|
|
34
|
+
* <- {"v":1,"question":{...},"selected":true,"mac":"<64hex>","daemon_pid":123,"answered_at":"..."}
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* - **The question is a PROPOSAL; the echoed question is the daemon's own
|
|
38
|
+
* derivation.** The daemon does not take `live_rate` or `policy_hash` on the
|
|
39
|
+
* asker's word — it resolves the class from its own policy and echoes what it
|
|
40
|
+
* derived. The asker requires the echo to equal what it asked. A daemon and a
|
|
41
|
+
* hook reading different policies therefore fail closed instead of agreeing on
|
|
42
|
+
* the wrong rate, and no answer is ever MAC'd over a rate the daemon did not
|
|
43
|
+
* itself compute.
|
|
44
|
+
* - **The MAC binds the whole question AND the verdict.** `HMAC-SHA-256(secret,
|
|
45
|
+
* domain || canonical question || "\n" || selected)`. An answer is worth
|
|
46
|
+
* nothing without it: the asker cannot check it (it holds no secret), so it
|
|
47
|
+
* records it, and an operator who holds the secret recomputes it later from
|
|
48
|
+
* the record's own `action_key`, `payload_hash` and `policy_sha256`. That is
|
|
49
|
+
* what keeps SPEC.md §11's "self-reported fields never reduce scrutiny" true
|
|
50
|
+
* here — the answer is not believed, it is EVIDENCED, and an answer that
|
|
51
|
+
* cannot be evidenced (absent daemon, dead daemon, malformed reply) is refused
|
|
52
|
+
* and the action gates.
|
|
53
|
+
* - **A domain separator.** The MAC message is prefixed with
|
|
54
|
+
* {@link DRAW_MAC_DOMAIN} so a MAC can never be confused with, or substituted
|
|
55
|
+
* for, the selection value `core/sampler.ts` derives from the same key over a
|
|
56
|
+
* bare payload hash.
|
|
57
|
+
*
|
|
58
|
+
* ## Why the socket is not a free oracle
|
|
59
|
+
*
|
|
60
|
+
* An owner-only socket is reachable by any process of the same user, and an
|
|
61
|
+
* agent session runs as that user. If the daemon answered any question put to
|
|
62
|
+
* it, the agent could ask "would these bytes be sampled?" for a thousand
|
|
63
|
+
* candidate payloads and pick one that is not — the very grinding attack the
|
|
64
|
+
* secret exists to prevent, restored through the front door.
|
|
65
|
+
*
|
|
66
|
+
* `daemon/draw.ts` closes it by refusing to answer for an action that is not
|
|
67
|
+
* ALREADY REGISTERED in the verified log with exactly this payload hash. To ask
|
|
68
|
+
* a question you must first append a `task.registered` naming the bytes, so
|
|
69
|
+
* every candidate an agent grinds is permanently in the append-only log, in
|
|
70
|
+
* order, countable by anyone. The oracle still exists; it just cannot be
|
|
71
|
+
* consulted in secret, which is the property this project trades in.
|
|
72
|
+
*
|
|
73
|
+
* ## The secret is never here
|
|
74
|
+
*
|
|
75
|
+
* This module takes a secret as a parameter in exactly two pure functions
|
|
76
|
+
* ({@link drawMac}, {@link verifyDrawAnswer}) and stores it nowhere. Nothing in
|
|
77
|
+
* the client half ever sees one: the asking process holds no secret, which is
|
|
78
|
+
* the entire point.
|
|
79
|
+
*/
|
|
80
|
+
/** The protocol version. A mismatch is a refusal, never a negotiation. */
|
|
81
|
+
export declare const DRAW_PROTOCOL_VERSION = 1;
|
|
82
|
+
/** The socket's basename under the approval home's `daemon/` directory. */
|
|
83
|
+
export declare const DRAW_SOCKET_NAME = "draw.sock";
|
|
84
|
+
/** The MAC's domain separator. See the module header. */
|
|
85
|
+
export declare const DRAW_MAC_DOMAIN = "approval.md/live-draw/v1";
|
|
86
|
+
/**
|
|
87
|
+
* The longest socket path this runtime will bind or dial.
|
|
88
|
+
*
|
|
89
|
+
* `sockaddr_un.sun_path` is 104 bytes on macOS and 108 on Linux, and a `bind`
|
|
90
|
+
* past it fails with a message an operator cannot act on. Checked on both sides
|
|
91
|
+
* so the daemon reports it as a refusal to serve and the asker reports it as an
|
|
92
|
+
* absent daemon, rather than either of them producing an `ENAMETOOLONG` nobody
|
|
93
|
+
* expected.
|
|
94
|
+
*/
|
|
95
|
+
export declare const DRAW_SOCKET_PATH_LIMIT = 100;
|
|
96
|
+
/** How long the asking child waits for a connection and an answer. */
|
|
97
|
+
export declare const DRAW_TIMEOUT_MS = 500;
|
|
98
|
+
/** The whole child invocation, including Node's own start. */
|
|
99
|
+
export declare const DRAW_SPAWN_TIMEOUT_MS = 5000;
|
|
100
|
+
/**
|
|
101
|
+
* The approval home's `daemon/` directory for a log, derived and never
|
|
102
|
+
* configured.
|
|
103
|
+
*
|
|
104
|
+
* `logPath` is `<home>/log/events.jsonl`, so the home is two levels up. One log
|
|
105
|
+
* has one daemon directory: a reader that had to be TOLD where to look could be
|
|
106
|
+
* pointed at another instance's daemon, and answers would cross between
|
|
107
|
+
* instances that share a machine.
|
|
108
|
+
*/
|
|
109
|
+
export declare function drawDirFor(logPath: string): string;
|
|
110
|
+
/** Where the draw socket for `logPath` lives. */
|
|
111
|
+
export declare function drawSocketPathFor(logPath: string): string;
|
|
112
|
+
/**
|
|
113
|
+
* Which class patterns this policy declares `supervised-live`, sorted.
|
|
114
|
+
*
|
|
115
|
+
* The one question that decides whether any of this machinery is the operator's
|
|
116
|
+
* business at all: with no live class, no draw is ever made, so a missing
|
|
117
|
+
* socket is not a fault, an unset secret is not a misconfiguration, and neither
|
|
118
|
+
* deserves a doctor row or a line on the daemon's stderr. Shared by the doctor
|
|
119
|
+
* row and the daemon's server so the two cannot come to different conclusions
|
|
120
|
+
* about the same file.
|
|
121
|
+
*
|
|
122
|
+
* `defaults.autonomy` is not consulted, and cannot be: `supervised-live` carries
|
|
123
|
+
* a required rate that `defaults` has no field to hold, which `policy.schema.json`
|
|
124
|
+
* enforces. Only a class rule can be live.
|
|
125
|
+
*/
|
|
126
|
+
export declare function liveClassesOf(policy: {
|
|
127
|
+
classes?: Record<string, {
|
|
128
|
+
autonomy?: string;
|
|
129
|
+
} | undefined>;
|
|
130
|
+
}): string[];
|
|
131
|
+
/**
|
|
132
|
+
* What is asked, and what a MAC is computed over.
|
|
133
|
+
*
|
|
134
|
+
* Deliberately without a timestamp. A caller-supplied clock inside MAC'd
|
|
135
|
+
* material would let the same question be asked again for a different MAC, and
|
|
136
|
+
* SPEC.md §11's rule that gate-typed events take no caller timestamp is the
|
|
137
|
+
* same rule wearing a different hat. The answer carries the daemon's own
|
|
138
|
+
* `answered_at` outside the MAC, for an operator reading a live socket, and it
|
|
139
|
+
* is not recorded in the log.
|
|
140
|
+
*/
|
|
141
|
+
export interface DrawQuestion {
|
|
142
|
+
v: number;
|
|
143
|
+
action_key: string;
|
|
144
|
+
payload_hash: string;
|
|
145
|
+
/** The `policy_sha256` the asker is routing under, echoed by the daemon. */
|
|
146
|
+
policy_hash: string;
|
|
147
|
+
/** The class's `live_rate`, echoed by the daemon from its OWN resolution. */
|
|
148
|
+
live_rate: number;
|
|
149
|
+
}
|
|
150
|
+
/** What the daemon answers. */
|
|
151
|
+
export interface DrawAnswer {
|
|
152
|
+
v: number;
|
|
153
|
+
/** The daemon's own derivation of the question. Compared field for field. */
|
|
154
|
+
question: DrawQuestion;
|
|
155
|
+
selected: boolean;
|
|
156
|
+
/** {@link drawMac} over `question` and `selected`. 64 lowercase hex. */
|
|
157
|
+
mac: string;
|
|
158
|
+
daemon_pid: number;
|
|
159
|
+
answered_at: string;
|
|
160
|
+
}
|
|
161
|
+
/** Is this a well-formed 64-character lowercase hex digest? */
|
|
162
|
+
export declare function isHex64(value: unknown): value is string;
|
|
163
|
+
/**
|
|
164
|
+
* The canonical bytes a MAC covers: RFC 8785 over the question.
|
|
165
|
+
*
|
|
166
|
+
* `core/jcs.ts` is the runtime's one canonicalizer, so the asker, the daemon and
|
|
167
|
+
* a later verifier cannot drift apart over key order or number spelling.
|
|
168
|
+
*/
|
|
169
|
+
export declare function canonicalQuestion(question: DrawQuestion): string;
|
|
170
|
+
/** `HMAC-SHA-256(secret, domain || question || verdict)`, lowercase hex. */
|
|
171
|
+
export declare function drawMac(secret: string, question: DrawQuestion, selected: boolean): string;
|
|
172
|
+
/**
|
|
173
|
+
* Does this MAC belong to this question and this verdict, under this secret?
|
|
174
|
+
*
|
|
175
|
+
* Constant-time, which costs nothing and removes the argument about whether it
|
|
176
|
+
* matters. A malformed MAC is `false` rather than a throw: this is a verifier,
|
|
177
|
+
* and every wrong answer is one answer.
|
|
178
|
+
*/
|
|
179
|
+
export declare function verifyDrawAnswer(secret: string, question: DrawQuestion, selected: boolean, mac: string): boolean;
|
|
180
|
+
/**
|
|
181
|
+
* Why a delegated draw produced no usable answer. Machine-readable, closed and
|
|
182
|
+
* DISTINCT (SPEC.md §11 invariant 6), because the operator's action differs:
|
|
183
|
+
* "start the daemon", "your daemon is wedged or was killed", "something
|
|
184
|
+
* answered and it was not a daemon holding your secret".
|
|
185
|
+
*
|
|
186
|
+
* Every one of them gates the action, exactly as an unavailable secret does
|
|
187
|
+
* today. None of them is a degraded mode.
|
|
188
|
+
*/
|
|
189
|
+
export declare const DRAW_REFUSAL_REASONS: readonly [
|
|
190
|
+
/** No socket at the derived path: no daemon has ever served draws here. */
|
|
191
|
+
"draw-daemon-absent",
|
|
192
|
+
/** A socket that cannot be dialled, times out, or names a pid that is gone. */
|
|
193
|
+
"draw-daemon-stale",
|
|
194
|
+
/** An answer that is malformed, off-version, off-question, or badly MAC'd. */
|
|
195
|
+
"draw-answer-invalid"];
|
|
196
|
+
export type DrawRefusalReason = (typeof DRAW_REFUSAL_REASONS)[number];
|
|
197
|
+
/** What the asker got back. */
|
|
198
|
+
export type DrawOutcome = {
|
|
199
|
+
ok: true;
|
|
200
|
+
answer: DrawAnswer;
|
|
201
|
+
} | {
|
|
202
|
+
ok: false;
|
|
203
|
+
reason: DrawRefusalReason;
|
|
204
|
+
detail: string;
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* The `live_draw` field an `approval.requested` carries when the draw was
|
|
208
|
+
* DELEGATED (APRV-208).
|
|
209
|
+
*
|
|
210
|
+
* ## Why this is recorded at all, when APRV-127 records nothing
|
|
211
|
+
*
|
|
212
|
+
* APRV-127 deliberately wrote nothing about the selection to the log, and that
|
|
213
|
+
* property is unchanged for an in-process draw: when the deciding process holds
|
|
214
|
+
* the secret it IS the operator's process, its verdict needs no evidence, and a
|
|
215
|
+
* sampled request stays byte-for-byte a manual one (pinned by
|
|
216
|
+
* `tests/autonomy-split.test.ts`).
|
|
217
|
+
*
|
|
218
|
+
* A DELEGATED verdict is a different object. The deciding process did not
|
|
219
|
+
* compute it; another process asserted it. An assertion recorded without its
|
|
220
|
+
* proof is a self-reported field, and SPEC.md §11 says those never reduce
|
|
221
|
+
* scrutiny. So the delegation is recorded WITH the MAC that makes it checkable,
|
|
222
|
+
* and a delegation that cannot be evidenced is not recorded at all — it gates,
|
|
223
|
+
* and the field says which of the three refusals happened.
|
|
224
|
+
*
|
|
225
|
+
* What is never recorded: the secret, the selection value, or any timestamp the
|
|
226
|
+
* asker or the daemon supplied. The MAC is a digest under a key nobody in this
|
|
227
|
+
* process holds, and the operator recomputes it from the record's own fields.
|
|
228
|
+
*/
|
|
229
|
+
export interface LiveDrawRecord {
|
|
230
|
+
v: number;
|
|
231
|
+
/**
|
|
232
|
+
* `"daemon"` — a MAC'd answer, recorded with its proof.
|
|
233
|
+
* `"unavailable"` — no usable answer; `reason` says which refusal.
|
|
234
|
+
*/
|
|
235
|
+
source: "daemon" | "unavailable";
|
|
236
|
+
/** `selected` for a daemon answer, else the {@link DrawRefusalReason}. */
|
|
237
|
+
reason: "selected" | DrawRefusalReason;
|
|
238
|
+
/** The rate the draw was made at, so a verifier reconstructs the question. */
|
|
239
|
+
live_rate: number;
|
|
240
|
+
/** The daemon's verdict. Present only with a MAC to check it against. */
|
|
241
|
+
selected?: boolean;
|
|
242
|
+
mac?: string;
|
|
243
|
+
daemon_pid?: number;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Recompute a recorded delegation, from the record and the operator's secret.
|
|
247
|
+
*
|
|
248
|
+
* This is the whole point of the MAC, and it is deliberately a function of the
|
|
249
|
+
* RECORD rather than of anything the asker kept: `action_key` and
|
|
250
|
+
* `payload_hash` are the request's own fields and `policy_sha256` is assigned at
|
|
251
|
+
* the write boundary, so a verifier reconstructs the question from bytes the
|
|
252
|
+
* requester could not choose freely and could not alter afterwards without
|
|
253
|
+
* breaking the chain.
|
|
254
|
+
*
|
|
255
|
+
* `false` for a record with no MAC (a refusal), for a tampered MAC, and for a
|
|
256
|
+
* flipped verdict. Never throws.
|
|
257
|
+
*/
|
|
258
|
+
export declare function verifyLiveDrawRecord(secret: string, fields: {
|
|
259
|
+
actionKey: string;
|
|
260
|
+
payloadHash: string;
|
|
261
|
+
policyHash: string;
|
|
262
|
+
draw: LiveDrawRecord;
|
|
263
|
+
}): boolean;
|
|
264
|
+
/**
|
|
265
|
+
* Read one answer line against the question that was asked.
|
|
266
|
+
*
|
|
267
|
+
* Every check can only REJECT. The asker holds no secret, so it cannot check the
|
|
268
|
+
* MAC; what it CAN check is that the answer is this protocol's version, is an
|
|
269
|
+
* answer to this exact question (the daemon's own derivation, field for field),
|
|
270
|
+
* carries a MAC of the right shape, and names a live process. Anything else is
|
|
271
|
+
* `draw-answer-invalid` and the action gates.
|
|
272
|
+
*/
|
|
273
|
+
export declare function parseDrawAnswer(text: string, asked: DrawQuestion): DrawOutcome;
|
|
274
|
+
/** The seam a caller (and every test) substitutes for the real spawn. */
|
|
275
|
+
export type DrawAsker = (logPath: string, question: DrawQuestion) => DrawOutcome;
|
|
276
|
+
/** Where the relay child lives, beside this module in the built tree. */
|
|
277
|
+
export declare function drawChildPath(): string;
|
|
278
|
+
/**
|
|
279
|
+
* What an asker concludes about the socket file, before anything is dialled.
|
|
280
|
+
*
|
|
281
|
+
* Exported since APRV-281 for a second, non-asking caller: the hook prints a
|
|
282
|
+
* line saying where a request went and whether anything is there to consume it,
|
|
283
|
+
* and "anything is there" is exactly this question. Nothing is connected here,
|
|
284
|
+
* so a `{ ok: true }` says the file looks like this user's live daemon rather
|
|
285
|
+
* than that the far side answers; a caller that needs the stronger claim asks a
|
|
286
|
+
* question through {@link askDaemonDraw}.
|
|
287
|
+
*
|
|
288
|
+
* One predicate rather than a copy per caller, for the reason `liveClassesOf`
|
|
289
|
+
* is shared: two readers of the same file that reach different conclusions
|
|
290
|
+
* about whether a daemon is up would each be right in its own words and
|
|
291
|
+
* useless together.
|
|
292
|
+
*/
|
|
293
|
+
export declare function drawSocketUsable(path: string): {
|
|
294
|
+
ok: true;
|
|
295
|
+
} | {
|
|
296
|
+
ok: false;
|
|
297
|
+
reason: DrawRefusalReason;
|
|
298
|
+
detail: string;
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* Ask the daemon, synchronously, from a process that holds no secret.
|
|
302
|
+
*
|
|
303
|
+
* A child process, and the module header of `daemon/draw-child.ts` says why:
|
|
304
|
+
* the gate's request path is synchronous end to end (APRV-188 measured and
|
|
305
|
+
* documented the same constraint), `node:net` is not, and there is no
|
|
306
|
+
* synchronous Unix-socket client in Node. So one `spawnSync` of a tiny relay,
|
|
307
|
+
* about 20-40 ms of Node start. That cost is affordable HERE and nowhere else:
|
|
308
|
+
* this runs only for a `supervised-live` class whose deciding process has no
|
|
309
|
+
* secret, which is off the pass-through path entirely — no `Read`, no ordinary
|
|
310
|
+
* `Edit`, no autonomous class ever reaches it.
|
|
311
|
+
*
|
|
312
|
+
* The child is given a deliberately bare environment. It needs `PATH` for
|
|
313
|
+
* nothing and holds no secret; passing the session's environment to it would
|
|
314
|
+
* hand a relay every credential the session was launched with, which is the
|
|
315
|
+
* scrub `core/child-env.ts` exists to prevent.
|
|
316
|
+
*/
|
|
317
|
+
export declare function askDaemonDraw(logPath: string, question: DrawQuestion): DrawOutcome;
|
|
318
|
+
/**
|
|
319
|
+
* The second thing this socket answers: not "would these bytes be sampled" but
|
|
320
|
+
* "is your sampler running at all" (APRV-271).
|
|
321
|
+
*
|
|
322
|
+
* ## Why the socket, and not the asker's own environment
|
|
323
|
+
*
|
|
324
|
+
* `approval doctor`'s `audit-sampling` row read `APPROVAL_SAMPLING_SECRET` out
|
|
325
|
+
* of the shell doctor was launched in, and reported `secret-unset` whenever it
|
|
326
|
+
* was not there. It is never there: the secret lives in the ONE terminal the
|
|
327
|
+
* operator ran `eval "$(approval env)"` in and started the daemon from, and
|
|
328
|
+
* `core/child-env.ts` strips `APPROVAL_*` from every child. So the row was red
|
|
329
|
+
* on machines where sampling was running perfectly, which is the same shape of
|
|
330
|
+
* bug APRV-208 fixed for the draw itself, in the row next door.
|
|
331
|
+
*
|
|
332
|
+
* The process that legitimately knows is the daemon. This asks it.
|
|
333
|
+
*
|
|
334
|
+
* ## What it deliberately is not
|
|
335
|
+
*
|
|
336
|
+
* **Not MAC'd, and not believed the way a draw is.** A draw answer decides a
|
|
337
|
+
* verdict, so it is evidenced: recorded with a MAC an operator recomputes later
|
|
338
|
+
* (see {@link LiveDrawRecord}). This one decides nothing. It authorizes no
|
|
339
|
+
* action, spends no budget, gates nothing and is written to no log; it changes
|
|
340
|
+
* the wording and the colour of one diagnostic row. What bounds who may make
|
|
341
|
+
* the claim is the socket itself, which {@link askDaemonSampling} requires to
|
|
342
|
+
* be owner-only and owned by this euid before it will dial it.
|
|
343
|
+
*
|
|
344
|
+
* That bound is why the caller must keep the question narrow. The only sampler
|
|
345
|
+
* state another process may honestly answer for is `secret-unset`, which is a
|
|
346
|
+
* fact about a PROCESS ENVIRONMENT. Every other disabled reason (`rate-absent`,
|
|
347
|
+
* `rate-invalid`, `rate-zero`, `secret-env-unnamed`, `policy-unreadable`) is a
|
|
348
|
+
* fact about the policy FILE, which the asker is reading for itself and no
|
|
349
|
+
* daemon's answer may soften. `cli/doctor.ts` consults this on that one branch
|
|
350
|
+
* and nowhere else.
|
|
351
|
+
*
|
|
352
|
+
* **Never the secret.** The report carries the variable's NAME, which the
|
|
353
|
+
* policy file already states in the open, and the rate, which it also states.
|
|
354
|
+
* The value is in one field of one process and leaves it in nothing but a MAC.
|
|
355
|
+
*/
|
|
356
|
+
export declare const SAMPLING_QUERY = "sampling";
|
|
357
|
+
/** A daemon's report on its OWN sampler. Values are facts, not instructions. */
|
|
358
|
+
export interface SamplingReport {
|
|
359
|
+
/** Is `core/sampler.ts` enabled in the answering process? */
|
|
360
|
+
enabled: boolean;
|
|
361
|
+
/** The machine-readable disabled reason, or `null` when it is enabled. */
|
|
362
|
+
reason: string | null;
|
|
363
|
+
/** The NAME of the secret's environment variable. Never its value. */
|
|
364
|
+
secret_env: string | null;
|
|
365
|
+
/** The global fallback rate, `null` when only class rules declare one. */
|
|
366
|
+
rate: number | null;
|
|
367
|
+
/** Which class patterns the daemon's policy declares `supervised-live`. */
|
|
368
|
+
live_classes: string[];
|
|
369
|
+
}
|
|
370
|
+
/** What the daemon answers a {@link SAMPLING_QUERY} with. */
|
|
371
|
+
export interface SamplingAnswer {
|
|
372
|
+
v: number;
|
|
373
|
+
sampling: SamplingReport;
|
|
374
|
+
daemon_pid: number;
|
|
375
|
+
answered_at: string;
|
|
376
|
+
}
|
|
377
|
+
/** What the asker got back, with the socket it asked, for the report. */
|
|
378
|
+
export type SamplingOutcome = {
|
|
379
|
+
ok: true;
|
|
380
|
+
answer: SamplingAnswer;
|
|
381
|
+
socket: string;
|
|
382
|
+
} | {
|
|
383
|
+
ok: false;
|
|
384
|
+
reason: DrawRefusalReason;
|
|
385
|
+
detail: string;
|
|
386
|
+
socket: string;
|
|
387
|
+
};
|
|
388
|
+
/** Is this line a status question rather than a draw? Used by the server. */
|
|
389
|
+
export declare function isSamplingQuery(line: string): boolean;
|
|
390
|
+
/**
|
|
391
|
+
* Read one status answer.
|
|
392
|
+
*
|
|
393
|
+
* Every field is taken apart and rebuilt rather than passed through, so an
|
|
394
|
+
* answer carrying keys this protocol never defined loses them here: a report
|
|
395
|
+
* printed by a diagnostic must not be able to carry text of the answerer's
|
|
396
|
+
* choosing into an operator's terminal.
|
|
397
|
+
*/
|
|
398
|
+
export declare function parseSamplingAnswer(text: string): {
|
|
399
|
+
ok: true;
|
|
400
|
+
answer: SamplingAnswer;
|
|
401
|
+
} | {
|
|
402
|
+
ok: false;
|
|
403
|
+
reason: DrawRefusalReason;
|
|
404
|
+
detail: string;
|
|
405
|
+
};
|
|
406
|
+
/**
|
|
407
|
+
* Open the draw socket, optionally exchange one line, and hang up.
|
|
408
|
+
*
|
|
409
|
+
* ASYNCHRONOUS, unlike {@link askDaemonDraw}, and that difference is the whole
|
|
410
|
+
* reason it can exist without a relay child. The draw is asked from the gate's
|
|
411
|
+
* synchronous request path, which is why it pays for a `spawnSync`; every
|
|
412
|
+
* caller of this one is a diagnostic that is already inside an async function,
|
|
413
|
+
* so it dials the socket directly and costs no process at all.
|
|
414
|
+
*
|
|
415
|
+
* `request` of `null` connects and closes without saying anything, which is how
|
|
416
|
+
* a caller asks the only question a socket file cannot answer by existing:
|
|
417
|
+
* whether anything is listening on it.
|
|
418
|
+
*/
|
|
419
|
+
export declare function dialDrawSocket(path: string, request: string | null, timeoutMs?: number): Promise<{
|
|
420
|
+
ok: true;
|
|
421
|
+
line: string | null;
|
|
422
|
+
} | {
|
|
423
|
+
ok: false;
|
|
424
|
+
reason: DrawRefusalReason;
|
|
425
|
+
detail: string;
|
|
426
|
+
}>;
|
|
427
|
+
/**
|
|
428
|
+
* Ask the running daemon what its own sampler is doing.
|
|
429
|
+
*
|
|
430
|
+
* The same pre-checks a draw makes, for the same reasons: a path past the
|
|
431
|
+
* address limit, an absent file, a foreign owner and a group- or world-writable
|
|
432
|
+
* mode each mean no answer from here can be attributed to this user's daemon.
|
|
433
|
+
* The pid is checked for liveness afterwards, because a socket that outlived
|
|
434
|
+
* its server is a file answering for a process that is gone.
|
|
435
|
+
*/
|
|
436
|
+
export declare function askDaemonSampling(logPath: string, timeoutMs?: number): Promise<SamplingOutcome>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chain drift: how two copies of one log relate to each other (APRV-125).
|
|
3
|
+
*
|
|
4
|
+
* Two verbs and one doctor check ask the same question in three places. `approval
|
|
5
|
+
* log sync` asks it after a fast-forward pull ("is the committed baseline a
|
|
6
|
+
* prefix of the chain I was holding, or did the two fork?"), `approval doctor`
|
|
7
|
+
* asks it standing still ("is my working log ahead of what is committed, and by
|
|
8
|
+
* how much?"), and a future ambient runtime will ask it before offering to
|
|
9
|
+
* advance. Three implementations of one comparison would be three chances to
|
|
10
|
+
* disagree about whether a repository has forked, so there is one, and it lives
|
|
11
|
+
* here.
|
|
12
|
+
*
|
|
13
|
+
* ## What a comparison is allowed to read
|
|
14
|
+
*
|
|
15
|
+
* Only verified records. Both sides go through `core/verify.ts` before a single
|
|
16
|
+
* `seq` is compared, and a side that does not verify clean is a refusal rather
|
|
17
|
+
* than an answer: a torn or broken chain has no head worth naming, and
|
|
18
|
+
* "diverged" would be the wrong word for "unreadable". This is SPEC §11.1's
|
|
19
|
+
* "enforcement paths read only verified records" applied to the one path that
|
|
20
|
+
* decides whether a working log may be put back after a pull.
|
|
21
|
+
*
|
|
22
|
+
* ## The four relations, and why `behind` is not a fork
|
|
23
|
+
*
|
|
24
|
+
* The chains are compared record by record on their hashes, which is the whole
|
|
25
|
+
* comparison: a hash equality at position i means every byte of records 1..i is
|
|
26
|
+
* shared, because each hash covers its own record and its predecessor's.
|
|
27
|
+
*
|
|
28
|
+
* - `equal` — the same chain. Nothing to do.
|
|
29
|
+
* - `ahead` — the committed chain is a strict PREFIX of the working one. The
|
|
30
|
+
* working file carries appends that are not committed yet; this is the normal
|
|
31
|
+
* state of a machine that has been granting approvals.
|
|
32
|
+
* - `behind` — the working chain is a strict prefix of the committed one. The
|
|
33
|
+
* pull brought records this machine did not have. Adopting the longer chain
|
|
34
|
+
* extends the working log and rewinds nothing, so it is not a fork either.
|
|
35
|
+
* - `diverged` — the chains agree up to some point and then do not. Two
|
|
36
|
+
* appenders built different records on the same predecessor. Hash chains do
|
|
37
|
+
* not merge and nothing in this codebase may try to merge them; the only
|
|
38
|
+
* honest answer is the seq where they parted and a refusal.
|
|
39
|
+
*/
|
|
40
|
+
import type { LogHead } from "./log.js";
|
|
41
|
+
import type { ValidateOptions } from "./validate.js";
|
|
42
|
+
/** How the working chain stands relative to the committed one. */
|
|
43
|
+
export type LogRelation = "equal" | "ahead" | "behind" | "diverged";
|
|
44
|
+
/** The comparison's answer. Every field is present in every relation. */
|
|
45
|
+
export interface LogDrift {
|
|
46
|
+
relation: LogRelation;
|
|
47
|
+
/** Records the working chain holds beyond the committed one. */
|
|
48
|
+
ahead: number;
|
|
49
|
+
/** Records the committed chain holds beyond the working one. */
|
|
50
|
+
behind: number;
|
|
51
|
+
workingHead: LogHead | null;
|
|
52
|
+
committedHead: LogHead | null;
|
|
53
|
+
/**
|
|
54
|
+
* The first `seq` at which the two chains carry different records. `null`
|
|
55
|
+
* unless `relation` is `diverged`; a prefix relationship has no such point.
|
|
56
|
+
*/
|
|
57
|
+
firstDivergentSeq: number | null;
|
|
58
|
+
}
|
|
59
|
+
/** Why a comparison could not be made. Distinct from any relation. */
|
|
60
|
+
export type ReconcileRefusalCode = "working-unverified" | "committed-unverified";
|
|
61
|
+
export type ReconcileResult = {
|
|
62
|
+
ok: true;
|
|
63
|
+
drift: LogDrift;
|
|
64
|
+
} | {
|
|
65
|
+
ok: false;
|
|
66
|
+
code: ReconcileRefusalCode;
|
|
67
|
+
message: string;
|
|
68
|
+
};
|
|
69
|
+
/** One side of the comparison, named so a refusal can say which side failed. */
|
|
70
|
+
export interface ChainSide {
|
|
71
|
+
/** How this side is named in messages ("the working log", "HEAD:<path>"). */
|
|
72
|
+
label: string;
|
|
73
|
+
/** The whole file, as text. An absent file is the empty string. */
|
|
74
|
+
text: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Compare a working chain against a committed one.
|
|
78
|
+
*
|
|
79
|
+
* `working` is the chain a machine is holding (the file on disk, or a snapshot
|
|
80
|
+
* of it); `committed` is the chain in version control. The relation is stated
|
|
81
|
+
* from the working chain's point of view, which is the direction both callers
|
|
82
|
+
* report in: doctor says "ahead by 3", sync says "the committed baseline is a
|
|
83
|
+
* prefix, so the snapshot goes back".
|
|
84
|
+
*/
|
|
85
|
+
export declare function compareChains(working: ChainSide, committed: ChainSide, options?: ValidateOptions): ReconcileResult;
|
|
86
|
+
/** A head, as messages and runbooks spell one. */
|
|
87
|
+
export declare function describeHead(head: LogHead | null): string;
|
|
88
|
+
/** One sentence naming the relation, shared by sync's report and doctor's detail. */
|
|
89
|
+
export declare function describeDrift(drift: LogDrift): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pull-based subscription to the verified event log (APRV-322).
|
|
3
|
+
*
|
|
4
|
+
* A filesystem notification is only a hint that another read may be useful.
|
|
5
|
+
* Every emitted batch comes from one complete, genesis-to-head verification;
|
|
6
|
+
* a notification, a parsed line, or an intact prefix is never authority by
|
|
7
|
+
* itself. The iterator queues no events: while a consumer is slow it retains
|
|
8
|
+
* only the current verified snapshot and coalesces every wakeup into one bit.
|
|
9
|
+
*/
|
|
10
|
+
import type { EventRecord } from "./log.js";
|
|
11
|
+
import { type VerifyFailureReason } from "./verify.js";
|
|
12
|
+
export type LogSubscriptionFailureKind = "integrity" | "torn-tail" | "io";
|
|
13
|
+
/** A terminal subscription failure, mapped by the CLI to the existing exits. */
|
|
14
|
+
export declare class LogSubscriptionError extends Error {
|
|
15
|
+
readonly kind: LogSubscriptionFailureKind;
|
|
16
|
+
readonly reason: VerifyFailureReason | "cursor-mismatch" | null;
|
|
17
|
+
constructor(kind: LogSubscriptionFailureKind, message: string, reason?: VerifyFailureReason | "cursor-mismatch" | null);
|
|
18
|
+
}
|
|
19
|
+
export interface LogSubscriptionOptions {
|
|
20
|
+
/** Exclusive cursor. Zero replays the whole verified log. */
|
|
21
|
+
from?: number;
|
|
22
|
+
/** Hash of record `from`, retained outside the log by the consumer. */
|
|
23
|
+
expectedHash?: string;
|
|
24
|
+
/** Cancellation closes the watcher, timer, and signal listener. */
|
|
25
|
+
signal?: AbortSignal;
|
|
26
|
+
/** Bounded fallback when filesystem notifications are missing. */
|
|
27
|
+
pollIntervalMs?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Yield verified records after an exclusive cursor, then wait for appends.
|
|
31
|
+
*
|
|
32
|
+
* The optional expected hash binds the first read to the caller's stored
|
|
33
|
+
* cursor. After every yield, the iterator carries that same binding forward,
|
|
34
|
+
* so truncation or replacement during one process lifetime is also refused.
|
|
35
|
+
*/
|
|
36
|
+
export declare function subscribeVerifiedLog(logPath: string, options?: LogSubscriptionOptions): AsyncGenerator<EventRecord, void, void>;
|