approval-md 0.1.0 → 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/README.md +584 -553
- package/SPEC.md +42 -13
- 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 +623 -0
- 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 +1832 -0
- package/dist/src/channels/web.d.ts +341 -0
- 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/args.d.ts +43 -0
- package/dist/src/cli/attest.d.ts +41 -0
- 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 +806 -0
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel.d.ts +71 -0
- package/dist/src/cli/checkpoint-tap.d.ts +169 -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/daemon.d.ts +120 -0
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +119 -5
- 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/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 +103 -0
- package/dist/src/cli/help.js +173 -51
- package/dist/src/cli/help.js.map +1 -1
- 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 +186 -80
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.d.ts +35 -0
- 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 +155 -5
- 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.d.ts +43 -0
- package/dist/src/cli/preflight.d.ts +363 -0
- package/dist/src/cli/preflight.js +294 -7
- 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/scaffold.d.ts +79 -0
- 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 +117 -0
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-common.d.ts +275 -0
- 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 +202 -0
- 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 +4 -2
- 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/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 +176 -8
- 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/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/agents-md.d.ts +276 -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 +13 -1
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +492 -0
- package/dist/src/core/budgets.d.ts +238 -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 +543 -0
- package/dist/src/core/command-class.js +43 -8
- package/dist/src/core/command-class.js.map +1 -1
- 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/dark-session.d.ts +331 -0
- package/dist/src/core/decision-refusal.d.ts +185 -0
- package/dist/src/core/env-file.d.ts +450 -0
- package/dist/src/core/execute.d.ts +858 -0
- package/dist/src/core/execute.js +44 -6
- 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 +1364 -0
- package/dist/src/core/gate.js +68 -13
- package/dist/src/core/gate.js.map +1 -1
- 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 +2 -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 +253 -0
- 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 +278 -0
- 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 +150 -0
- package/dist/src/core/policy-explain.js +31 -3
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +527 -0
- package/dist/src/core/policy-load.js +15 -3
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +281 -0
- package/dist/src/core/policy-match.js +20 -9
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +265 -0
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/protected-path-guard.d.ts +453 -0
- package/dist/src/core/protected-path-guard.js +514 -35
- package/dist/src/core/protected-path-guard.js.map +1 -1
- 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 +290 -0
- package/dist/src/core/seal.d.ts +165 -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 +137 -0
- 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 +466 -0
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- 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 +9 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +389 -36
- package/docs/codex-enforced-session.md +30 -0
- package/package.json +12 -2
- package/schema/codex-instance.schema.json +82 -0
- package/schema/event.schema.json +2 -1
- package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
- package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
- package/schema/policy.schema.json +21 -1
- package/templates/codex/README.md +9 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vault passphrase, resolved from `.approval/env` inside a granted window
|
|
3
|
+
* (SPEC.md §10.4, §5.2; APRV-168).
|
|
4
|
+
*
|
|
5
|
+
* ## Why this narrows a load-bearing rule, and how far
|
|
6
|
+
*
|
|
7
|
+
* `core/env-file.ts` states the rule this module bends, and states it as the
|
|
8
|
+
* reason the file exists at all: **no verb in this runtime reads `.approval/env`
|
|
9
|
+
* into its own environment.** The argument there is `APPROVAL_HUMAN`. A
|
|
10
|
+
* working-tree file that could set human identity would move SPEC.md §11's trust
|
|
11
|
+
* boundary from "the local machine" to "anyone who can write a file in the
|
|
12
|
+
* repository", and every agent that can edit source can write a file in the
|
|
13
|
+
* repository. So the file is inert, one verb resolves it, and a human evaluates
|
|
14
|
+
* that verb's output in their own shell.
|
|
15
|
+
*
|
|
16
|
+
* The narrowing here keeps every part of that argument intact:
|
|
17
|
+
*
|
|
18
|
+
* 1. **One variable, named by the policy.** What this module resolves is the
|
|
19
|
+
* `vault.passphrase_env` variable and nothing else. It cannot be asked for
|
|
20
|
+
* `APPROVAL_HUMAN`, so it cannot be used to claim an identity, attest a
|
|
21
|
+
* policy, or grant a request. The whole human-only gate is untouched.
|
|
22
|
+
* 2. **One caller, inside a token window.** Reaching this function requires an
|
|
23
|
+
* {@link ExecutionGrant}, which `adapters/contract.ts` mints and no other
|
|
24
|
+
* module can construct: its brand is a `unique symbol` that is never
|
|
25
|
+
* exported, so a call site outside the contract's execution path does not
|
|
26
|
+
* type-check. A generic vault verb, `approval doctor`, `approval setup` and
|
|
27
|
+
* every other caller of `vaultCredentialProvider` have no way to produce one,
|
|
28
|
+
* which is the "no verb reads `.approval/env`" rule holding everywhere except
|
|
29
|
+
* the one path named here.
|
|
30
|
+
*
|
|
31
|
+
* The contract mints a grant in two phases and both are honoured, for a
|
|
32
|
+
* reason worth stating: the credential resolution APRV-169 moved AHEAD of the
|
|
33
|
+
* token spend runs in the `presented` phase, so a fallback that insisted on
|
|
34
|
+
* `consumed` would refuse the very execution it exists to enable. A
|
|
35
|
+
* `presented` grant is minted only when the caller's token matches the digest
|
|
36
|
+
* the log's `approval.granted` recorded for this action, so it is still proof
|
|
37
|
+
* that a human approved this action and that the caller holds the token that
|
|
38
|
+
* approval minted. What it does not prove is that the token may still be
|
|
39
|
+
* spent (TTL, revocation, single use), and it does not have to: those are
|
|
40
|
+
* checked in `startExecution` before anything is appended, and no side effect
|
|
41
|
+
* happens on the strength of this phase.
|
|
42
|
+
* 3. **The authority is the token, not the file.** A human looked at the
|
|
43
|
+
* payload on their phone, tapped Approve, and a token was minted, delivered,
|
|
44
|
+
* verified and consumed for exactly this action key and exactly these bytes.
|
|
45
|
+
* The question this module answers is narrower than "may this process act":
|
|
46
|
+
* that was already answered, by a person. It is "the action a human approved
|
|
47
|
+
* is about to run, and the credential it needs is described in the
|
|
48
|
+
* instance's own configuration; may the runtime read that description".
|
|
49
|
+
* 4. **Nothing is loaded into an environment.** The value is returned to the
|
|
50
|
+
* vault provider and used to derive one key. It is never written to
|
|
51
|
+
* `process.env`, never placed in an argv (`core/env-file.ts`'s helper
|
|
52
|
+
* lookups already pass a service NAME and take the secret on stdout), never
|
|
53
|
+
* logged, and never put in a message, a refusal, or a thrown error.
|
|
54
|
+
*
|
|
55
|
+
* What the demo case looks like, since it is the one that produced this task:
|
|
56
|
+
* the web-agent runner scrubs the agent child's environment of everything
|
|
57
|
+
* matching `APPROVAL|VAULT|TELEGRAM`, deliberately, because the server itself
|
|
58
|
+
* must hold no vault passphrase. The child then holds a granted token for one
|
|
59
|
+
* approved email and no way to open the vault that token is the key to. Before
|
|
60
|
+
* this module the only answers were to weaken the scrub or to send the mail by
|
|
61
|
+
* hand.
|
|
62
|
+
*
|
|
63
|
+
* ## What it does not defend
|
|
64
|
+
*
|
|
65
|
+
* Exactly what SPEC.md §10.4 and §11 already say the vault does not defend: a
|
|
66
|
+
* compromised host, and an agent that can read the passphrase. An agent that can
|
|
67
|
+
* read `.approval/env` and run the same helper lookups needs no adapter and no
|
|
68
|
+
* token; it decrypts the file directly. This module does not widen that
|
|
69
|
+
* exposure, because it reads what such an agent could already read. What it
|
|
70
|
+
* changes is that a process which holds a HUMAN'S GRANT and no passphrase can
|
|
71
|
+
* complete the action the human approved.
|
|
72
|
+
*
|
|
73
|
+
* Total and synchronous, like everything on the credential path: nothing here
|
|
74
|
+
* throws, and every failure is `null`. A `null` carries no detail on purpose;
|
|
75
|
+
* the caller's own refusal names the variable and the vault, and a diagnostic
|
|
76
|
+
* that quoted a keychain error would be the one string in this system that
|
|
77
|
+
* describes where a passphrase lives.
|
|
78
|
+
*/
|
|
79
|
+
import { type SourceRunner } from "../core/env-file.js";
|
|
80
|
+
import type { ExecutionGrant } from "./contract.js";
|
|
81
|
+
/**
|
|
82
|
+
* Resolve `variable` from the source map at `envFilePath`, under `grant`.
|
|
83
|
+
*
|
|
84
|
+
* Returns the value, or `null` for every other outcome: no grant, a `consumed`
|
|
85
|
+
* grant from a path where no token was spent, no file, a file this runtime will
|
|
86
|
+
* not read (wrong mode, unparseable), no line for this variable, an `env:` line
|
|
87
|
+
* (which asserts the value comes from the shell and resolves to nothing on its
|
|
88
|
+
* own), a helper that is missing or declined, or an empty result.
|
|
89
|
+
*
|
|
90
|
+
* Not re-exported anywhere. `adapters/vault-provider.ts` is its only caller, and
|
|
91
|
+
* `tests/vault-provider.test.ts` pins that.
|
|
92
|
+
*/
|
|
93
|
+
export declare function passphraseUnderGrant(grant: ExecutionGrant | null, envFilePath: string, variable: string, runner?: SourceRunner): string | null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The supported adapter-author API.
|
|
3
|
+
*
|
|
4
|
+
* Keep this barrel deliberately small. Every name exported here is a package
|
|
5
|
+
* compatibility commitment; the surrounding adapter and core modules remain
|
|
6
|
+
* implementation details.
|
|
7
|
+
*/
|
|
8
|
+
export { ADAPTER_REFUSAL_CODES, CREDENTIAL_REFUSAL_CODES, executeThroughAdapter, type ActInput, type ActOutcome, type Adapter, type AdapterExecuteOptions, type AdapterExecuteRequest, type AdapterExecuteResult, type AdapterExecuteSuccess, type AdapterRefusal, type AdapterRefusalCode, type CredentialProvider, type CredentialRefusalCode, type CredentialResult, type ExecutionGrant, type JsonValue, type PrecheckInput, type PrecheckOutcome, } from "./contract.js";
|
|
9
|
+
export { runAdapterConformance, type AdapterConformanceCase, type AdapterConformanceHarness, type ConformanceContext, } from "./conformance.js";
|
|
10
|
+
export { vaultCredentialProvider, type VaultLocation, type VaultProviderOptions, } from "./vault-provider.js";
|
|
11
|
+
export type { CredentialKind, CredentialSpec } from "../core/credential-spec.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The supported adapter-author API.
|
|
3
|
+
*
|
|
4
|
+
* Keep this barrel deliberately small. Every name exported here is a package
|
|
5
|
+
* compatibility commitment; the surrounding adapter and core modules remain
|
|
6
|
+
* implementation details.
|
|
7
|
+
*/
|
|
8
|
+
export { ADAPTER_REFUSAL_CODES, CREDENTIAL_REFUSAL_CODES, executeThroughAdapter, } from "./contract.js";
|
|
9
|
+
export { runAdapterConformance, } from "./conformance.js";
|
|
10
|
+
export { vaultCredentialProvider, } from "./vault-provider.js";
|
|
11
|
+
//# sourceMappingURL=public.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.js","sourceRoot":"","sources":["../../../src/adapters/public.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,GAiBtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,GAItB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,uBAAuB,GAGxB,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The built-in adapters, keyed by the classes they serve (APRV-205).
|
|
3
|
+
*
|
|
4
|
+
* One question is asked of this module and one only: **which credential names
|
|
5
|
+
* did the adapters for this class declare they cannot act without?** `approval
|
|
6
|
+
* run` asks it before it spawns, because those names are the ones the scrub in
|
|
7
|
+
* `core/child-env.ts` lets through into the child's environment; everything
|
|
8
|
+
* else under the credential-bearing prefixes is withheld.
|
|
9
|
+
*
|
|
10
|
+
* The declaration is the adapter's own, static, and reached through no flag.
|
|
11
|
+
* That is the point: a caller-supplied list of variables to keep would be a
|
|
12
|
+
* caller-supplied way to get the token back, which is the hole this task
|
|
13
|
+
* closes rather than a feature of the fix. {@link declaredCredentialsForClass}
|
|
14
|
+
* therefore takes a class and nothing else, and reads {@link builtInAdapters}
|
|
15
|
+
* and nothing else.
|
|
16
|
+
*
|
|
17
|
+
* The list is deliberately tiny and deliberately here rather than in
|
|
18
|
+
* `src/core/`: core does not know about adapters and must not learn, so the
|
|
19
|
+
* lookup lives beside the adapters and the CLI does the joining.
|
|
20
|
+
*
|
|
21
|
+
* This roster is the build's answer to "which adapters exist". The CLI's own
|
|
22
|
+
* adapter table (`cli/adapter.ts`) answers a different question, "which
|
|
23
|
+
* adapters have a verb", and the two are kept side by side on purpose: an
|
|
24
|
+
* adapter with no CLI face still declares credentials, and this module must
|
|
25
|
+
* not depend on the CLI to know that.
|
|
26
|
+
*/
|
|
27
|
+
import type { Adapter } from "./contract.js";
|
|
28
|
+
/**
|
|
29
|
+
* Every adapter this build ships. Since APRV-223 that is two, and both serve
|
|
30
|
+
* `communicate.email.external`: the union of their declared names is what the
|
|
31
|
+
* scrub lets through, which is the honest superset {@link
|
|
32
|
+
* unionRequiredCredentials} was written for rather than a collision to resolve.
|
|
33
|
+
*
|
|
34
|
+
* Constructed with defaults: the question asked
|
|
35
|
+
* here is about DECLARED names, and no default answers it differently from a
|
|
36
|
+
* configured instance except by renaming vault entries, which is a deployment's
|
|
37
|
+
* own business and not a reason to open a socket at lookup time.
|
|
38
|
+
*/
|
|
39
|
+
export declare function builtInAdapters(): readonly Adapter[];
|
|
40
|
+
/**
|
|
41
|
+
* The credential names declared by `adapters` serving `cls`, deduplicated and
|
|
42
|
+
* in roster order.
|
|
43
|
+
*
|
|
44
|
+
* Every adapter that serves the class contributes, because "which adapter would
|
|
45
|
+
* have run this" is a question with no answer at this point in the flow, and
|
|
46
|
+
* the union of two adapters' declarations is the honest superset. Two adapters
|
|
47
|
+
* may serve one class, and when they do the scrub lets both sets of names
|
|
48
|
+
* through rather than guessing between them.
|
|
49
|
+
*
|
|
50
|
+
* Pure, and separated from {@link declaredCredentialsForClass} so the union can
|
|
51
|
+
* be tested over more adapters than a build happens to ship. It is not a seam
|
|
52
|
+
* for enforcement: nothing outside a test passes it a roster.
|
|
53
|
+
*/
|
|
54
|
+
export declare function unionRequiredCredentials(adapters: readonly Adapter[], cls: string): readonly string[];
|
|
55
|
+
/**
|
|
56
|
+
* The credential names declared by the built-in adapters serving `cls`, or an
|
|
57
|
+
* empty list when no adapter serves it.
|
|
58
|
+
*/
|
|
59
|
+
export declare function declaredCredentialsForClass(cls: string): readonly string[];
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
import { agentmailAdapter } from "./agentmail.js";
|
|
28
28
|
import { emailAdapter } from "./email.js";
|
|
29
|
+
import { zzzAdapter } from "./zzz.js";
|
|
29
30
|
/**
|
|
30
31
|
* Every adapter this build ships. Since APRV-223 that is two, and both serve
|
|
31
32
|
* `communicate.email.external`: the union of their declared names is what the
|
|
@@ -38,7 +39,7 @@ import { emailAdapter } from "./email.js";
|
|
|
38
39
|
* own business and not a reason to open a socket at lookup time.
|
|
39
40
|
*/
|
|
40
41
|
export function builtInAdapters() {
|
|
41
|
-
return [emailAdapter(), agentmailAdapter()];
|
|
42
|
+
return [emailAdapter(), agentmailAdapter(), zzzAdapter()];
|
|
42
43
|
}
|
|
43
44
|
/**
|
|
44
45
|
* The credential names declared by `adapters` serving `cls`, deduplicated and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/adapters/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/adapters/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,YAAY,EAAE,EAAE,gBAAgB,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,wBAAwB,CACtC,QAA4B,EAC5B,GAAW;IAEX,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAS;QAC7C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,mBAAmB,IAAI,EAAE,EAAE,CAAC;YACrD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,GAAW;IACrD,OAAO,wBAAwB,CAAC,eAAe,EAAE,EAAE,GAAG,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,213 @@
|
|
|
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
|
+
/** How the connection is protected. */
|
|
80
|
+
export type SmtpSecurity =
|
|
81
|
+
/** TLS from the first byte (the submissions port, 465). */
|
|
82
|
+
"implicit"
|
|
83
|
+
/** Plaintext, then a mandatory STARTTLS upgrade (the submission port, 587). */
|
|
84
|
+
| "starttls"
|
|
85
|
+
/**
|
|
86
|
+
* Plaintext, and it stays that way. Only sane for a relay on the same host;
|
|
87
|
+
* the adapter refuses to AUTH over it, because a password on a cleartext
|
|
88
|
+
* socket is a password on the wire.
|
|
89
|
+
*/
|
|
90
|
+
| "none";
|
|
91
|
+
export declare function isSmtpSecurity(value: unknown): value is SmtpSecurity;
|
|
92
|
+
/**
|
|
93
|
+
* Transport failures this client can report. Frozen union, additive only
|
|
94
|
+
* (SPEC.md §11.1(6)).
|
|
95
|
+
*
|
|
96
|
+
* They are distinguished because they call for four different responses: fix
|
|
97
|
+
* the address, fix the TLS configuration, look at why the far side is slow, and
|
|
98
|
+
* report a server that is not speaking SMTP.
|
|
99
|
+
*/
|
|
100
|
+
export declare const SMTP_TRANSPORT_FAILURE_CODES: readonly [
|
|
101
|
+
/** The TCP connection could not be established at all. */
|
|
102
|
+
"smtp-connect-failed",
|
|
103
|
+
/** TLS could not be established, or the server would not offer STARTTLS. */
|
|
104
|
+
"smtp-tls-failed",
|
|
105
|
+
/** The session exceeded its whole-transaction budget. */
|
|
106
|
+
"smtp-timeout",
|
|
107
|
+
/** The far side sent something that is not an SMTP reply, or hung up. */
|
|
108
|
+
"smtp-protocol-error"];
|
|
109
|
+
export type SmtpTransportFailureCode = (typeof SMTP_TRANSPORT_FAILURE_CODES)[number];
|
|
110
|
+
/** The shape of a minted reply-code failure: `smtp-` and three digits. */
|
|
111
|
+
export declare const SMTP_REPLY_CODE_PATTERN: RegExp;
|
|
112
|
+
/** The default whole-session budget, in milliseconds. */
|
|
113
|
+
export declare const DEFAULT_SMTP_TIMEOUT_MS = 30000;
|
|
114
|
+
/** A parsed SMTP reply. `lines` holds the text of each line, code stripped. */
|
|
115
|
+
export interface SmtpReply {
|
|
116
|
+
code: number;
|
|
117
|
+
lines: string[];
|
|
118
|
+
/** The first line's text, which is the only part any message may quote. */
|
|
119
|
+
first: string;
|
|
120
|
+
}
|
|
121
|
+
export interface SmtpTransportOptions {
|
|
122
|
+
host: string;
|
|
123
|
+
port: number;
|
|
124
|
+
security: SmtpSecurity;
|
|
125
|
+
/** Omitted (or empty) means the session does not authenticate. */
|
|
126
|
+
user?: string;
|
|
127
|
+
password?: string;
|
|
128
|
+
/** Whole-session budget. Exceeding it is `smtp-timeout`. */
|
|
129
|
+
timeoutMs?: number;
|
|
130
|
+
/** The name this client gives in EHLO. */
|
|
131
|
+
clientName?: string;
|
|
132
|
+
/**
|
|
133
|
+
* TLS certificate verification. **Defaults to `true`, and production must
|
|
134
|
+
* leave it there.** The only sanctioned `false` is a test against a mock on
|
|
135
|
+
* 127.0.0.1 holding a self-signed fixture certificate.
|
|
136
|
+
*/
|
|
137
|
+
tlsRejectUnauthorized?: boolean;
|
|
138
|
+
/** Applied to every string this module returns. See the module header. */
|
|
139
|
+
redact?(text: string): string;
|
|
140
|
+
}
|
|
141
|
+
/** The SMTP envelope, which is not the message's headers. */
|
|
142
|
+
export interface SmtpEnvelope {
|
|
143
|
+
/** MAIL FROM. The return path, not necessarily the From: header. */
|
|
144
|
+
from: string;
|
|
145
|
+
/** RCPT TO, once each: To, Cc, and **Bcc**, which appear in no header. */
|
|
146
|
+
recipients: readonly string[];
|
|
147
|
+
}
|
|
148
|
+
export type SmtpSendResult = {
|
|
149
|
+
ok: true;
|
|
150
|
+
/** The reply to the end-of-data terminator: the server accepting it. */
|
|
151
|
+
reply: {
|
|
152
|
+
code: number;
|
|
153
|
+
text: string;
|
|
154
|
+
};
|
|
155
|
+
/** `VERB code` for each step, in order. Never a command argument. */
|
|
156
|
+
transcript: string[];
|
|
157
|
+
/** Was the message handed over on an encrypted socket? */
|
|
158
|
+
secure: boolean;
|
|
159
|
+
/** Did the session authenticate, and with which mechanism? */
|
|
160
|
+
authenticated: "PLAIN" | "LOGIN" | null;
|
|
161
|
+
} | {
|
|
162
|
+
ok: false;
|
|
163
|
+
code: SmtpTransportFailureCode | `smtp-${number}`;
|
|
164
|
+
message: string;
|
|
165
|
+
transcript: string[];
|
|
166
|
+
secure: boolean;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* What {@link probeSmtp} reports: {@link SmtpSendResult} without `reply`, since
|
|
170
|
+
* a probe never issues a verb whose reply is anything but a step of the
|
|
171
|
+
* session. The failure codes are the same union, from the same code path.
|
|
172
|
+
*/
|
|
173
|
+
export type SmtpProbeResult = {
|
|
174
|
+
ok: true;
|
|
175
|
+
/** `VERB code` for each step, in order. Never a command argument. */
|
|
176
|
+
transcript: string[];
|
|
177
|
+
/** Was the session encrypted when it ended? */
|
|
178
|
+
secure: boolean;
|
|
179
|
+
/** Did the session authenticate, and with which mechanism? */
|
|
180
|
+
authenticated: "PLAIN" | "LOGIN" | null;
|
|
181
|
+
} | {
|
|
182
|
+
ok: false;
|
|
183
|
+
code: SmtpTransportFailureCode | `smtp-${number}`;
|
|
184
|
+
message: string;
|
|
185
|
+
transcript: string[];
|
|
186
|
+
secure: boolean;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Run one SMTP transaction and report how it went. Never throws.
|
|
190
|
+
*
|
|
191
|
+
* The whole of it is {@link runSession}; this is the entry point that supplies
|
|
192
|
+
* an envelope and a message, and its result is unchanged from the day it was
|
|
193
|
+
* the whole function.
|
|
194
|
+
*/
|
|
195
|
+
export declare function sendMail(options: SmtpTransportOptions, envelope: SmtpEnvelope, message: string): Promise<SmtpSendResult>;
|
|
196
|
+
/**
|
|
197
|
+
* Open a session, authenticate, send nothing, and report. Never throws.
|
|
198
|
+
*
|
|
199
|
+
* Exactly {@link sendMail}'s session up to AUTH — the same connection, the same
|
|
200
|
+
* STARTTLS rules including the no-downgrade refusal and the response-injection
|
|
201
|
+
* guard, the same refusal to put a password on a cleartext socket, the same
|
|
202
|
+
* one-session budget, the same redaction of every string it returns — and then
|
|
203
|
+
* QUIT. See this module's header for what a success does and does not prove:
|
|
204
|
+
* transport, TLS mode and credential, never that a message would be delivered.
|
|
205
|
+
*/
|
|
206
|
+
export declare function probeSmtp(options: SmtpTransportOptions): Promise<SmtpProbeResult>;
|
|
207
|
+
/**
|
|
208
|
+
* RFC 5321 §4.5.2: a line of the message that begins with `.` gets a second
|
|
209
|
+
* one, so the terminator cannot be forged by the message's own content. The
|
|
210
|
+
* terminating `.` line is NOT added here; {@link sendMail} sends it as its own
|
|
211
|
+
* command so the transcript records the reply to it.
|
|
212
|
+
*/
|
|
213
|
+
export declare function dotStuff(message: string): string;
|
|
@@ -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;
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
* A provider built here is only ever handed to
|
|
14
14
|
* {@link executeThroughAdapter} through {@link AdapterExecuteOptions.credentials}.
|
|
15
15
|
* The contract wraps it in a window that closes the instant `act` returns, so
|
|
16
|
-
* every read is inside a verified
|
|
17
|
-
*
|
|
18
|
-
* hold that rule up, and none
|
|
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
19
|
*
|
|
20
20
|
* 1. `core/vault.ts` exports exactly one function that returns a credential
|
|
21
21
|
* value (`getCredential`), and this module is its only caller in the
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential-custody transport for zzz.bot messages (APRV-320).
|
|
3
|
+
*
|
|
4
|
+
* The adapter sends only the JSON value a grant binds. Destination selection
|
|
5
|
+
* is a strict tagged union over two fixed service origins; neither the CLI nor
|
|
6
|
+
* the payload can supply an arbitrary URL. The Bearer credential comes only
|
|
7
|
+
* from the scoped provider opened by the shared adapter contract.
|
|
8
|
+
*/
|
|
9
|
+
import type { CredentialSpec } from "../core/credential-spec.js";
|
|
10
|
+
import { type Adapter, type JsonValue } from "./contract.js";
|
|
11
|
+
export declare const ZZZ_CLASS = "communicate.zzz.external";
|
|
12
|
+
export declare const ZZZ_PRODUCTION_API_BASE = "https://zzz.bot";
|
|
13
|
+
export declare const ZZZ_PREVIEW_API_BASE = "https://zzz-preview.soycarts.workers.dev";
|
|
14
|
+
export declare const ZZZ_DEFAULT_TIMEOUT_MS = 15000;
|
|
15
|
+
export declare const ZZZ_TOKEN_NAME = "zzz.agent_token";
|
|
16
|
+
export declare const ZZZ_FAILURE_CODES: readonly ["zzz-payload-invalid", "zzz-config-invalid", "zzz-invalid-request", "zzz-unauthorized", "zzz-forbidden", "zzz-not-found", "zzz-idempotency-conflict", "zzz-payload-too-large", "zzz-rejected", "zzz-rate-limited", "zzz-unreachable", "zzz-cancelled", "credential-unavailable", "credential-refused", "credential-window-closed"];
|
|
17
|
+
export declare const ZZZ_CREDENTIAL_SPECS: readonly CredentialSpec[];
|
|
18
|
+
type Reference = {
|
|
19
|
+
kind: "external" | "post";
|
|
20
|
+
target: string;
|
|
21
|
+
label: string;
|
|
22
|
+
relationship: "source" | "context" | "supersedes";
|
|
23
|
+
};
|
|
24
|
+
type Message = {
|
|
25
|
+
body: string;
|
|
26
|
+
metadata?: JsonValue;
|
|
27
|
+
tags?: string[];
|
|
28
|
+
references?: Reference[];
|
|
29
|
+
};
|
|
30
|
+
export type ZzzPayload = ({
|
|
31
|
+
environment: "production" | "preview";
|
|
32
|
+
operation: "create_thread";
|
|
33
|
+
room_id: string;
|
|
34
|
+
title: string;
|
|
35
|
+
} & Message) | ({
|
|
36
|
+
environment: "production" | "preview";
|
|
37
|
+
operation: "create_reply";
|
|
38
|
+
thread_id: string;
|
|
39
|
+
} & Message);
|
|
40
|
+
type Validation = {
|
|
41
|
+
ok: true;
|
|
42
|
+
payload: ZzzPayload;
|
|
43
|
+
path: string;
|
|
44
|
+
body: Record<string, JsonValue>;
|
|
45
|
+
} | {
|
|
46
|
+
ok: false;
|
|
47
|
+
message: string;
|
|
48
|
+
};
|
|
49
|
+
export declare function validateZzzPayload(value: JsonValue): Validation;
|
|
50
|
+
export interface ZzzAdapterOptions {
|
|
51
|
+
fetch?: typeof globalThis.fetch;
|
|
52
|
+
timeoutMs?: number;
|
|
53
|
+
/** Constructor-only test seam. The CLI never exposes these values. */
|
|
54
|
+
origins?: Partial<Record<"production" | "preview", string>>;
|
|
55
|
+
}
|
|
56
|
+
export declare function zzzAdapter(options?: ZzzAdapterOptions): Adapter;
|
|
57
|
+
export type ZzzProbeResult = {
|
|
58
|
+
ok: true;
|
|
59
|
+
} | {
|
|
60
|
+
ok: false;
|
|
61
|
+
code: string;
|
|
62
|
+
message: string;
|
|
63
|
+
};
|
|
64
|
+
/** Read-only authentication probe. It proves no write scope or room access. */
|
|
65
|
+
export declare function probeZzz(token: string, options?: ZzzAdapterOptions): Promise<ZzzProbeResult>;
|
|
66
|
+
export {};
|