Repository navigation
Description
Problem
The transaction policy engine ships inside @tetherto/wdk. Every install of core gets it, and its zod dependency, whether or not the app registers a single policy. The engine is in beta and opt-in by nature: nothing else in core uses it, and the ten files in src/policy/ import nothing from the rest of core, only zod.
The engine moves to tetherto/wdk-policies, published as @tetherto/wdk-policies, which attaches to a WDK instance as a plugin. Core keeps one small hook, so the plugin can still govern every account the instance hands out.
Companion issue: tetherto/wdk-policies#1
What changes in core
Removed
src/policy/andtypes/src/policy/.WDK#registerPolicy,WDK#getPolicyExclusions, the constructor optionsmaxConditionTimeoutMsandpolicyExclusions,_applyPolicies, and the engine calls indispose.- From
index.js:PolicyViolationError,PolicyConfigurationError,DEFAULT_POLICY_EXCLUSIONS,DENIAL_CODES, and the policy typedefs (Policy,PolicyRule,PolicyCondition,PolicyContext,PolicyAction,PolicyScope,PolicyOperation,SimulationResult,SimulationTraceEntry,RegisterPolicyOptions,DenialCode). tests/wdk-policy.test.js, which moves with the engine.- The policy sections of
README.mdandAGENTS.md, replaced by a pointer to the new package. zodfromdependencies.
Changed: the middleware hook
The engine used to wrap the account after middlewares ran. The plugin does the same thing through a middleware, which needs three changes to _runMiddlewares:
- A middleware receives a second argument,
{ blockchain, index, path }.indexis absent forgetAccountByPath;pathis the account's full derivation path. - A middleware's non-nullish return value replaces the account. The next middleware, and the caller of
getAccount, get the replacement. - Middlewares run after
_registerProtocols, at the point where the engine used to wrap the account. The account a middleware returns therefore already has the protocol getters.
New typedef MiddlewareContext. MiddlewareFunction becomes (account, context) => Promise<account | void>.
Version: 1.0.0-beta.22. A beta release may remove API; the changelog names the new package.
Size, measured on a branch cut from main at 339a403
- 31 files, +101 / −6,940. Of that,
src/wdk.jsis +8 / −27 lines of code; the rest is the engine, its types, tests and docs leaving. - Core's suite after the move: 52 pass. The middleware tests expect the context argument and gain two replacement cases.
standard: clean.- The engine's own suite still passes, unchanged apart from its import line, against core plus the plugin. See the companion issue.
What a developer changes
| Before, in core | After |
|---|---|
new WDK(seed, { maxConditionTimeoutMs, policyExclusions }) |
new WDK(seed) and new WdkPolicies({ maxConditionTimeoutMs, policyExclusions }) |
wdk.registerPolicy(policies, options) |
new WdkPolicies().attach(wdk, 'ethereum').register(policies, options) |
wdk.getPolicyExclusions() |
policies.getExclusions() |
wdk.dispose(blockchains) |
wdk.dispose(blockchains), plus policies.dispose(blockchains) |
errors and constants from @tetherto/wdk |
the same names from @tetherto/wdk-policies |
Policies, rules, conditions, the evaluation context, simulation, denial codes and the error classes do not change.
Changelog note. A middleware that returns a value by accident would now replace the account. The known middlewares, in wdk-examples/wdk/middleware.ts and the docs' middleware guide, return nothing.
Open work on the engine
These PRs change src/policy/, and the move has to account for them:
| PR | Fixes | Also touches src/wdk.js |
|---|---|---|
| #117 | #98, protocols constructed with the unwrapped account | yes |
| #119, #111 | #100, override ALLOW skipping a later account DENY. These two PRs are the same fix. | no |
| #120 | #101, path not frozen at wrap time | yes |
| #121 | #102, functions added after wrap | no |
| #88, #89 (drafts) | Policy base classes | no |
The proposal is to decide these in core first, then cut the copy from the resulting commit. The copy is mechanical, so landing fixes first costs nothing, and they are reviewed against the suite they were written for. Anything still open when the copy is cut gets reopened against wdk-policies.
#117 needs care. It builds each protocol lazily from the governed account, so a protocol cannot reach the raw account. The hook has to keep that property: a protocol must be built with the account the middlewares return. The size above was measured against main without #117, where protocols are still built with the raw account, which is today's behaviour.
Release order
@tetherto/wdk1.0.0-beta.22: the engine removed, the hook added.@tetherto/wdk-policies1.0.0-beta.1, right behind it. Its lockfile and CI depend on^1.0.0-beta.22being on npm.- wdk-docs, the same week:
- the changelog;
- the core API reference, core configuration and core index;
- the transactions guide;
- the transaction-policies guide, which moves under a wdk-policies heading;
- the middleware guide, which gains the context argument and the return value.
The plugin can publish minutes after core, so there is no window with neither.
Alternative considered
A wrapper around the WDK instance would need no core change at all. But the code that created the instance could still get ungoverned accounts from it. The hook keeps today's property that every account the instance hands out goes through the engine.
Activity
Move the transaction policy engine to @tetherto/wdk-policies