Skip to main content



An extension is a WASM plugin that allows you to extend moon with additional functionality, have whitelisted access to the file system, and receive partial information about the current workspace. Extensions are extremely useful in offering new and unique functionality that doesn't need to be built into moon's core. It also enables the community to build and share their own extensions!

Using extensions

Before an extension can be executed with the moon ext command, it must be configured with the extensions setting in .moon/workspace.yml (excluding built-in's).

plugin: 'source:'

Once configured, it can be executed with moon ext by name. Arguments unique to the extension must be passed after a -- separator.

$ moon ext example -- --arg1 --arg2

Built-in extensions

moon is shipped with a few built-in extensions that are configured and enabled by default. Official moon extensions are built and published in our moonrepo/moon-extensions repository.


The download extension can be used to download a file from a URL into the current workspace, as defined by the --url argument. For example, say we want to download the latest proto binary:

$ moon ext download --\

By default this will download proto_cli-aarch64-apple-darwin.tar.xz into the current working directory. To customize the location, use the --dest argument. However, do note that the destination must be within the current moon workspace, as only certain directories are whitelisted for WASM.

$ moon ext download --\
--dest ./temp


  • --url (required) - URL of a file to download.
  • --dest - Destination folder to save the file. Defaults to the current working directory.
  • --name - Override the file name. Defaults to the file name in the URL.


This extension is currently experimental and will be improved over time.

The migrate-nx extension can be used to migrate an Nx powered repository to moon. This process will convert the root nx.json and workspace.json files, and any project.json and package.json files found within the repository. The following changes are made:

  • Migrates targetDefaults as global tasks to .moon/tasks/node.yml (or bun.yml), namedInputs as file groups, workspaceLayout as projects, and more.
  • Migrates all project.json settings to moon.yml equivalent settings. Target to task conversion assumes the following:
    • Target executor will be removed, and we'll attempt to extract the appropriate npm package command. For example, @nx/webpack:build -> webpack build.
    • Target options will be converted to task args.
    • The {projectRoot} and {workspaceRoot} interpolations will be replaced with moon tokens.
$ moon ext migrate-nx

Nx and moon are quite different, so many settings are either ignored when converting, or are not a 1:1 conversion. We do our best to convert as much as possible, but some manual patching will most likely be required! We suggest testing each converted task 1-by-1 to ensure it works as expected.


  • --bun - Migrate to Bun based commands instead of Node.js.


The following features are not supported in moon, and are ignored when converting.

  • Most settings in nx.json.
  • Named input variants: externel dependencies, dependent task output files, dependent project inputs, or runtime commands.
  • Target configurations and defaultConfiguration. Another task will be created instead that uses extends.
  • Project root and sourceRoot.


The migrate-turborepo extension can be used to migrate a Turborepo powered repository to moon. This process will convert the root turbo.json file, and any turbo.json files found within the repository. The following changes are made:

  • Migrates pipeline global tasks to .moon/tasks/node.yml (or bun.yml) and project scoped tasks to moon.yml. Task commands will execute package.json scripts through a package manager.
  • Migrates root global* settings to .moon/tasks/node.yml (or bun.yml) as implicitInputs.
$ moon ext migrate-turborepo


  • --bun - Migrate to Bun based commands instead of Node.js.

Creating an extension

Refer to our official WASM guide for more information on how our WASM plugins work, critical concepts to know, how to create a plugin, and more. Once you have a good understanding, you may continue this specific guide.


Refer to our moonrepo/moon-extensions repository for in-depth examples.

Implementing execution

Extensions support a single plugin function, execute_extension, which is called by the moon ext command to execute the extension. This is where all your business logic will reside.

use extism_pdk::*;
use moon_pdk::{*, extension::*};

extern "ExtismHost" {
fn host_log(input: Json<HostLogInput>);

pub fn execute_extension(Json(input): Json<ExecuteExtensionInput>) -> FnResult<()> {
host_log!(stdout, "Executing extension!");


Supporting arguments

Most extensions will require arguments, as it provides a mechanism for users to pass information into the WASM runtime. To parse arguments, we provide the Args trait/macro from the clap crate. Refer to their official documentation on usage (we don't support everything).

use moon_pdk::args::*;

pub struct ExampleExtensionArgs {
// --url, -u
#[arg(long, short = 'u', required = true)]
pub url: String,

Once your struct has been defined, you can parse the provided input arguments using the parse_args function.

pub fn execute_extension(Json(input): Json<ExecuteExtensionInput>) -> FnResult<()> {
let args = parse_args::<ExampleExtensionArgs>(&input.args)?;

args.url; // --url


Supporting configuration

Users can configure extensions with additional settings in .moon/workspace.yml. Do note that settings should be in camelCase for them to be parsed correctly!

plugin: 'source:./path/to/example.wasm'
someSetting: 'abc'
anotherSetting: 123

In the plugin, we can map these settings (excluding plugin) into a struct. The Default trait must be implemented to handle situations where settings were not configured, or some are missing.

pub struct ExampleExtensionConfig {
pub some_setting: String,
pub another_setting: u32,

Once your struct has been defined, you can access the configuration using the get_extension_config function

pub fn execute_extension(Json(input): Json<ExecuteExtensionInput>) -> FnResult<()> {
let config = get_extension_config::<ExampleExtensionConfig>()?;

config.another_setting; // 123