npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@oaath/automation-server

v0.3.13

Published

Self-hostable OAAth automation service: HTTP API, scheduler and executor over PostgreSQL.

Readme

@oaath/automation-server

A self-hostable automation service: one Node process with the HTTP API, the scheduler and the executors, and PostgreSQL as its only dependency. It runs declarative automations from @oaath/automation with Grants it obtains as an ordinary OAuth client of an OAAth issuer.

npx @oaath/automation-server   # the `oaath-automation` bin; configure with the variables below

Run any number of replicas against one database. The schema is created on first start; an older schema is refused and must be dropped and recreated (there are no migrations).

Configuration

| Variable | Meaning | | --- | --- | | DATABASE_URL | PostgreSQL connection URL. Required. | | AUTOMATION_SEAL_KEY | 64 hex characters: the AES-256-GCM key sealing session keys and OAuth verifiers at rest. Required; keep it with the database. | | AUTOMATION_PUBLIC_URL | The service's external base URL. The OAuth redirect URI is <url>/v1/oauth/callback. Required. | | AUTOMATION_LISTEN | host:port; default 127.0.0.1:4317. | | OAATH_ISSUER | The OAAth issuer URL, e.g. https://oaath.taek.tech. Required. | | OAATH_CLIENT_ID | This service's client, registered at the issuer with the redirect URI above. Required. | | AUTOMATION_APPLICATIONS | Comma-separated id:sha256hex application credentials (SHA-256 of each bearer token). Required. | | AUTOMATION_ALLOWED_ORIGINS | Comma-separated browser origins allowed to call the API and to be returnTo targets. | | AUTOMATION_DEFINITIONS | Comma-separated JSON files, each one definition or an array. Required. | | AUTOMATION_RPC_URL_<chainId> | Chain RPC for every chain a definition names. Required. | | AUTOMATION_BUNDLER_URL_<chainId> | ERC-4337 bundler for that chain. Required. | | AUTOMATION_BUNDLER_API_KEY_<chainId> | Optional server-only JSON-RPC apiKey credential, sent only to that chain's bundler. Required when its endpoint enforces client authentication. | | AUTOMATION_PAYMASTER_URL_<chainId> | Optional ERC-7677 paymaster; operations are self-funded without it. | | AUTOMATION_PAYMASTER_API_KEY_<chainId> | Optional key for that paymaster, sent as the ERC-7677 context { "apiKey": ... } (as paymaster-rs accepts it) instead of {}. Requires the paymaster URL. | | AUTOMATION_RELAY_PAYS_GAS_<chainId> | Optional true when that chain's bundler pays gas itself (bundle_rs fast mode): operations carry zero fees and need no funds or paymaster. Refused together with a paymaster URL. | | AUTOMATION_MAX_OPEN_SLOTS | Occurrences of one plan that may be open at once, each on its own nonce lane; default 4. | | AUTOMATION_RPC_BUDGET, AUTOMATION_BUNDLER_BUDGET, AUTOMATION_PAYMASTER_BUDGET | Hard request budgets per window; defaults 3000, 300 and 100. | | AUTOMATION_BUDGET_WINDOW_SECONDS | Budget window; default 600. Exhausted work waits for the next window. |

Logs never include URLs, tokens, keys or provider errors.

Execution

plan  draft -> awaiting_consent -> authorized -> active <-> paused
      active|paused -> completed | expired; open plans -> cancelling -> cancelled
      authorized -> active once setup is included and successful (not final)
      authorized|active|paused -> failed when setup fails, reverts or drops
      authorized -> expired (setup_not_included) at the Grant end
run   due -> claimed -> prepared -> submitted -> observed -> finalized
      terminal: finalized | failed | skipped
  • Slot windows are the plan's terms: slot N opens at startAt + N*every and closes grace later, exactly as the setup's on-chain plan and the Grant policy were signed, capped at the Grant end. Activation gates admission without moving a window: slot N is due at max(its opening, activation) and is skipped once its window closes, or at once if it opens after the Grant ends. An authorized plan never expires before the Grant end while it waits for its setup.
  • Each run has one Kernel nonce lane, assigned once at admission: setup and cancel use the default lane; occurrence slot N uses lane N + 1 when the plan has a setup (still observed on lane 0 until final), else lane N. One open run per plan, chain and lane (a unique index), so one unresolved operation per Grant, chain and lane.
  • A slot does not wait for an earlier operation's finality: once one of the plan's operations is included (the permission install is on chain), up to AUTOMATION_MAX_OPEN_SLOTS occurrences run side by side. Before that, slots open one at a time. A slot that cannot open is skipped once its window closes. Cancel waits until no run is open.
  • Runs are claimed with SELECT … FOR UPDATE SKIP LOCKED and a lease; every write is fenced by the claim's generation.
  • The calls are persisted before sending. The SDK journals each operation before signing it, and the same transaction records its hash and nonce on the run; an identity no prepared run claims is refused.
  • A run with an operation hash is only ever observed again. A timeout, missing receipt, drop or unreadable observation never resubmits.
  • Session keys are generated per user (default) or per application (POST /v1/application {"keyScope":"application"}) and sealed at rest.

On-demand plans

An on-demand definition lets the application's backend submit exact calls under one Grant that covers every chain the definition lists, without a person signing each operation. Create the plan with { "automation": "<id>", "validFor": <seconds>, "idempotencyKey": "…" }; it becomes active as soon as the root approves it (there is no setup).

plan  draft -> awaiting_consent -> authorized -> active <-> paused
      active|paused -> expired at the Grant end once no run is open (never completed)
      cancel: stops admission -> cancelled once no run is open (no cancel calls)

POST /v1/plans/{id}/operations (user session) with { "chainId": 8453, "calls": [{ "to": "0x…", "data": "0x…", "value": "0" }], "idempotencyKey": "…" } admits one operation in one transaction holding the plan row. It is refused unless the plan is active and before its Grant end (plan_not_active, plan_expired, 409), the chain is one of the plan's (chain_not_in_plan, 422), and every call fits the approved policy: its to and selector (the first 4 bytes of data; empty data is 0x00000000, 1 to 3 bytes are refused) are an allowed call and value is within that call's limit (call_not_permitted, value_limit_exceeded, 422). The calls are persisted on a new request run and the executor sends exactly them. The response is 201 (or 200 for a replay):

{ "run": { "id": "request:<sha256(idempotencyKey)>", "kind": "request", "slot": null,
  "chainId": 8453, "lane": 0, "status": "due", "scheduledAt": 1900000000,
  "closesAt": 1900604800, "operation": null, "nonce": null,
  "transactionHash": null, "reason": null } }

The same idempotencyKey with the same body returns the same run; a different body is 409 idempotency_conflict. GET /v1/plans/{id}/runs/{runId} (URL-encode the id) returns the same { run }; GET /v1/plans/{id}/runs lists runs in that shape. Journal the run's operation (UserOperation hash) and transactionHash from there; a lost reply is recovered by replaying the same key, never by a new one.

Lanes are per chain: until one of the plan's operations is included on a chain, requests there run one at a time on lane 0, whose first operation installs the permission in enable mode; afterwards each takes the lowest free lane in 1..AUTOMATION_MAX_OPEN_SLOTS. When none is free the request is refused with 429 lanes_busy and nothing is queued; retry later with the same key.

The Grant bounds targets, selectors, native value, the per-chain operation count and expiry. It does not bound arguments: for a deployer target, it does not bound which initcode is deployed. Whoever holds a user session for the account can deploy anything through the allowed calls until the plan ends or is cancelled, and the root revokes the permission on chain in the portal.

A Kernel 0.3.3 approval covers every listed chain only when the account's enable nonce for this permission is the same on each (the portal reads it on one chain). Where an account's chains differ, align them first with kernelPermissionNonceAlignmentCalls from @oaath/sdk/kernel as one owner operation per lower chain; otherwise the install fails on that chain.

Authorization pushes an oaath_grant request (PKCE, the session key as the Grant's signer, the policy derived from the plan) to the issuer. The account root approves it in the portal; the first operation installs the permission in enable mode. Cancellation stops admission and runs the definition's cancel calls; uninstalling the permission on chain remains the account root's action in the portal.

Test

bun run --filter @oaath/automation-server test       # unit and PostgreSQL suites
bun run --filter @oaath/automation-server test:e2e   # relay + two replicas + Anvil

The PostgreSQL suites start a throwaway local cluster (or use OAATH_TEST_POSTGRES_URL) and skip without PostgreSQL binaries unless OAATH_REQUIRE_POSTGRES=1. The end-to-end run builds the relay with cargo, approves a DCA plan through the portal API as its account root, and checks that the setup and two due occurrences each send exactly once, with finality held so that the plan activates on setup inclusion, a held operation is observed repeatedly, and the second occurrence comes due while the first is included but not final. Its bundler is a loopback fixture over EntryPoint.handleOps; no public network is contacted.