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,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derived state: the one place the runtime turns the log into answers
|
|
3
|
+
* (SPEC.md §6.3, §7, §8).
|
|
4
|
+
*
|
|
5
|
+
* Everything the gate, the token module, and the executor believe about an
|
|
6
|
+
* action comes from here. The module exists for two reasons, both structural,
|
|
7
|
+
* both from the APRV-20 review:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Verified reads (finding S1).** {@link readVerifiedRecords} is the only
|
|
10
|
+
* sanctioned way for a decision-making module to read the log. It runs the
|
|
11
|
+
* *full* chain verification — hash recompute, schema validation, `prev`/`seq`
|
|
12
|
+
* walk — by calling `core/verify.ts`'s {@link verifyWithRecords}, and refuses
|
|
13
|
+
* a corrupt log outright. Before this, the gate parsed lines as JSON and
|
|
14
|
+
* trusted them: a forged or spliced record could authorize an action, and the
|
|
15
|
+
* corruption would surface only when someone ran `approval log verify`. A
|
|
16
|
+
* permission system that reads its own evidence without checking it is not a
|
|
17
|
+
* permission system.
|
|
18
|
+
*
|
|
19
|
+
* APRV-43 lifted the linear cost that S1 accepted; see "The verified-read
|
|
20
|
+
* cache" below. What it did not lift is the rule: a decision is still made
|
|
21
|
+
* only from records this process verified in full, against bytes it proved
|
|
22
|
+
* unchanged.
|
|
23
|
+
*
|
|
24
|
+
* 2. **One derivation, no cycle (finding S4).** {@link requestState} used to
|
|
25
|
+
* live in `core/gate.ts`, which `core/token.ts` imported — while
|
|
26
|
+
* `core/gate.ts` imported `core/token.ts` to mint at grant. That import cycle
|
|
27
|
+
* made "which module owns approval state?" unanswerable. The derivation lives
|
|
28
|
+
* here now; `gate.ts`, `token.ts`, and `execute.ts` all import it, and the
|
|
29
|
+
* only remaining edge between them is the intended one, gate → token, at the
|
|
30
|
+
* mint seam. `gate.ts` re-exports the moved names so existing importers (the
|
|
31
|
+
* CLI, the tests) are unaffected.
|
|
32
|
+
*
|
|
33
|
+
* ## The verified-read cache (APRV-43), and why it is not a bypass
|
|
34
|
+
*
|
|
35
|
+
* A daemon re-reads the log on every watch event. Re-verifying from genesis
|
|
36
|
+
* every time makes a session quadratic in the log it is watching, so
|
|
37
|
+
* {@link readVerifiedRecords} keeps a process-lifetime, memory-only cache of the
|
|
38
|
+
* last log it verified clean: the prefix bytes' length, a SHA-256 over those
|
|
39
|
+
* bytes, the head line's bytes at their offset, the chain head, and the records
|
|
40
|
+
* the walk produced. On the next read of the same path, a prefix proved
|
|
41
|
+
* byte-identical is not re-walked; only the appended suffix is verified, chained
|
|
42
|
+
* onto the cached head.
|
|
43
|
+
*
|
|
44
|
+
* **Global invariant 1 ("enforcement paths read only verified records") is what
|
|
45
|
+
* this touches, so the argument is written out rather than assumed.**
|
|
46
|
+
*
|
|
47
|
+
* The tempting design is the cheap one: remember the head line and its offset,
|
|
48
|
+
* and on a re-read accept the prefix if the head line is byte-identical where it
|
|
49
|
+
* was. That design is unsound, and the specific attack says why. Append-only
|
|
50
|
+
* growth is a convention the *writer* honors; it is not a property of the file.
|
|
51
|
+
* An attacker with write access can mutate a record strictly before the head
|
|
52
|
+
* without changing its length — swap two characters of a `summary`, flip a digit
|
|
53
|
+
* of `est_cost_usd` — leaving the file size, the head line, and the head line's
|
|
54
|
+
* offset all identical. Nothing in the suffix walk touches those bytes. The
|
|
55
|
+
* forged record would be handed to the gate as verified, and a hash chain would
|
|
56
|
+
* have been defeated by a cache. "Byte-identical head at the same offset" does
|
|
57
|
+
* not imply "unchanged prefix", and no amount of `stat` makes it imply that.
|
|
58
|
+
*
|
|
59
|
+
* So the cache pays for what it claims: it stores a SHA-256 over the entire
|
|
60
|
+
* verified prefix and **re-hashes those bytes on every cached read**. A match
|
|
61
|
+
* proves the prefix on disk is bit-for-bit the prefix this process verified in
|
|
62
|
+
* full, in this process lifetime. Verification is a pure function of (bytes,
|
|
63
|
+
* schema files, options) — `core/verify.ts` says so and holds no state — so
|
|
64
|
+
* identical bytes verify identically, and replaying the walk over them could
|
|
65
|
+
* only reproduce the records already held. The cache therefore never *admits* a
|
|
66
|
+
* record: it declines to recompute a conclusion it has already computed from
|
|
67
|
+
* bytes it has just re-proved. Every record the caller receives was walked
|
|
68
|
+
* through the full check ladder (parse, `alg`, schema, hash recompute, `seq`
|
|
69
|
+
* succession, `prev` link) by this process, over exactly these bytes.
|
|
70
|
+
*
|
|
71
|
+
* **APRV-217 makes the last sentence conditional, and only under a policy that
|
|
72
|
+
* says so.** Under the default proof (`full`) the paragraph above is exactly
|
|
73
|
+
* what happens on every cached read. Under `incremental`, an operator's policy
|
|
74
|
+
* trades "re-hash the whole prefix on every read" for "hash the appended bytes,
|
|
75
|
+
* and re-hash the whole prefix on a cadence" — same guards, same walk, same
|
|
76
|
+
* verdicts, a bounded window in which an in-place rewrite of the prefix would be
|
|
77
|
+
* served from cache. The argument for that trade, and what it costs, is in the
|
|
78
|
+
* "incremental prefix proof" section below and in
|
|
79
|
+
* `docs/proposals/incremental-prefix-proof.md`.
|
|
80
|
+
*
|
|
81
|
+
* That is still a large win, because the two costs are not comparable: hashing
|
|
82
|
+
* bytes is a single linear pass at memory bandwidth, while the walk it replaces
|
|
83
|
+
* is a JSON parse, an Ajv schema validation, a JCS canonicalization, and a
|
|
84
|
+
* SHA-256 *per record*. The cache trades a full re-verification for a hash of
|
|
85
|
+
* the same bytes and a real verification of the new tail.
|
|
86
|
+
*
|
|
87
|
+
* Everything else the cache records is a discard trigger and never a licence:
|
|
88
|
+
*
|
|
89
|
+
* - **Size** shrinking below the cached prefix discards the entry. A shorter
|
|
90
|
+
* file cannot contain the prefix, and a truncated log must be re-read cold so
|
|
91
|
+
* that the head reported is the file's head, never the remembered one.
|
|
92
|
+
* - **mtime** is a staleness hint with no evidentiary weight. It can only cause
|
|
93
|
+
* a discard (a same-size file whose mtime moved is suspicious), never skip a
|
|
94
|
+
* check. Correctness does not depend on its granularity, on the clock being
|
|
95
|
+
* monotonic, or on the filesystem storing it at all: delete the mtime
|
|
96
|
+
* comparison and the cache is exactly as sound.
|
|
97
|
+
* - **The head line's bytes at their recorded offset** are compared before the
|
|
98
|
+
* prefix hash. This is a fast rejection of the ordinary tamper, not the proof;
|
|
99
|
+
* the prefix hash covers those same bytes and is what the soundness argument
|
|
100
|
+
* rests on.
|
|
101
|
+
* - **The schema directory** is part of the key. Records verified against one
|
|
102
|
+
* schema set are not evidence under another.
|
|
103
|
+
*
|
|
104
|
+
* Only a `clean` verdict populates the cache. A torn tail or a corrupt log
|
|
105
|
+
* leaves the previous entry in place unused (it is discarded on the mismatch
|
|
106
|
+
* that revealed the damage), so nothing derived from a broken read can ever be
|
|
107
|
+
* resumed from. And the cache is memory-only and process-lifetime: no file, no
|
|
108
|
+
* shared state between processes, nothing an attacker can pre-seed. A CLI
|
|
109
|
+
* invocation is a fresh process with an empty cache and behaves exactly as it
|
|
110
|
+
* did before this existed.
|
|
111
|
+
*
|
|
112
|
+
* Records handed out from the cache are deep-frozen, because a caller that
|
|
113
|
+
* mutated a returned record would otherwise corrupt the next reader's evidence.
|
|
114
|
+
* Freezing makes the aliasing safe instead of merely unlikely.
|
|
115
|
+
*
|
|
116
|
+
* Determinism: `requestState` and everything downstream of it read no clock, no
|
|
117
|
+
* network, and no cache; `ts` is a parameter everywhere, so a derivation can be
|
|
118
|
+
* replayed from the log exactly as it was made. The cache sits strictly above
|
|
119
|
+
* that line, in the read path, and is observationally invisible: for the same
|
|
120
|
+
* bytes it returns the same records and the same verdict as a cold read, which
|
|
121
|
+
* `tests/state-cache.test.ts` asserts scenario by scenario.
|
|
122
|
+
*/
|
|
123
|
+
import { type EventRecord, type LogHead } from "./log.js";
|
|
124
|
+
import { type VerifiedLog, type VerifyOptions } from "./verify.js";
|
|
125
|
+
/**
|
|
126
|
+
* Why a verified read refused. These names are shared verbatim by the gate, the
|
|
127
|
+
* token module, and the executor, so the CLI maps all three onto the frozen exit
|
|
128
|
+
* table with one function.
|
|
129
|
+
*/
|
|
130
|
+
export type LogReadRefusalCode =
|
|
131
|
+
/** The log could not be opened (permissions, a directory, a broken path). */
|
|
132
|
+
"log-unreadable"
|
|
133
|
+
/** The log's final line is unterminated: the signature of a crashed write. */
|
|
134
|
+
| "log-torn-tail"
|
|
135
|
+
/**
|
|
136
|
+
* The chain does not verify: a mutated, spliced, reordered, or schema-invalid
|
|
137
|
+
* record. Distinct from `log-unreadable` because the facts are different — one
|
|
138
|
+
* is a filesystem problem, the other is evidence of tampering — and the
|
|
139
|
+
* repairs are different. `approval log verify` owns the detailed vocabulary
|
|
140
|
+
* (`hash-mismatch`, `seq-gap`, …); this code says only "the log is not
|
|
141
|
+
* trustworthy, so nothing may be authorized from it".
|
|
142
|
+
*/
|
|
143
|
+
| "log-corrupt";
|
|
144
|
+
/** A refused read. Structurally a subset of every consumer's refusal shape. */
|
|
145
|
+
export interface LogReadRefusal {
|
|
146
|
+
ok: false;
|
|
147
|
+
code: LogReadRefusalCode;
|
|
148
|
+
message: string;
|
|
149
|
+
}
|
|
150
|
+
/** A successful verified read: the records and the head they end at. */
|
|
151
|
+
export interface VerifiedRecords {
|
|
152
|
+
ok: true;
|
|
153
|
+
records: EventRecord[];
|
|
154
|
+
/**
|
|
155
|
+
* The chain head — `(seq, hash)` of the last record, or `null` for an empty
|
|
156
|
+
* log. This is what a caller passes as `appendEvent`'s `expectedHead`, so that
|
|
157
|
+
* a decision made from these records cannot be appended onto a log that moved
|
|
158
|
+
* underneath it (APRV-20 finding B1).
|
|
159
|
+
*/
|
|
160
|
+
head: LogHead | null;
|
|
161
|
+
}
|
|
162
|
+
export type ReadRecordsResult = VerifiedRecords | LogReadRefusal;
|
|
163
|
+
/** Options accepted by {@link readVerifiedRecords}. */
|
|
164
|
+
export interface ReadVerifiedOptions extends VerifyOptions {
|
|
165
|
+
/**
|
|
166
|
+
* Verified-read cache to use.
|
|
167
|
+
*
|
|
168
|
+
* Defaults to {@link processReadCache}, so a repeat reader (the daemon's watch
|
|
169
|
+
* loop) is accelerated with no wiring and a one-shot CLI process cannot tell
|
|
170
|
+
* the difference. Pass an own {@link VerifiedReadCache} to keep a private one,
|
|
171
|
+
* or `null` to force a cold verification from genesis — which is what an
|
|
172
|
+
* explicit audit wants, and what the equivalence tests compare against.
|
|
173
|
+
*/
|
|
174
|
+
cache?: VerifiedReadCache | null;
|
|
175
|
+
/**
|
|
176
|
+
* Publish a verified-head snapshot beside the log after a clean read
|
|
177
|
+
* (APRV-188).
|
|
178
|
+
*
|
|
179
|
+
* Off by default, and set by exactly one caller: the daemon, whose warm cache
|
|
180
|
+
* makes it the process that has already done the walk every hook process
|
|
181
|
+
* would otherwise repeat. It is set on the READ rather than done afterwards
|
|
182
|
+
* so the bytes endorsed are the bytes verified — a publisher that re-read the
|
|
183
|
+
* file to hash it could endorse a digest of bytes nobody walked.
|
|
184
|
+
*/
|
|
185
|
+
publishSnapshot?: boolean;
|
|
186
|
+
/**
|
|
187
|
+
* Which prefix proof a cached read runs (APRV-217).
|
|
188
|
+
*
|
|
189
|
+
* Absent means {@link FULL_READ_PROOF}: today's behaviour, byte for byte, for
|
|
190
|
+
* every caller that does not ask otherwise. It is set by exactly one kind of
|
|
191
|
+
* caller — a long-lived process (the daemon) that was handed a policy or a
|
|
192
|
+
* flag naming `incremental`. One-shot processes, the hook included, never set
|
|
193
|
+
* it, and a `cache: null` read ignores it entirely.
|
|
194
|
+
*/
|
|
195
|
+
readProof?: ReadProof;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Set (or clear, with `null`) the default proof for this process's reads.
|
|
199
|
+
*
|
|
200
|
+
* Called by exactly one kind of caller: the two long-lived operator verbs, at
|
|
201
|
+
* startup, after they have resolved the flag against the policy.
|
|
202
|
+
*/
|
|
203
|
+
export declare function useReadProof(proof: ReadProof | null): void;
|
|
204
|
+
/** The default proof in force here. Diagnostics and tests. */
|
|
205
|
+
export declare function readProofInForce(): ReadProof;
|
|
206
|
+
/** Opt this process into (or out of) snapshot-resumed reads. */
|
|
207
|
+
export declare function useVerifiedSnapshots(enabled: boolean): void;
|
|
208
|
+
/** Whether snapshot-resumed reads are enabled here. Diagnostics and tests. */
|
|
209
|
+
export declare function verifiedSnapshotsEnabled(): boolean;
|
|
210
|
+
/** The head of a record list: the last record's `(seq, hash)`, or `null`. */
|
|
211
|
+
export declare function headOf(records: EventRecord[]): LogHead | null;
|
|
212
|
+
/** Which prefix proof a read runs. `full` is today's, byte for byte. */
|
|
213
|
+
export type ReadProofMode = "full" | "incremental";
|
|
214
|
+
/** Reads between full re-proofs under `incremental` (SPEC-signed default). */
|
|
215
|
+
export declare const DEFAULT_FULL_REPROOF_EVERY = 50;
|
|
216
|
+
/** Milliseconds between full re-proofs under `incremental`. */
|
|
217
|
+
export declare const DEFAULT_FULL_REPROOF_AFTER_MS = 60000;
|
|
218
|
+
/** The proof a read runs, with the cadence that bounds `incremental`. */
|
|
219
|
+
export interface ReadProof {
|
|
220
|
+
mode: ReadProofMode;
|
|
221
|
+
/** A full re-proof runs at least this often, counted in reads. */
|
|
222
|
+
everyReads: number;
|
|
223
|
+
/** …and at least this often in wall-clock milliseconds. */
|
|
224
|
+
afterMs: number;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* What a reader gets when it asks for nothing: today's proof on every read.
|
|
228
|
+
*
|
|
229
|
+
* The default is `full` because it is the behaviour this repository has today
|
|
230
|
+
* and the one an operator has attested to. `incremental` is reached only by a
|
|
231
|
+
* caller that was handed a policy (or a flag) saying so.
|
|
232
|
+
*/
|
|
233
|
+
export declare const FULL_READ_PROOF: ReadProof;
|
|
234
|
+
/** Bytes hashed by the verified-read cache so far. Diagnostics and tests. */
|
|
235
|
+
export declare function hashedByteCount(): number;
|
|
236
|
+
/** Reset {@link hashedByteCount}. Tests only. */
|
|
237
|
+
export declare function resetHashedByteCount(): void;
|
|
238
|
+
/**
|
|
239
|
+
* What one read may do with the published snapshot beside the log (APRV-188).
|
|
240
|
+
*
|
|
241
|
+
* Both halves default to off. `consume` is set from the process-wide switch
|
|
242
|
+
* `useVerifiedSnapshots` (the hook turns it on); `publish` is set per call by
|
|
243
|
+
* the daemon.
|
|
244
|
+
*/
|
|
245
|
+
export interface SnapshotUse {
|
|
246
|
+
consume?: boolean;
|
|
247
|
+
publish?: boolean;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Process-lifetime, memory-only store of last-verified log state.
|
|
251
|
+
*
|
|
252
|
+
* Nothing here is written to disk and nothing is shared between processes. An
|
|
253
|
+
* instance is safe to construct per caller (the daemon may want its own); the
|
|
254
|
+
* default is {@link processReadCache}, which is why a repeat reader gets the
|
|
255
|
+
* acceleration without asking for it and a one-shot CLI process cannot notice
|
|
256
|
+
* it exists.
|
|
257
|
+
*/
|
|
258
|
+
export declare class VerifiedReadCache {
|
|
259
|
+
#private;
|
|
260
|
+
/** Forget everything. Tests use this to force a genuinely cold read. */
|
|
261
|
+
clear(): void;
|
|
262
|
+
/** How many logs are remembered. Diagnostics and tests only. */
|
|
263
|
+
get size(): number;
|
|
264
|
+
/**
|
|
265
|
+
* Reads that reused a proved prefix, and reads that verified from genesis.
|
|
266
|
+
*
|
|
267
|
+
* Diagnostics, and the one way a test can tell the two paths apart: reusing a
|
|
268
|
+
* prefix is *designed* to be invisible in the result, so a test that wants to
|
|
269
|
+
* assert "this tamper discarded the cache" has nothing else to look at.
|
|
270
|
+
*/
|
|
271
|
+
get stats(): {
|
|
272
|
+
hits: number;
|
|
273
|
+
misses: number;
|
|
274
|
+
resumed: number;
|
|
275
|
+
fullReproofs: number;
|
|
276
|
+
};
|
|
277
|
+
/**
|
|
278
|
+
* Require a full re-proof of `logPath` on this cache's next read (APRV-217).
|
|
279
|
+
*
|
|
280
|
+
* Called by `core/log.ts` after a successful append, through the listener it
|
|
281
|
+
* registers below. Can only add work: the next read hashes the whole prefix
|
|
282
|
+
* exactly as a `full` read does.
|
|
283
|
+
*/
|
|
284
|
+
requireFullReproof(logPath: string): void;
|
|
285
|
+
/**
|
|
286
|
+
* Verify `logPath`, reusing a proved-identical prefix when there is one.
|
|
287
|
+
*
|
|
288
|
+
* The whole file is read once, and every decision is made from that single
|
|
289
|
+
* snapshot: nothing is re-`stat`ed and re-read behind its own conclusion.
|
|
290
|
+
*/
|
|
291
|
+
read(logPath: string, options: VerifyOptions, snapshot?: SnapshotUse, proof?: ReadProof): VerifiedLog;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* The cache {@link readVerifiedRecords} uses when a caller names none.
|
|
295
|
+
*
|
|
296
|
+
* Shared per process, which is what makes a watch loop fast without any wiring:
|
|
297
|
+
* the daemon calls `readVerifiedRecords` exactly as every other consumer does.
|
|
298
|
+
*/
|
|
299
|
+
export declare const processReadCache: VerifiedReadCache;
|
|
300
|
+
/**
|
|
301
|
+
* Read the log and refuse unless the whole chain verifies.
|
|
302
|
+
*
|
|
303
|
+
* An absent log is an empty log (nothing has happened yet), exactly as
|
|
304
|
+
* `appendEvent` and `approval log verify` treat it.
|
|
305
|
+
*
|
|
306
|
+
* The file is opened once as a readability probe *before* verification so that
|
|
307
|
+
* "I could not open this file" stays an I/O fact (`log-unreadable`) and never
|
|
308
|
+
* arrives dressed as corruption — the same split the CLI's exit table draws, and
|
|
309
|
+
* the one thing `verify()` alone cannot express, since from inside the chain
|
|
310
|
+
* walker an unreadable log is indistinguishable from a broken one.
|
|
311
|
+
*
|
|
312
|
+
* Torn-tail behavior is unchanged from the pre-APRV-20 reader: the tear is
|
|
313
|
+
* reported as `log-torn-tail` and nothing is repaired, because truncating a torn
|
|
314
|
+
* line is a human decision.
|
|
315
|
+
*
|
|
316
|
+
* Reads go through the verified-read cache by default (see the module header):
|
|
317
|
+
* a prefix re-proved byte-identical is not re-walked, the appended suffix is
|
|
318
|
+
* verified in full, and any mismatch falls back to genesis. The result is the
|
|
319
|
+
* result of a cold read on the same bytes, always. `cache: null` opts out.
|
|
320
|
+
*/
|
|
321
|
+
export declare function readVerifiedRecords(logPath: string, options?: ReadVerifiedOptions): ReadRecordsResult;
|
|
322
|
+
/**
|
|
323
|
+
* One action's approval state, derived from the log.
|
|
324
|
+
*
|
|
325
|
+
* These are the gate's names for the approval lifecycle of SPEC.md §6.3. The
|
|
326
|
+
* envelope's `state:` enum is the projection of the same thing over a whole
|
|
327
|
+
* task (`proposed`/`awaiting`/`approved`/…); the mapping is one-to-one for the
|
|
328
|
+
* action-scoped states and is applied by the projection layer, not here.
|
|
329
|
+
*/
|
|
330
|
+
export type RequestState =
|
|
331
|
+
/** No `approval.requested` for this key. */
|
|
332
|
+
"none"
|
|
333
|
+
/** Requested and undecided — the envelope's `awaiting`. */
|
|
334
|
+
| "requested"
|
|
335
|
+
/** A human granted it — the envelope's `approved`. */
|
|
336
|
+
| "granted" | "rejected" | "revoked"
|
|
337
|
+
/** TTL lapsed, by event or by arithmetic. */
|
|
338
|
+
| "expired"
|
|
339
|
+
/**
|
|
340
|
+
* The requester took the question back while it was still pending (APRV-106,
|
|
341
|
+
* amended SPEC.md §6.3).
|
|
342
|
+
*
|
|
343
|
+
* Terminal, and terminal in the same sense `rejected` is: the gate refuses a
|
|
344
|
+
* grant, a rejection, a revocation or a second withdrawal afterwards. It is
|
|
345
|
+
* NOT a decision — nobody answered — which is why the projection layer reads
|
|
346
|
+
* it as "back to proposed" rather than as a refusal, and why no authorization
|
|
347
|
+
* is implied in either direction.
|
|
348
|
+
*/
|
|
349
|
+
| "withdrawn";
|
|
350
|
+
/** The three terminal decisions a human can record, plus runtime expiry. */
|
|
351
|
+
export type Decision = "grant" | "reject" | "revoke";
|
|
352
|
+
/**
|
|
353
|
+
* Why a requester retracted a pending request (amended SPEC.md §6.3, APRV-106).
|
|
354
|
+
*
|
|
355
|
+
* Closed, and mirrored by `event.schema.json`: a withdrawal that does not say
|
|
356
|
+
* why is a fact audit cannot use. `timeout` is the one the hook writes — the
|
|
357
|
+
* requester stopped waiting, so a decision on this request can no longer be
|
|
358
|
+
* consumed by anyone.
|
|
359
|
+
*/
|
|
360
|
+
export declare const WITHDRAW_REASONS: readonly ["timeout", "cancelled", "superseded"];
|
|
361
|
+
export type WithdrawReason = (typeof WITHDRAW_REASONS)[number];
|
|
362
|
+
/** Is `value` one of the closed withdrawal reasons? */
|
|
363
|
+
export declare function isWithdrawReason(value: unknown): value is WithdrawReason;
|
|
364
|
+
/**
|
|
365
|
+
* What the request declared, copied out of the `approval.requested` payload.
|
|
366
|
+
*
|
|
367
|
+
* ## The scrutiny ratchet (amended SPEC.md §11), stated where it binds
|
|
368
|
+
*
|
|
369
|
+
* `summary` and `est_cost_usd` are **claimed** fields: the party under
|
|
370
|
+
* oversight authored them. So is `route.confidence`, which this derivation
|
|
371
|
+
* deliberately does not carry at all — nothing in the resolution, sampling, or
|
|
372
|
+
* refusal paths reads it, and `tests/ratchet.test.ts` scans the source to keep
|
|
373
|
+
* it that way. Claimed fields MAY raise scrutiny, inform display, or feed
|
|
374
|
+
* retrospective analysis; they MUST NOT reduce sampling probability, downgrade
|
|
375
|
+
* a resolved autonomy level, or shortcut any refusal path. `est_cost_usd: 0`
|
|
376
|
+
* therefore still consumes one action of every action-count budget, and a
|
|
377
|
+
* confident summary buys nothing. Scrutiny only ratchets upward on self-report.
|
|
378
|
+
*
|
|
379
|
+
* `class` and `payload_hash` sit on the other side of that line: `class` is
|
|
380
|
+
* matched against policy the human attested, and `payload_hash` is *checked*
|
|
381
|
+
* against the bytes an executor presents (`core/payload.ts`), so a false one
|
|
382
|
+
* refuses rather than relaxes.
|
|
383
|
+
*/
|
|
384
|
+
export interface DeclaredAction {
|
|
385
|
+
class: string | null;
|
|
386
|
+
/**
|
|
387
|
+
* The declared cost as a canonical decimal USD string (APRV-121), or `null`
|
|
388
|
+
* when the request declared none. A record written before that change carries
|
|
389
|
+
* a JSON number and is normalized to the same string here, so every reader
|
|
390
|
+
* downstream sees one representation regardless of when its record was
|
|
391
|
+
* written.
|
|
392
|
+
*/
|
|
393
|
+
est_cost_usd: string | null;
|
|
394
|
+
reversible: boolean | null;
|
|
395
|
+
summary: string | null;
|
|
396
|
+
/**
|
|
397
|
+
* The content binding of amended SPEC.md §6.2, copied from the registered
|
|
398
|
+
* declaration onto `approval.requested` so the grant can copy it in turn.
|
|
399
|
+
* `null` for a request that declared none — which the gate admits only off
|
|
400
|
+
* the manual path.
|
|
401
|
+
*/
|
|
402
|
+
payload_hash: string | null;
|
|
403
|
+
/**
|
|
404
|
+
* `"harness"` when the requester declared that it will never run this action
|
|
405
|
+
* through `approval run` (APRV-106, the Claude Code hook): the harness
|
|
406
|
+
* executes the command itself and the gate's answer is a permission decision,
|
|
407
|
+
* not a key. A grant on such a request mints no execution token.
|
|
408
|
+
*
|
|
409
|
+
* On the ratchet's safe side (SPEC.md §11.1), and deliberately so. It is a
|
|
410
|
+
* self-reported field, and every effect it has REMOVES capability from the
|
|
411
|
+
* party that reported it: no token is minted, so `approval run` and
|
|
412
|
+
* `approval consume` refuse the key outright. There is no reading of a false
|
|
413
|
+
* `harness` claim that authorizes anything the truthful claim would not.
|
|
414
|
+
*/
|
|
415
|
+
execution: "harness" | null;
|
|
416
|
+
/**
|
|
417
|
+
* The deadline the requester says it will wait until, ISO-8601, or `null`.
|
|
418
|
+
*
|
|
419
|
+
* Display only, and claimed: channels render it so an approver can see that
|
|
420
|
+
* an answer after this instant reaches nobody (APRV-106). Nothing in the
|
|
421
|
+
* gate, the token module, the budgets or the TTL reads it — the TTL is the
|
|
422
|
+
* policy's and is the only deadline that governs anything — so a requester
|
|
423
|
+
* that lies about it can only make its own request look MORE urgent than it
|
|
424
|
+
* is, never less.
|
|
425
|
+
*/
|
|
426
|
+
wait_until: string | null;
|
|
427
|
+
/**
|
|
428
|
+
* The SHA-256 of the attested policy in force when the runtime evaluated the
|
|
429
|
+
* request (APRV-118, amended SPEC.md §5.2), or `null` for a record written
|
|
430
|
+
* before the field existed.
|
|
431
|
+
*
|
|
432
|
+
* Computed, never claimed: the requester has no parameter that reaches it, and
|
|
433
|
+
* the gate assigns it at the write boundary from its own read of the attested
|
|
434
|
+
* file, exactly as it assigns `ts`. The grant path compares it against the hash
|
|
435
|
+
* in force at decision time and refuses `policy-drift` on a difference, so a
|
|
436
|
+
* value that disagrees with the runtime's can only refuse a grant, never
|
|
437
|
+
* produce one.
|
|
438
|
+
*/
|
|
439
|
+
policy_sha256: string | null;
|
|
440
|
+
}
|
|
441
|
+
/** Execution facts for the action key: the seq of each event, or `null`. */
|
|
442
|
+
export interface ExecutionFacts {
|
|
443
|
+
started: number | null;
|
|
444
|
+
completed: number | null;
|
|
445
|
+
failed: number | null;
|
|
446
|
+
}
|
|
447
|
+
/** The full derivation, so a caller never re-walks the log to explain itself. */
|
|
448
|
+
export interface RequestDerivation {
|
|
449
|
+
actionKey: string;
|
|
450
|
+
state: RequestState;
|
|
451
|
+
task: string | null;
|
|
452
|
+
requestSeq: number | null;
|
|
453
|
+
requestTs: string | null;
|
|
454
|
+
/**
|
|
455
|
+
* The actor of the `approval.requested` record that opened the current cycle
|
|
456
|
+
* (APRV-106). The gate compares a withdrawal's actor against it: only the
|
|
457
|
+
* party that asked may take the question back.
|
|
458
|
+
*/
|
|
459
|
+
requestActor: string | null;
|
|
460
|
+
decision: "granted" | "rejected" | "revoked" | "expired" | "withdrawn" | null;
|
|
461
|
+
decisionSeq: number | null;
|
|
462
|
+
decisionTs: string | null;
|
|
463
|
+
/** An `approval.expired` record exists for the current request. */
|
|
464
|
+
expiredByEvent: boolean;
|
|
465
|
+
/** The TTL lapsed by arithmetic, with no `approval.expired` record. */
|
|
466
|
+
expiredLazily: boolean;
|
|
467
|
+
declared: DeclaredAction;
|
|
468
|
+
execution: ExecutionFacts;
|
|
469
|
+
}
|
|
470
|
+
/** A record's payload as a map, or `{}` when it has none. */
|
|
471
|
+
export declare function payloadOf(record: EventRecord): Record<string, unknown>;
|
|
472
|
+
/**
|
|
473
|
+
* Derive `actionKey`'s approval state from `records`.
|
|
474
|
+
*
|
|
475
|
+
* Pure: no I/O, no clock. `ts` is the moment the question is being asked and is
|
|
476
|
+
* **required** — lazy expiry is arithmetic on it, and a state function that read
|
|
477
|
+
* the clock could not be replayed.
|
|
478
|
+
*
|
|
479
|
+
* Sequencing rules, all of them deliberate:
|
|
480
|
+
*
|
|
481
|
+
* - An `approval.requested` **resets** the derivation. A key that was rejected
|
|
482
|
+
* or expired may be requested again; the new request starts a fresh cycle and
|
|
483
|
+
* the old decision no longer governs. (An action that has *executed* is a
|
|
484
|
+
* different matter — `core/gate.ts`'s `request` refuses that on idempotency
|
|
485
|
+
* grounds.)
|
|
486
|
+
* - The **first** decision after a request wins. The gate refuses to append a
|
|
487
|
+
* second one, so a log carrying two is a log written by something else; the
|
|
488
|
+
* fail-closed reading is that the earliest human decision stands rather than
|
|
489
|
+
* that a later append can overwrite it.
|
|
490
|
+
* - Execution facts accumulate across the whole log for the key, independent of
|
|
491
|
+
* the request cycle: an action that has executed has executed, and no
|
|
492
|
+
* subsequent request un-executes it.
|
|
493
|
+
* - Withdrawal (APRV-106): an `approval.withdrawn` settles the request like any
|
|
494
|
+
* other terminal event, and is the only one of them that records no decision.
|
|
495
|
+
* It participates in the "first settlement wins" rule above, so a withdrawal
|
|
496
|
+
* appended after a human's answer does not erase the answer; the gate refuses
|
|
497
|
+
* to append one at all in that case.
|
|
498
|
+
* - Expiry: an `approval.expired` record sets `expiredByEvent`. With no such
|
|
499
|
+
* record, `ttlMs !== null` and `ts > requestTs + ttlMs` sets `expiredLazily`.
|
|
500
|
+
* Both yield `state: "expired"`. An unparseable `requestTs` or `ts` also
|
|
501
|
+
* yields `expired`: liveness that cannot be demonstrated is not assumed. (The
|
|
502
|
+
* event schema's `date-time` format makes that unreachable through the real
|
|
503
|
+
* append path; it is a backstop, not a live branch.)
|
|
504
|
+
*/
|
|
505
|
+
export declare function requestState(records: EventRecord[], actionKey: string, ts: string, ttlMs: number | null): RequestDerivation;
|