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,499 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A minimal SMTP client, for the email adapter and nothing else (APRV-69).
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists rather than a dependency
|
|
5
|
+
*
|
|
6
|
+
* `CLAUDE.md` and SPEC.md §14 both put minimal dependencies among this
|
|
7
|
+
* repository's invariants, and a mail library is a large surface to take on for
|
|
8
|
+
* one send: nodemailer pulls a templating layer, an attachment pipeline, a DKIM
|
|
9
|
+
* signer, OAuth2 token refresh, and a plugin system, all of it running inside
|
|
10
|
+
* the one call this project spends its whole design protecting. What is actually
|
|
11
|
+
* needed is RFC 5321's client half of a single transaction — greeting, EHLO,
|
|
12
|
+
* optionally STARTTLS, optionally AUTH, MAIL FROM, RCPT TO, DATA, QUIT — which
|
|
13
|
+
* is this file. `node:net` and `node:tls` and nothing else.
|
|
14
|
+
*
|
|
15
|
+
* ## What it deliberately does not do
|
|
16
|
+
*
|
|
17
|
+
* No connection pooling, no pipelining, no CHUNKING/BDAT, no SMTPUTF8, no DSN,
|
|
18
|
+
* no retry. One transaction per {@link sendMail} call, one message per
|
|
19
|
+
* transaction, and a failure is reported rather than retried: a retry inside an
|
|
20
|
+
* adapter would be a second side effect under one consumed token, and deciding
|
|
21
|
+
* to send again is a decision the gate exists to make.
|
|
22
|
+
*
|
|
23
|
+
* ## The probe, and the limit of what it proves
|
|
24
|
+
*
|
|
25
|
+
* {@link probeSmtp} runs the same session as {@link sendMail} up to and
|
|
26
|
+
* including AUTH, then says QUIT. It is the same code, one call away
|
|
27
|
+
* (`runSession` with no envelope), because a setup check that exercises a
|
|
28
|
+
* different client than the send does is a check that can pass while the send
|
|
29
|
+
* fails.
|
|
30
|
+
*
|
|
31
|
+
* A successful probe proves three things and no more: the host and port accept
|
|
32
|
+
* a connection, the requested transport security (implicit TLS, or the STARTTLS
|
|
33
|
+
* upgrade, with the same no-downgrade rule the send obeys) was actually
|
|
34
|
+
* established, and this server accepts this credential.
|
|
35
|
+
*
|
|
36
|
+
* It does **not** prove that a message would be accepted. MAIL FROM, RCPT TO
|
|
37
|
+
* and DATA are never issued, so nothing about the server's sender policy, its
|
|
38
|
+
* relaying rules, its recipient validation, its size limits or its content
|
|
39
|
+
* filtering is exercised. A probe that succeeds against a server which will
|
|
40
|
+
* later refuse `MAIL FROM:<…>` with a 550 is a probe behaving correctly. Any
|
|
41
|
+
* caller reporting the result to an operator (`setup adapter email` says
|
|
42
|
+
* "verified") owes them that distinction: the transport and the login are
|
|
43
|
+
* verified, the delivery is not.
|
|
44
|
+
*
|
|
45
|
+
* ## Failure vocabulary
|
|
46
|
+
*
|
|
47
|
+
* Everything is reported, nothing is thrown out of {@link sendMail}. Two
|
|
48
|
+
* families:
|
|
49
|
+
*
|
|
50
|
+
* - the transport codes in {@link SMTP_TRANSPORT_FAILURE_CODES}, which are a
|
|
51
|
+
* frozen, additive union (SPEC.md §11.1(6)); and
|
|
52
|
+
* - a reply-code family, `smtp-<NNN>`, minted from the server's own three-digit
|
|
53
|
+
* reply whenever the far side refuses a verb. It is a family rather than an
|
|
54
|
+
* enumeration because RFC 5321 lets a server answer with codes this repository
|
|
55
|
+
* cannot list in advance, and collapsing "mailbox unavailable" and
|
|
56
|
+
* "authentication failed" into one local name would throw away the one piece
|
|
57
|
+
* of information an operator needs. {@link SMTP_REPLY_CODE_PATTERN} pins the
|
|
58
|
+
* shape.
|
|
59
|
+
*
|
|
60
|
+
* ## What may appear in a failure message
|
|
61
|
+
*
|
|
62
|
+
* The verb, the reply code, and **the first line of the server's reply text**.
|
|
63
|
+
* Not the whole reply: a multi-line refusal can run to a screen of banner text,
|
|
64
|
+
* and everything after the first line is the server explaining itself to a human
|
|
65
|
+
* rather than telling the client what happened. Not the command: an `AUTH PLAIN
|
|
66
|
+
* <base64>` echoed into a diagnostic would publish the credential in the one
|
|
67
|
+
* string the adapter's caller is most likely to print.
|
|
68
|
+
*
|
|
69
|
+
* And even the first line is passed through {@link SmtpTransportOptions.redact}
|
|
70
|
+
* before it is returned, because a server is entitled to quote back the username
|
|
71
|
+
* it just refused ("535 5.7.8 authentication failed for bot@example.com"), and a
|
|
72
|
+
* deployment whose username IS the credential would otherwise leak it through a
|
|
73
|
+
* channel the adapter contract's own guard also covers but which this module
|
|
74
|
+
* should not be relying on. Two independent scrubs, by design.
|
|
75
|
+
*
|
|
76
|
+
* Deterministic apart from the network and the clock it does not read: no
|
|
77
|
+
* randomness, no ambient configuration, no environment reads.
|
|
78
|
+
*/
|
|
79
|
+
import { connect as netConnect } from "node:net";
|
|
80
|
+
import { connect as tlsConnect } from "node:tls";
|
|
81
|
+
export function isSmtpSecurity(value) {
|
|
82
|
+
return value === "implicit" || value === "starttls" || value === "none";
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Transport failures this client can report. Frozen union, additive only
|
|
86
|
+
* (SPEC.md §11.1(6)).
|
|
87
|
+
*
|
|
88
|
+
* They are distinguished because they call for four different responses: fix
|
|
89
|
+
* the address, fix the TLS configuration, look at why the far side is slow, and
|
|
90
|
+
* report a server that is not speaking SMTP.
|
|
91
|
+
*/
|
|
92
|
+
export const SMTP_TRANSPORT_FAILURE_CODES = [
|
|
93
|
+
/** The TCP connection could not be established at all. */
|
|
94
|
+
"smtp-connect-failed",
|
|
95
|
+
/** TLS could not be established, or the server would not offer STARTTLS. */
|
|
96
|
+
"smtp-tls-failed",
|
|
97
|
+
/** The session exceeded its whole-transaction budget. */
|
|
98
|
+
"smtp-timeout",
|
|
99
|
+
/** The far side sent something that is not an SMTP reply, or hung up. */
|
|
100
|
+
"smtp-protocol-error",
|
|
101
|
+
];
|
|
102
|
+
/** The shape of a minted reply-code failure: `smtp-` and three digits. */
|
|
103
|
+
export const SMTP_REPLY_CODE_PATTERN = /^smtp-[1-5]\d\d$/u;
|
|
104
|
+
/** The default whole-session budget, in milliseconds. */
|
|
105
|
+
export const DEFAULT_SMTP_TIMEOUT_MS = 30_000;
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Internals
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
const CRLF = "\r\n";
|
|
110
|
+
/** An internal control-flow carrier. Never escapes {@link sendMail}. */
|
|
111
|
+
class SmtpError extends Error {
|
|
112
|
+
code;
|
|
113
|
+
constructor(code, message) {
|
|
114
|
+
super(message);
|
|
115
|
+
this.code = code;
|
|
116
|
+
this.name = "SmtpError";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The reply reader.
|
|
121
|
+
*
|
|
122
|
+
* SMTP framing is line-oriented: a reply is one or more lines, each opening
|
|
123
|
+
* with the same three-digit code, continuation lines separated from the last by
|
|
124
|
+
* a hyphen (`250-STARTTLS`) rather than a space (`250 HELP`). Bytes are
|
|
125
|
+
* accumulated as a Buffer and decoded per complete line, so a multi-byte UTF-8
|
|
126
|
+
* character split across two TCP segments cannot become a replacement character
|
|
127
|
+
* in a diagnostic.
|
|
128
|
+
*/
|
|
129
|
+
class ReplyReader {
|
|
130
|
+
raw = Buffer.alloc(0);
|
|
131
|
+
lines = [];
|
|
132
|
+
ready = [];
|
|
133
|
+
waiting = null;
|
|
134
|
+
failure = null;
|
|
135
|
+
/** Feed bytes. Called from the socket's `data` handler. */
|
|
136
|
+
push(chunk) {
|
|
137
|
+
this.raw = this.raw.length === 0 ? chunk : Buffer.concat([this.raw, chunk]);
|
|
138
|
+
for (;;) {
|
|
139
|
+
const index = this.raw.indexOf(0x0a);
|
|
140
|
+
if (index === -1)
|
|
141
|
+
break;
|
|
142
|
+
const line = this.raw.subarray(0, index).toString("utf8").replace(/\r$/u, "");
|
|
143
|
+
this.raw = this.raw.subarray(index + 1);
|
|
144
|
+
this.consume(line);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Bytes received but not yet forming a line. Non-empty is a violation. */
|
|
148
|
+
get pendingBytes() {
|
|
149
|
+
return this.raw.length;
|
|
150
|
+
}
|
|
151
|
+
/** Replies parsed but not yet awaited. Non-empty before STARTTLS is fatal. */
|
|
152
|
+
get pendingReplies() {
|
|
153
|
+
return this.ready.length;
|
|
154
|
+
}
|
|
155
|
+
consume(line) {
|
|
156
|
+
const match = /^(\d{3})([ -]?)(.*)$/u.exec(line);
|
|
157
|
+
if (match === null) {
|
|
158
|
+
this.fail(new SmtpError("smtp-protocol-error", `the server sent a line that is not an SMTP reply (${JSON.stringify(line.slice(0, 120))})`));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const code = Number(match[1]);
|
|
162
|
+
const last = match[2] !== "-";
|
|
163
|
+
this.lines.push({ code, text: match[3] ?? "", last });
|
|
164
|
+
if (!last)
|
|
165
|
+
return;
|
|
166
|
+
const texts = this.lines.map((entry) => entry.text);
|
|
167
|
+
const reply = { code, lines: texts, first: texts[0] ?? "" };
|
|
168
|
+
this.lines = [];
|
|
169
|
+
const waiter = this.waiting;
|
|
170
|
+
if (waiter === null)
|
|
171
|
+
this.ready.push(reply);
|
|
172
|
+
else {
|
|
173
|
+
this.waiting = null;
|
|
174
|
+
waiter.resolve(reply);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** Terminate every pending and future read with `error`. Idempotent. */
|
|
178
|
+
fail(error) {
|
|
179
|
+
if (this.failure === null)
|
|
180
|
+
this.failure = error;
|
|
181
|
+
const waiter = this.waiting;
|
|
182
|
+
if (waiter !== null) {
|
|
183
|
+
this.waiting = null;
|
|
184
|
+
waiter.reject(this.failure);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/** The next complete reply. Rejects with the session's failure, if any. */
|
|
188
|
+
next() {
|
|
189
|
+
const queued = this.ready.shift();
|
|
190
|
+
if (queued !== undefined)
|
|
191
|
+
return Promise.resolve(queued);
|
|
192
|
+
if (this.failure !== null)
|
|
193
|
+
return Promise.reject(this.failure);
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
this.waiting = { resolve, reject };
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** Is `code` a success for this verb? RFC 5321: 2yz, plus 3yz where expected. */
|
|
200
|
+
function expected(reply, codes) {
|
|
201
|
+
return codes.includes(reply.code) || codes.includes(Math.floor(reply.code / 100));
|
|
202
|
+
}
|
|
203
|
+
function replyFailure(verb, reply, redact) {
|
|
204
|
+
return new SmtpError(`smtp-${reply.code}`,
|
|
205
|
+
// The whole vocabulary of a reply-code failure, and its whole content: the
|
|
206
|
+
// verb, the code, the FIRST line of the reply, redacted. See the header.
|
|
207
|
+
`${verb} refused: ${String(reply.code)} ${redact(reply.first)}`);
|
|
208
|
+
}
|
|
209
|
+
/** `\0user\0password`, base64. RFC 4616 over RFC 4954. */
|
|
210
|
+
function plainToken(user, password) {
|
|
211
|
+
return Buffer.from(`\u0000${user}\u0000${password}`, "utf8").toString("base64");
|
|
212
|
+
}
|
|
213
|
+
/** The mechanisms the server advertised on its EHLO `AUTH` line, uppercased. */
|
|
214
|
+
function authMechanisms(capabilities) {
|
|
215
|
+
for (const line of capabilities) {
|
|
216
|
+
const match = /^AUTH(?:[ =])(.*)$/iu.exec(line.trim());
|
|
217
|
+
if (match === null)
|
|
218
|
+
continue;
|
|
219
|
+
return (match[1] ?? "")
|
|
220
|
+
.split(/\s+/u)
|
|
221
|
+
.filter((entry) => entry.length > 0)
|
|
222
|
+
.map((entry) => entry.toUpperCase());
|
|
223
|
+
}
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
function advertises(capabilities, keyword) {
|
|
227
|
+
return capabilities.some((line) => line.trim().toUpperCase().split(/\s+/u)[0] === keyword);
|
|
228
|
+
}
|
|
229
|
+
async function runSession(options, envelope, message) {
|
|
230
|
+
const redact = options.redact ?? ((text) => text);
|
|
231
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_SMTP_TIMEOUT_MS;
|
|
232
|
+
const clientName = options.clientName ?? "approval.md";
|
|
233
|
+
const rejectUnauthorized = options.tlsRejectUnauthorized ?? true;
|
|
234
|
+
const transcript = [];
|
|
235
|
+
const reader = new ReplyReader();
|
|
236
|
+
let socket = null;
|
|
237
|
+
let secure = options.security === "implicit";
|
|
238
|
+
let authenticated = null;
|
|
239
|
+
// The one timer. Firing it poisons the reader, so whichever await is in
|
|
240
|
+
// flight rejects with the timeout rather than hanging until the socket does.
|
|
241
|
+
let expired = false;
|
|
242
|
+
const deadline = setTimeout(() => {
|
|
243
|
+
expired = true;
|
|
244
|
+
reader.fail(new SmtpError("smtp-timeout", `the SMTP session exceeded its ${String(timeoutMs)}ms budget; the message may or may not have been accepted, so the execution is recorded as failed and a human decides whether to send again`));
|
|
245
|
+
socket?.destroy();
|
|
246
|
+
}, timeoutMs);
|
|
247
|
+
// A timer must never hold the process open on its own.
|
|
248
|
+
deadline.unref?.();
|
|
249
|
+
/** Attach the reader to `next`, replacing any previous attachment. */
|
|
250
|
+
const attach = (next) => {
|
|
251
|
+
socket = next;
|
|
252
|
+
next.on("data", (chunk) => reader.push(chunk));
|
|
253
|
+
next.on("error", (cause) => {
|
|
254
|
+
reader.fail(expired
|
|
255
|
+
? new SmtpError("smtp-timeout", "the SMTP session timed out")
|
|
256
|
+
: new SmtpError("smtp-protocol-error", `the connection failed: ${redact(cause.message)}`));
|
|
257
|
+
});
|
|
258
|
+
next.on("close", () => {
|
|
259
|
+
reader.fail(new SmtpError("smtp-protocol-error", "the server closed the connection before the transaction finished"));
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
/** Detach every listener, so a TLS wrapper can take the socket over. */
|
|
263
|
+
const detach = (from) => {
|
|
264
|
+
from.removeAllListeners("data");
|
|
265
|
+
from.removeAllListeners("error");
|
|
266
|
+
from.removeAllListeners("close");
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* Detach, then swallow. A socket this function has finished with can still
|
|
270
|
+
* emit `ECONNRESET` when the far side goes away, and an unhandled `error` on
|
|
271
|
+
* a socket is an uncaught exception that would take the process down — which
|
|
272
|
+
* for an adapter means a crash *after* a message was already delivered.
|
|
273
|
+
*/
|
|
274
|
+
const abandon = (from) => {
|
|
275
|
+
detach(from);
|
|
276
|
+
from.on("error", () => {
|
|
277
|
+
/* the transaction is over; there is nothing left to report */
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
const write = (line) => {
|
|
281
|
+
socket?.write(`${line}${CRLF}`);
|
|
282
|
+
};
|
|
283
|
+
/** Send `line`, read one reply, and insist it is in `codes`. */
|
|
284
|
+
const say = async (verb, line, codes) => {
|
|
285
|
+
if (line !== null)
|
|
286
|
+
write(line);
|
|
287
|
+
const reply = await reader.next();
|
|
288
|
+
transcript.push(`${verb} ${String(reply.code)}`);
|
|
289
|
+
if (!expected(reply, codes))
|
|
290
|
+
throw replyFailure(verb, reply, redact);
|
|
291
|
+
return reply;
|
|
292
|
+
};
|
|
293
|
+
try {
|
|
294
|
+
// (1) The connection.
|
|
295
|
+
socket = await new Promise((resolve, reject) => {
|
|
296
|
+
const onError = (cause) => {
|
|
297
|
+
reject(new SmtpError(options.security === "implicit" ? "smtp-tls-failed" : "smtp-connect-failed",
|
|
298
|
+
// The address is NOT interpolated: it came from the vault, and
|
|
299
|
+
// every value the vault handed over is redacted out of anything
|
|
300
|
+
// this adapter returns (see the email adapter's header). Naming the
|
|
301
|
+
// CREDENTIAL leaves the operator a diagnostic that survives the
|
|
302
|
+
// scrub and points at the thing to fix.
|
|
303
|
+
`could not connect to the SMTP host named by smtp.host, on the port named by smtp.port: ${redact(cause.message)}`));
|
|
304
|
+
};
|
|
305
|
+
if (options.security === "implicit") {
|
|
306
|
+
const tlsOptions = {
|
|
307
|
+
host: options.host,
|
|
308
|
+
port: options.port,
|
|
309
|
+
rejectUnauthorized,
|
|
310
|
+
servername: options.host,
|
|
311
|
+
};
|
|
312
|
+
const created = tlsConnect(tlsOptions, () => {
|
|
313
|
+
created.removeListener("error", onError);
|
|
314
|
+
resolve(created);
|
|
315
|
+
});
|
|
316
|
+
created.once("error", onError);
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
const created = netConnect({ host: options.host, port: options.port }, () => {
|
|
320
|
+
created.removeListener("error", onError);
|
|
321
|
+
resolve(created);
|
|
322
|
+
});
|
|
323
|
+
created.once("error", onError);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
attach(socket);
|
|
327
|
+
// (2) The greeting, then EHLO.
|
|
328
|
+
await say("greeting", null, [220]);
|
|
329
|
+
let ehlo = await say("EHLO", `EHLO ${clientName}`, [250]);
|
|
330
|
+
let capabilities = ehlo.lines.slice(1);
|
|
331
|
+
// (3) The STARTTLS upgrade, when the caller asked for one. A server that
|
|
332
|
+
// does not offer it is a failure and never a silent downgrade: the
|
|
333
|
+
// caller asked for an encrypted session and would otherwise get a
|
|
334
|
+
// plaintext one carrying a password.
|
|
335
|
+
if (options.security === "starttls") {
|
|
336
|
+
if (!advertises(capabilities, "STARTTLS")) {
|
|
337
|
+
throw new SmtpError("smtp-tls-failed", "the server named by smtp.host does not advertise STARTTLS, and this adapter will not silently downgrade to a plaintext session carrying a password");
|
|
338
|
+
}
|
|
339
|
+
await say("STARTTLS", "STARTTLS", [220]);
|
|
340
|
+
// Anything already buffered here was injected by something that is not
|
|
341
|
+
// the server's TLS layer, and accepting it would be the classic STARTTLS
|
|
342
|
+
// command-injection hole. Refuse, loudly.
|
|
343
|
+
if (reader.pendingBytes > 0 || reader.pendingReplies > 0) {
|
|
344
|
+
throw new SmtpError("smtp-protocol-error", "bytes arrived after the STARTTLS reply and before the handshake; they were injected by something that is not the TLS session, and the transaction was abandoned");
|
|
345
|
+
}
|
|
346
|
+
const plain = socket;
|
|
347
|
+
detach(plain);
|
|
348
|
+
socket = await new Promise((resolve, reject) => {
|
|
349
|
+
const onError = (cause) => {
|
|
350
|
+
reject(new SmtpError("smtp-tls-failed", `the STARTTLS handshake failed: ${redact(cause.message)}`));
|
|
351
|
+
};
|
|
352
|
+
const upgraded = tlsConnect({ socket: plain, rejectUnauthorized, servername: options.host }, () => {
|
|
353
|
+
upgraded.removeListener("error", onError);
|
|
354
|
+
resolve(upgraded);
|
|
355
|
+
});
|
|
356
|
+
upgraded.once("error", onError);
|
|
357
|
+
});
|
|
358
|
+
attach(socket);
|
|
359
|
+
secure = true;
|
|
360
|
+
// RFC 3207: the client MUST discard everything it learned before the
|
|
361
|
+
// upgrade and re-issue EHLO. A capability list from the plaintext phase
|
|
362
|
+
// is a capability list an attacker could have written.
|
|
363
|
+
ehlo = await say("EHLO", `EHLO ${clientName}`, [250]);
|
|
364
|
+
capabilities = ehlo.lines.slice(1);
|
|
365
|
+
}
|
|
366
|
+
// (4) AUTH, when a credential was supplied. PLAIN first, LOGIN as the
|
|
367
|
+
// fallback, because some servers advertise both and accept only one.
|
|
368
|
+
const user = options.user ?? "";
|
|
369
|
+
const password = options.password ?? "";
|
|
370
|
+
if (user.length > 0 && password.length > 0) {
|
|
371
|
+
if (!secure) {
|
|
372
|
+
throw new SmtpError("smtp-tls-failed", 'a credential was supplied for a session with security "none"; this adapter will not send a password over a cleartext socket. Use "starttls" or "implicit", or store no smtp.user/smtp.password for a local relay that needs none');
|
|
373
|
+
}
|
|
374
|
+
const mechanisms = authMechanisms(capabilities);
|
|
375
|
+
const allow = (name) => mechanisms.length === 0 || mechanisms.includes(name);
|
|
376
|
+
let authError = null;
|
|
377
|
+
if (allow("PLAIN")) {
|
|
378
|
+
try {
|
|
379
|
+
await say("AUTH", `AUTH PLAIN ${plainToken(user, password)}`, [235]);
|
|
380
|
+
authenticated = "PLAIN";
|
|
381
|
+
}
|
|
382
|
+
catch (cause) {
|
|
383
|
+
if (!(cause instanceof SmtpError))
|
|
384
|
+
throw cause;
|
|
385
|
+
// Only a REFUSAL is worth a second mechanism. A timeout or a dropped
|
|
386
|
+
// socket has nothing left to fall back onto.
|
|
387
|
+
if (!SMTP_REPLY_CODE_PATTERN.test(cause.code))
|
|
388
|
+
throw cause;
|
|
389
|
+
authError = cause;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (authenticated === null && allow("LOGIN")) {
|
|
393
|
+
await say("AUTH", "AUTH LOGIN", [334]);
|
|
394
|
+
await say("AUTH", Buffer.from(user, "utf8").toString("base64"), [334]);
|
|
395
|
+
await say("AUTH", Buffer.from(password, "utf8").toString("base64"), [235]);
|
|
396
|
+
authenticated = "LOGIN";
|
|
397
|
+
}
|
|
398
|
+
if (authenticated === null) {
|
|
399
|
+
throw (authError ??
|
|
400
|
+
new SmtpError("smtp-protocol-error", `a credential was supplied but the server advertises no mechanism this adapter speaks (it offers ${mechanisms.length === 0 ? "none" : mechanisms.join(", ")}; PLAIN and LOGIN are implemented)`));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
// (5) The envelope. Bcc recipients are here and in no header.
|
|
404
|
+
// A probe stops before this line: MAIL FROM is the first verb that
|
|
405
|
+
// tells the server a message is coming, and a probe has nothing to say.
|
|
406
|
+
let accepted = null;
|
|
407
|
+
if (envelope !== null && message !== null) {
|
|
408
|
+
await say("MAIL FROM", `MAIL FROM:<${envelope.from}>`, [250]);
|
|
409
|
+
for (const recipient of envelope.recipients) {
|
|
410
|
+
await say("RCPT TO", `RCPT TO:<${recipient}>`, [250, 251]);
|
|
411
|
+
}
|
|
412
|
+
// (6) The message. Dot-stuffing and the terminator are transport framing.
|
|
413
|
+
await say("DATA", "DATA", [354]);
|
|
414
|
+
socket.write(dotStuff(message));
|
|
415
|
+
accepted = await say("message", ".", [250]);
|
|
416
|
+
}
|
|
417
|
+
// (7) QUIT is courtesy: the message is already accepted, and a server that
|
|
418
|
+
// mishandles the goodbye has not unsent it. Failures here are ignored
|
|
419
|
+
// on purpose — reporting one would turn a delivered message into a
|
|
420
|
+
// failed execution, which is the worst lie this adapter could tell.
|
|
421
|
+
// A probe has nothing to lose here either; it has already learned
|
|
422
|
+
// everything the session can tell it.
|
|
423
|
+
clearTimeout(deadline);
|
|
424
|
+
abandon(socket);
|
|
425
|
+
// `end`, not `write` then `destroy`: a destroyed TLS socket discards the
|
|
426
|
+
// bytes still in its write buffer, and the far side would see the session
|
|
427
|
+
// vanish rather than end. The message is already accepted either way, so
|
|
428
|
+
// nothing here can turn a delivered message into a failed execution.
|
|
429
|
+
try {
|
|
430
|
+
socket.end(`QUIT${CRLF}`);
|
|
431
|
+
}
|
|
432
|
+
catch {
|
|
433
|
+
/* already delivered */
|
|
434
|
+
}
|
|
435
|
+
if (accepted === null)
|
|
436
|
+
return { ok: true, transcript, secure, authenticated };
|
|
437
|
+
return {
|
|
438
|
+
ok: true,
|
|
439
|
+
reply: { code: accepted.code, text: redact(accepted.first) },
|
|
440
|
+
transcript,
|
|
441
|
+
secure,
|
|
442
|
+
authenticated,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
catch (cause) {
|
|
446
|
+
clearTimeout(deadline);
|
|
447
|
+
if (socket !== null) {
|
|
448
|
+
abandon(socket);
|
|
449
|
+
socket.destroy();
|
|
450
|
+
}
|
|
451
|
+
if (cause instanceof SmtpError) {
|
|
452
|
+
return { ok: false, code: cause.code, message: cause.message, transcript, secure };
|
|
453
|
+
}
|
|
454
|
+
return {
|
|
455
|
+
ok: false,
|
|
456
|
+
code: "smtp-protocol-error",
|
|
457
|
+
message: redact(cause instanceof Error ? cause.message : String(cause)),
|
|
458
|
+
transcript,
|
|
459
|
+
secure,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Run one SMTP transaction and report how it went. Never throws.
|
|
465
|
+
*
|
|
466
|
+
* The whole of it is {@link runSession}; this is the entry point that supplies
|
|
467
|
+
* an envelope and a message, and its result is unchanged from the day it was
|
|
468
|
+
* the whole function.
|
|
469
|
+
*/
|
|
470
|
+
export async function sendMail(options, envelope, message) {
|
|
471
|
+
return runSession(options, envelope, message);
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Open a session, authenticate, send nothing, and report. Never throws.
|
|
475
|
+
*
|
|
476
|
+
* Exactly {@link sendMail}'s session up to AUTH — the same connection, the same
|
|
477
|
+
* STARTTLS rules including the no-downgrade refusal and the response-injection
|
|
478
|
+
* guard, the same refusal to put a password on a cleartext socket, the same
|
|
479
|
+
* one-session budget, the same redaction of every string it returns — and then
|
|
480
|
+
* QUIT. See this module's header for what a success does and does not prove:
|
|
481
|
+
* transport, TLS mode and credential, never that a message would be delivered.
|
|
482
|
+
*/
|
|
483
|
+
export async function probeSmtp(options) {
|
|
484
|
+
return runSession(options, null, null);
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* RFC 5321 §4.5.2: a line of the message that begins with `.` gets a second
|
|
488
|
+
* one, so the terminator cannot be forged by the message's own content. The
|
|
489
|
+
* terminating `.` line is NOT added here; {@link sendMail} sends it as its own
|
|
490
|
+
* command so the transcript records the reply to it.
|
|
491
|
+
*/
|
|
492
|
+
export function dotStuff(message) {
|
|
493
|
+
const body = message
|
|
494
|
+
.split(CRLF)
|
|
495
|
+
.map((line) => (line.startsWith(".") ? `.${line}` : line))
|
|
496
|
+
.join(CRLF);
|
|
497
|
+
return body.endsWith(CRLF) ? body : `${body}${CRLF}`;
|
|
498
|
+
}
|
|
499
|
+
//# sourceMappingURL=smtp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smtp.js","sourceRoot":"","sources":["../../../src/adapters/smtp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vault as a {@link CredentialProvider} (SPEC.md §10.4; APRV-68).
|
|
3
|
+
*
|
|
4
|
+
* `adapters/contract.ts` left one seam open and named the task that would fill
|
|
5
|
+
* it: "No vault. {@link CredentialProvider} is the seam a real vault implements
|
|
6
|
+
* (APRV-68)." This module is that implementation, and it is deliberately thin.
|
|
7
|
+
* Everything about *when* a credential may be read belongs to the contract;
|
|
8
|
+
* everything about *how* the bytes are stored belongs to `core/vault.ts`. What
|
|
9
|
+
* is left here is the translation between the two vocabularies.
|
|
10
|
+
*
|
|
11
|
+
* ## The structural rule
|
|
12
|
+
*
|
|
13
|
+
* A provider built here is only ever handed to
|
|
14
|
+
* {@link executeThroughAdapter} through {@link AdapterExecuteOptions.credentials}.
|
|
15
|
+
* The contract wraps it in a window that closes the instant `act` returns, so
|
|
16
|
+
* every read during `act` is inside a verified execution. Manual and selected
|
|
17
|
+
* live paths consume a token; explicitly authorized supervised or autonomous
|
|
18
|
+
* paths do not. Three things hold that rule up, and none is a convention:
|
|
19
|
+
*
|
|
20
|
+
* 1. `core/vault.ts` exports exactly one function that returns a credential
|
|
21
|
+
* value (`getCredential`), and this module is its only caller in the
|
|
22
|
+
* repository. `tests/vault.test.ts` pins both halves.
|
|
23
|
+
* 2. There is no CLI verb that prints a value. `approval vault` can set, list
|
|
24
|
+
* (names), and remove; it cannot show. See `src/cli/vault.ts`.
|
|
25
|
+
* 3. The contract scans everything an adapter returns for the values the
|
|
26
|
+
* provider handed out and redacts them, so a leak by a careless adapter is
|
|
27
|
+
* caught mechanically rather than reviewed for.
|
|
28
|
+
*
|
|
29
|
+
* ## What this defends, and what it does not
|
|
30
|
+
*
|
|
31
|
+
* Exactly what the vault module's own threat model says, restated because a
|
|
32
|
+
* reader arriving from the adapter side deserves it here too. **Defended:**
|
|
33
|
+
* credentials at rest, and casual reads by an agent that can read files in the
|
|
34
|
+
* working tree — the ciphertext hides the names as well as the values.
|
|
35
|
+
* **Not defended (SPEC.md §11, plainly):** a compromised host, and an agent that
|
|
36
|
+
* can read the passphrase environment variable. Such an agent does not need this
|
|
37
|
+
* provider; it can decrypt the file directly. The vault raises the cost of a
|
|
38
|
+
* leak from reading a file to owning the session, and claims nothing beyond
|
|
39
|
+
* that.
|
|
40
|
+
*
|
|
41
|
+
* Total and synchronous, as {@link CredentialProvider} requires: nothing here
|
|
42
|
+
* throws, nothing blocks on a human, and every failure is one of the three
|
|
43
|
+
* {@link CREDENTIAL_REFUSAL_CODES}.
|
|
44
|
+
*/
|
|
45
|
+
import { type SourceRunner } from "../core/env-file.js";
|
|
46
|
+
import type { CredentialProvider } from "./contract.js";
|
|
47
|
+
/**
|
|
48
|
+
* How to reach the vault. Either the vault file directly, or the log path the
|
|
49
|
+
* convention derives it from — never both, so there is one answer to "which
|
|
50
|
+
* file".
|
|
51
|
+
*/
|
|
52
|
+
export type VaultLocation = {
|
|
53
|
+
vaultPath: string;
|
|
54
|
+
} | {
|
|
55
|
+
logPath: string;
|
|
56
|
+
};
|
|
57
|
+
export interface VaultProviderOptions {
|
|
58
|
+
/**
|
|
59
|
+
* The NAME of the environment variable holding the passphrase. Callers get
|
|
60
|
+
* this from `passphraseEnvFor(loadPolicy(...))`, so the policy's declaration
|
|
61
|
+
* is honoured and the default applies when it declares nothing.
|
|
62
|
+
*/
|
|
63
|
+
passphraseEnv: string;
|
|
64
|
+
/** Injectable for tests. Defaults to this process's environment. */
|
|
65
|
+
env?: NodeJS.ProcessEnv;
|
|
66
|
+
/**
|
|
67
|
+
* The instance's `.approval/env`, enabling the scoped passphrase fallback
|
|
68
|
+
* (APRV-168). Absent, and the provider behaves exactly as it always did:
|
|
69
|
+
* the passphrase comes from the environment or it comes from nowhere.
|
|
70
|
+
*
|
|
71
|
+
* Supplied by `approval adapter <name>`, which executes through the contract
|
|
72
|
+
* and therefore inside a consumed-token window. It is deliberately NOT
|
|
73
|
+
* supplied by `approval setup adapter <name>`, `approval vault`, or anything
|
|
74
|
+
* else: those hold no token, so the fallback would have no authority behind
|
|
75
|
+
* it. See `adapters/env-passphrase.ts` for the whole argument.
|
|
76
|
+
*/
|
|
77
|
+
envFilePath?: string;
|
|
78
|
+
/** The keychain / secret-service seam, injectable exactly as it is there. */
|
|
79
|
+
sourceRunner?: SourceRunner;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A provider that answers from the encrypted vault.
|
|
83
|
+
*
|
|
84
|
+
* **Lazy, and cached for the life of the provider.** The vault is not opened
|
|
85
|
+
* until an adapter actually asks for something, so an execution that needs no
|
|
86
|
+
* credential pays no scrypt cost and touches no ciphertext. Once opened, the
|
|
87
|
+
* derived value for a name is remembered, because a provider's life *is* one
|
|
88
|
+
* `act` call: the contract closes it when `act` returns, so the cache cannot
|
|
89
|
+
* outlive the token window it was built for, and the alternative — a fresh
|
|
90
|
+
* ~100 ms key derivation per credential — would put a visible tax on an adapter
|
|
91
|
+
* that needs two.
|
|
92
|
+
*
|
|
93
|
+
* The passphrase is read from the environment on every open rather than
|
|
94
|
+
* captured at construction, so a provider built before the operator exported the
|
|
95
|
+
* variable is not permanently poisoned.
|
|
96
|
+
*
|
|
97
|
+
* **The scoped fallback (APRV-168).** When {@link VaultProviderOptions.envFilePath}
|
|
98
|
+
* is supplied AND the contract has told this provider it is inside a token
|
|
99
|
+
* window, a passphrase absent from the environment is resolved from the
|
|
100
|
+
* instance's `.approval/env` instead. That is the one narrowing of
|
|
101
|
+
* `core/env-file.ts`'s "no verb reads this file" rule, and the whole argument
|
|
102
|
+
* for it lives in `adapters/env-passphrase.ts`. The short form: the authority is
|
|
103
|
+
* the token, and a human tapped Approve for exactly this action. The value is
|
|
104
|
+
* used to derive one key and reaches no environment, no argv, no log, and no
|
|
105
|
+
* message; the cache is dropped when the window closes, so a passphrase resolved
|
|
106
|
+
* under one grant opens nothing under the next.
|
|
107
|
+
*
|
|
108
|
+
* Messages name the environment VARIABLE and the credential NAME, and never a
|
|
109
|
+
* value: an adapter's failure message is one of the strings the contract hands
|
|
110
|
+
* back to a caller, and a diagnostic that quoted the secret would defeat the
|
|
111
|
+
* redaction guard by putting the secret in the one place the guard cannot know
|
|
112
|
+
* to look for it.
|
|
113
|
+
*/
|
|
114
|
+
export declare function vaultCredentialProvider(location: VaultLocation, options: VaultProviderOptions): CredentialProvider;
|