AgentPay's installer lives at wlfi.sh, not in the tag, and its postinstall pulls Rust binaries
An open SDK for agentic payments. Let AI agents make payments, hold funds, and move money across chains with policy enforcement and human approval built in.
At a glance
- What is it?
- A MIT-licensed agentic payments SDK in Rust and TypeScript, with two payment paths: fiat card credentials issued by a Link app that the user approves, or a self-custodial local daemon that signs and broadcasts itself. The crypto side is unusually carefully built, with per-platform keychain backends and rustls-only TLS. The installation story is where it gets loose: an unversioned shell installer, a postinstall hook that installs native binaries, and one source directory described with two different package managers.
- Who is it for?
- Adopt AgentPay if you need an agent to spend money with a human in the loop and you control the host, since the crypto path keeps signing local and the fiat path routes approval through the Link app. Do not script the install off wlfi.sh in a pinned or air-gapped pipeline, because that URL serves whatever is current rather than the version your lockfile names.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 117 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The install script is served from wlfi.sh, not from the tag you install
The headline install command pipes a script from a short domain:
curl -fsSL https://wlfi.sh | bashThe URL has no path, no version segment and no reference to the repository. So the script that installs AgentPay SDK 0.5.0 is whatever wlfi.sh is serving on the day you run it, not the script as it stood at the v0.5.0 tag. A release tag pins the Rust crates, the TypeScript packages and the daemon binaries. It does not pin the thing that decides what gets written to your machine.
Compare that with the rest of the documentation, which is otherwise specific about versions. The manifest declares 0.5.0, the Cargo workspace declares 0.5.0, and the release tags run 0.3.1, 0.4.0, 0.5.0. The install path is the one place where no version appears anywhere.
The script is also the widest part of the tool. It can pick an install directory, download a prebuilt macOS or Linux runtime bundle instead of compiling the Cargo or pnpm workspaces, bootstrap Node 20+ when needed, pull Homebrew when Node is missing, install the CLI into a dedicated AGENTPAY_HOME, and then write skill packs and workspace adapter files into eight agent tools. That is a lot of filesystem writes decided by a script you cannot pin.
The narrower mode exists for exactly this reason:
curl -fsSL https://wlfi.sh | bash -s -- --skills-onlySkills-only applies just the embedded skill and adapter files and explicitly does not install the CLI, Node.js, PATH exports or wallet runtime files. It still downloads the same bundle from the same unpinned URL, so the exposure is smaller rather than gone.
postinstall installs Rust binaries, so installing the npm package runs native code
The manifest declares a postinstall hook:
"postinstall": "node ./scripts/install-rust-binaries.mjs",That runs on `pnpm install`, on `npm install`, and on any dependency install where this package is a transitive dependency. It is not a build-time script that only runs for maintainers. It is the default behaviour of installing the JavaScript package.
The reason is structural. The CLI launcher at `dist/cli.cjs` is a Node program, but the wallet, the policy engine, the signer and the daemon are Rust. So a working install needs native binaries placed into the agentpay home directory, and the hook is what does it.
The consequence for anyone who treats an npm install as inert is that this package has a supply-chain surface larger than its dependency list suggests. It ships Rust source in its tarball, so the files array includes crates, Cargo.toml and Cargo.lock alongside dist, scripts, src and packages. Installing it pulls down Rust code and then executes a script that puts it in a path your shell is configured to load.
The project does provide an escape hatch, and its existence is the clearest evidence that this is understood to be friction. On Windows:
AGENTPAY_SKIP_RUST_INSTALL=1 pnpm installThat variable exists because a Windows machine cannot build the daemon at all, which is the subject of the next problem. On macOS and Linux there is no documented equivalent for skipping the hook, since there the build is expected to work.
There is also a separate prepack hook that runs the build before packing, so the sequence for a maintainer publishing is build then install-binaries, and both paths exist in the same manifest.
The same scripts are named with pnpm in one sentence and npm in the next
The source-install section opens with pnpm:
pnpm install
pnpm run build
pnpm run install:cli-launcher
pnpm run install:rust-binariesFour lines later, describing what those last two do, it switches manager:
npm run install:cli-launcherand further down, in the reinstall guidance for a source checkout, it uses `npm run install:rust-binaries` again. The Windows note uses `AGENTPAY_SKIP_RUST_INSTALL=1 pnpm install`. So within about forty lines the same two scripts appear under both runners, with no statement that either works.
The repository supports both. There is a pnpm-workspace.yaml and a pnpm-lock.yaml at the root, and the manifest also declares a workspaces array with npm-style globs for packages/*, apps/* and apps/maintenance/*. That is two independent workspace mechanisms describing one dependency tree, and which one a command honours depends on which manager you invoke.
The ambiguity reaches into the manifest itself. The test script is `pnpm test:ts && pnpm test:rust`, so running `npm test` shells out to pnpm. The prepack script is `npm run build`, while build is `turbo run build && tsup`, so publishing through pnpm still reaches for npm. Nothing here is broken, but it means the documented commands and the manifest are not the same source of truth, and a reader cannot tell which pairing was actually tested.
For the build itself the stack is coherent: turbo drives the per-package builds, tsup produces the CommonJS bundles the bin and exports entries point at, biome handles lint and format, and a separate script runs the Rust tests in isolation.
Windows gets the JavaScript facade and none of the daemon
The platform story splits in two, and the split is architectural rather than a packaging oversight.
The crate workspace has ten members and none of them is a Windows transport. They are vault-domain, vault-policy, vault-signer, vault-daemon, vault-transport-unix, vault-sdk-agent, vault-cli-admin, vault-cli-agent, vault-cli-daemon and vault-transport-xpc. Unix and XPC are the two transport implementations, and XPC is a macOS inter-process mechanism. There is no third transport for Windows.
The documentation states the consequence plainly: source installs build and install the CLI and runtime binaries on macOS and Linux, and Windows does not yet support the Rust daemon or runtime from source in this repository. If you only need the JavaScript workspace there, install with the skip flag.
What a Windows user actually gets is the Link facade. The package exports a ./link subpath with both an import condition and a require condition, plus named functions for creating a card, listing payment methods and onboarding the Link account. Those are the fiat path, and they are pure JavaScript calls into a service. The crypto path, which is where the domain, policy, signer and daemon crates live, is simply absent.
So on Windows you can ask an agent to request a one-time card credential and you cannot give it a self-custodial wallet. That is a coherent split given that the crypto path is local-first and needs a signing device and a keychain, and it is worth stating before someone builds a plan on the assumption that the SDK is uniform across the three platforms. The managed daemon is documented as macOS launchd and Linux system systemd, with no third option offered.
The card number is written to a 0600 file even though stdout stays redacted
Creating a one-time card is the most sensitive operation in the fiat path, and the documentation is precise about where the sensitive data lands.
agentpay link card \
--payment-method-id csmrpd_xxx \
--merchant-name "Stripe Press" \
--merchant-url "https://press.stripe.com" \
--amount 3500 \
--context "The user asked this agent to buy Working in Public from Stripe Press. The user will approve this exact purchase in Link before any card credential can be used." \
--output-file ~/.agentpay/link-cards/stripe-press-card.jsonThe stated behaviour: full card credentials are written to a local file with 0600 permissions, and stdout stays redacted. Redacting the stream is the right call, because stdout is what lands in agent transcripts and logs. But the redaction is about the stream, not about the machine. The complete card credential ends up as a JSON file under the agentpay home, readable by the uid running the agent, and it persists until something deletes it.
The approval model is worth reading in the same breath. The --context value is free text that the caller supplies, and it asserts that the user will approve this exact purchase in Link before any credential can be used. That is a description of the Link-side flow, not an enforcement this SDK performs. The binding constraint is that the user approves each spend request in the Link app, which is what makes the one-time card safe to hand to an agent at all. The CLI holds no authority to spend; it holds a credential that only becomes useful after an approval elsewhere.
Note also what the example does not carry: no currency field alongside --amount 3500, and no expiry or single-use parameter. The scope of the credential comes from Link, not from these flags.
Advanced passthrough exists for the cases the card command does not cover, with subcommands for spend requests, machine payments and serving.
When no agent is detected, the installer enables every integration by default
The installer writes files into a long list of agent tools, and how it decides where is where the sharp behaviour is.
Supported targets are Codex, Claude, Cline, Goose, Windsurf and OpenClaw, plus a portable config directory, a legacy agents directory, and matching workspace skill directories. Workspace adapters go into AGENTS.md, CLAUDE.md, GEMINI.md, .github/copilot-instructions.md, a .clinerules file and the Cursor rule pack. The Cursor adapter is installed when the current directory is already a Cursor workspace or when a setup variable is set.
The detection path preselects already-installed destinations, and the user can toggle preset destinations or add custom paths. That is the careful behaviour, and it is the default.
Now the other branch. When no supported AI target is detected, the installer offers the same integrations with all options enabled by default. Everything on, unprompted, precisely in the case where the installer could not tell what the machine was.
That is a scope-widening fallback in the least informed state. The user is being asked to accept changes to six agent tools plus four workspace instruction files, on a machine where the installer found no evidence of any agent, and the default on that screen is yes to all of them. It is a deliberate trade, since a fresh machine has nothing to detect and requiring eight toggles before a first install is friction, but it is the opposite of the narrow-by-default posture the rest of the installer takes.
The same auto-detect-and-preselect logic appears in skills-only mode, so the behaviour is not confined to the full install.
One negative guarantee is worth recording alongside it: the installer does not configure browser-based relay or web approval services. Approval stays in the Link app rather than being proxied through something the installer stands up.
The signer runs a pre-release ed25519 library, and alloy sits on three minor lines
The Rust dependency table is where a reader should slow down, because this is the code that touches keys.
The signing and secret-handling crates are a sensible set: k256 with ecdsa for the EVM curve, ed25519-dalek for Solana, x25519-dalek with static_secrets and zeroize for key agreement, chacha20poly1305 for symmetric encryption, argon2 for password hashing, zeroize and bincode for serialisation and scrubbing, sha2 for hashing, and security-framework with the OSX_10_15 feature as the macOS Keychain binding.
Two entries deserve a second look.
ed25519-dalek is declared as 3.0.0-pre.6, with the rand_core feature. That is a pre-release of the signing library, resolved by default because a pre-release is what the requirement names. In a wallet-signing path, the version of the curve implementation is not a detail you would choose casually, and Cargo.lock will record whatever 3.0.0-pre.6 resolves to.
The alloy crates are on three different lines at once: alloy-chains at 0.2.33, alloy-dyn-abi at 0.8.26, and alloy-primitives and alloy-sol-types both at 0.8. Pre-1.0 crates carry no cross-crate semver guarantee, so a chains crate on 0.2 alongside primitives on 0.8 means two independent compatibility contracts in the same EVM dependency. It resolves today because Cargo.lock is committed, which is the right mitigation.
One pin is exact. The time crate is declared with a leading equals sign, holding it at 0.3.36. It is the only exact requirement in the workspace table, and pinning a date-and-time library exactly in a signing context is sensible.
The HTTP client is configured with default features disabled and rustls-tls enabled, so there is no OpenSSL dependency in the tree. That is a deliberate reproducibility choice visible in the manifest rather than inferred.
Linux auth storage needs a Secret Service, and zarf/ has no documentation at all
Two loose ends at the edges of the platform story.
The first is credential storage. On macOS the agent auth storage uses Keychain. On Linux it uses the Secret Service. Those are not equivalent. Keychain is an OS-provided store with no extra component. The Linux Secret Service is a D-Bus interface that some other program has to implement, so the requirement is really for gnome-keyring, KWallet or an equivalent running on the session bus. On a desktop session that is usually already there. On a headless server, a container without a session bus, or a systemd unit running without a user session, there is nothing to talk to, and the documented fallback is not given.
That matters because the managed wallet daemon is explicitly a systemd service on Linux, and a systemd unit is exactly the context where a user session Secret Service is often unavailable. The README pairs the two facts without connecting them. Anyone deploying the daemon non-interactively should confirm a provider exists before diagnosing a wallet failure.
The second loose end is a directory. The top level contains zarf/, alongside apps, packages, crates, skills, src, test, example, scripts and releases. Nothing in the readable documentation mentions zarf, what it packages or how it is used. For a repository whose central claim is that agents can move money, an undocumented deployment directory at the root is worth opening before you rely on the documented paths.
The rest of the tree is accounted for. LEGAL_DISCLAIMER.md and SECURITY.md sit at the root alongside CONTRIBUTING.md and a CHANGELOG.md, CLAUDE.md is correctly cased this time, biome.jsonc holds the lint configuration, turbo.json the task graph, and example/mpp/ holds four runnable machine-payment scripts against a testnet, including session and server variants.
Finally the version state. v0.5.0 shipped 2026-06-08 and the last push was the same day, so the tag matches the source. The branch has been quiet since, at 116 days before 2026-10-02.
Editorial conclusion
Adopt AgentPay if you need an agent to spend money with a human in the loop and you control the host, since the crypto path keeps signing local and the fiat path routes approval through the Link app. Do not script the install off wlfi.sh in a pinned or air-gapped pipeline, because that URL serves whatever is current rather than the version your lockfile names. Verify first on the target host that the Rust daemon actually builds and that a Secret Service provider exists on Linux, since auth storage depends on one and a headless server has none.
Frequently asked questions
What payment paths does the AgentPay SDK support?
Two. Fiat with Link, where the user binds a Link account and approves each spend request in the Link app, after which the agent receives a one-time card credential or a Link-backed machine-payment token. And crypto with a local wallet, where a self-custodial daemon constructs, signs and broadcasts transactions while keeping control of the wallet and approval path. The main entrypoint is the agentpay CLI.
Is the AgentPay SDK supported on Windows?
Only partially. The documentation states that Windows does not yet support the Rust daemon or runtime from source, and the crate workspace has no Windows transport, only vault-transport-unix and vault-transport-xpc. Windows users can install the JavaScript workspace with AGENTPAY_SKIP_RUST_INSTALL=1, which gives them the Link facade but not the self-custodial crypto path.
What does installing the AgentPay npm package actually do?
Beyond fetching JavaScript, it runs a postinstall hook that calls scripts/install-rust-binaries.mjs to place the Rust runtime into the agentpay home directory, because the wallet, signer and daemon are native. The tarball ships crates, Cargo.toml and Cargo.lock as well as the built JavaScript. AGENTPAY_SKIP_RUST_INSTALL=1 suppresses the hook where the daemon cannot be built.
Where does AgentPay store card credentials?
The agentpay link card command writes full card credentials to a local file with 0600 permissions, specified with --output-file, while stdout stays redacted. Approval happens in the Link app before any credential is usable, so the CLI holds no authority to spend on its own.
Which AI agents does the AgentPay installer configure?
Skill packs are installed for Codex, Claude, Cline, Goose, Windsurf, OpenClaw, a portable config directory and a legacy agents directory, with workspace adapters for AGENTS.md, CLAUDE.md, GEMINI.md, Copilot instructions, .clinerules and the Cursor rule pack. When no supported agent is detected, the installer offers the same integrations with all options enabled by default.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/worldliberty-agentpay-sdk)