Skip to content

Move the transaction policy engine to @tetherto/wdk-policies #122

Closed

Description

@AlonzoRicardo

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/ and types/src/policy/.
  • WDK#registerPolicy, WDK#getPolicyExclusions, the constructor options maxConditionTimeoutMs and policyExclusions, _applyPolicies, and the engine calls in dispose.
  • 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.md and AGENTS.md, replaced by a pointer to the new package.
  • zod from dependencies.

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 }. index is absent for getAccountByPath; path is 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.js is +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

  1. @tetherto/wdk 1.0.0-beta.22: the engine removed, the hook added.
  2. @tetherto/wdk-policies 1.0.0-beta.1, right behind it. Its lockfile and CI depend on ^1.0.0-beta.22 being on npm.
  3. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions