← All posts

Selling npm packages on Moatless

November 4, 2026 · Zach Daniel

Not a software engineer? You don't really have to know this stuff! Your agent can figure out how to integrate the things you buy, and can help you with what makes sense for you to buy. This is for those who put code up for sale or for those who want to know how it works under the hood.

A few days ago, Moatless got its own Hex repository for Elixir packages. Now it has an npm registry too. If your codebase is a JavaScript or TypeScript package, you can sell it on Moatless, and buyers install it with npm install, like any other package. They can still clone the code with git, like any other listing.

For sellers

You need a scoped package name, a switch on your listing, and a tarball for each release.

1. A scoped name. Your package's name in package.json needs a scope, like @acme/kit. npm can only send a whole scope to another registry, not a single package, so a scope is how buyers' npm knows to ask Moatless for your package.

A scope belongs to the first seller to sell a package in it, and a package name belongs to the first listing to release it. Moatless won't take a scope that has public packages on npmjs, since buyers who pointed it at Moatless would stop finding those. So if you also publish free packages to npmjs under @acme, sell the paid ones under a scope of their own, like @acme-pro/kit.

2. Turn it on for your listing. On your listing's page under Sell, open Advanced and check npm packages. It's off by default.

3. Attach a tarball to each release. Most JavaScript packages are built before they're published, and Moatless never runs your code, so it doesn't build your package for you. Instead, you build it the way you always do, run npm pack, and give Moatless the .tgz file that makes:

  • If your listing is built from GitHub, attach the tarball to the GitHub release for each release's tag. A CI step that runs npm pack and uploads the result is the easiest way. Moatless copies it when the release is published, and checks for new files for half an hour afterwards.
  • If your listing is built from another git host, upload the tarball to the release on your listing's page, with Upload files. Your agent can upload it for you too.

Moatless reads the package.json in the tarball, without running anything in it, and your listing's page shows buyers how to install it. If a tarball isn't a package it can serve, for example because its name has no scope, you'll see why on your listing's page.

A few things worth knowing:

  • Raise your version for each release. Buyers' lockfiles pin each version's checksum, so a version can't be released again with different contents, even if you remove it first. Attaching the exact same tarball to a later release is fine.
  • One listing can have several packages. Each tarball attached to a release is its own package, so a codebase that ships @acme/kit and @acme/kit-react can sell both.
  • Which versions buyers get. Subscribers get every version you publish. Buyers who paid once get the versions up to the release they bought. As with every listing on Moatless, subscribers are only charged for the months in which you publish a release.
  • Your agent can set it up. The Moatless skill knows about this setting, so your coding agent can turn it on when it creates the listing, and pack and upload your tarballs.

Release assets for every listing

The uploads above work for any file, not just npm tarballs. Release assets, the files buyers can download alongside the code, such as compiled binaries, used to come only from GitHub releases. Now any listing can include them. If your listing isn't built from GitHub, check Include release assets and upload the files to each release. Each file can be up to 250 MB, and a release can have up to 20.

For buyers

Once you've bought a package, you point its scope at Moatless's registry once, and then install it like any other package.

1. Create an API key in your Moatless settings.

2. Point the scope at Moatless. Add these lines to your project's .npmrc, with the scope from the listing's page, which has the exact lines:

@acme:registry=https://moatless.store/npm/
//moatless.store/npm/:_authToken=${MOATLESS_API_KEY}

The key comes from an environment variable, so it never ends up in the file or in your repository. npm, pnpm, Bun and Yarn 1 read .npmrc as it is. Yarn 2 and later take the same settings in .yarnrc.yml:

npmScopes:
  acme:
    npmRegistryServer: "https://moatless.store/npm/"
    npmAuthToken: "${MOATLESS_API_KEY}"

3. Install it:

npm install @acme/kit

Its own dependencies come from npmjs, as usual. If you haven't bought the package, or your key isn't set, npm tells you so, along with what to do about it.

Updates. New versions you're entitled to show up like any other package update, with npm update @acme/kit. If you subscribe, that's every new version. If you bought it once, it's the versions up to the release you bought, and buying it again gets you the newest.

CI and deploys. Your build needs the registry too, so commit the .npmrc and set MOATLESS_API_KEY in your CI's secrets. You can revoke a key from your settings at any time.

Happy Hacking!

  • Zach