---
title: Coming from Nx
description: A terminology map and porting sketch for a team moving a monorepo workspace from Nx to magus, with an honest list of what each tool has that the other does not.
tags: [nx, migration, monorepo, terminology, comparison, porting]
---

# Coming from Nx

This page is for a team that already knows [Nx](https://nx.dev) and wants to
map that mental model onto magus. Nx and magus solve the same core problem -
a task graph, an affected set, and a content-addressed cache for a monorepo -
with different philosophies: **Nx infers**, reading `package.json`/`project.json`
and plugin conventions to build its graph; **magus declares**, reading exactly
what a magusfile says and nothing it guesses. Nothing below is written to make
either tool look bad; where Nx has something magus does not, that is stated
plainly, and vice versa.

## Terminology map

| Nx                                            | magus                                                                                                                                                                               |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| workspace (`nx.json`)                         | workspace (`magus.yaml`)                                                                                                                                                            |
| project (`project.json` / `package.json`)     | project (a directory whose `magusfile.buzz` registers it)                                                                                                                           |
| target (`project.json` `targets`)             | target (an exported `fun` in the magusfile; seven canonical names plus custom - see [targets.md](../concepts/targets.md#the-target-name))                                                       |
| executor / plugin                             | spell op (a [spell](../concepts/spells.md) is a library of tool-native ops)                                                                                                                     |
| `nx:run-commands`                             | `os\exec(...)` in a target body                                                                                                                                                     |
| `dependsOn: ["^build"]`                       | `ctx.needs(...)` (target-level; the `^`-upstream semantics come from `depends_on` plus same-target ordering - see [dependencies.md](../concepts/dependencies.md))                             |
| `implicitDependencies`                        | `depends_on` in `magus\project`                                                                                                                                                     |
| `inputs` / `namedInputs`                      | a spell's `needs` globs, plus a project's own [`sources`](../concepts/workspace.md#magusproject-layering-policy)                                                                                |
| `outputs`                                     | `outputs` / a spell's `provides` globs                                                                                                                                              |
| `targetDefaults[t].cache: true`               | nothing - caching is already on; see [Caching is on by default](#caching-is-on-by-default-there-is-nothing-to-opt-into)                                                              |
| `targetDefaults[t].cache: false`              | the `skip_cache` target policy, but only when replay would be _wrong_ (see [cache.md](../concepts/cache.md#opting-out-and-busting))                                                  |
| `--skip-nx-cache`                             | `--no-cache` (one invocation; still snapshots afterward)                                                                                                                            |
| `nx affected`                                 | `magus affected`                                                                                                                                                                    |
| `nx graph`                                    | `magus graph` / `magus affected --graph` / `magus graph open`                                                                                                                       |
| Nx Cloud remote cache (Nx Replay)             | [magus remote cache](../concepts/cache/remote.md) (self-hosted backends, Ed25519-signed artifacts)                                                                                              |
| Nx Cloud DTE / Nx Agents                      | `magus affected --plan` (a provider-neutral JSON shard plan; you bring the runners)                                                                                                 |
| generators / scaffolding                      | fixed, not extensible: `magus init` writes a starter magusfile, `magus init spell <name>` scaffolds one spell stub; there is no generator framework for custom, pluggable scaffolds |
| task pipeline (`targetDefaults` in `nx.json`) | composed `magus\needs` calls in the magusfile                                                                                                                                       |

## Model differences

**Config is code, not JSON.** A magusfile is [Buzz](../concepts/engines.md), a small
embedded scripting language, not a JSON/YAML document a plugin interprets.
There is no schema to look up; a target is a function, and its dependencies
are calls you can trace by reading top to bottom.

**Explicit declarations, not plugin inference.** Nx plugins read your
`package.json`/config files and infer targets, inputs, and dependencies for
you - powerful, but the inference is only as good as the plugin's
understanding of your setup. magus caches exactly what you declare: a spell's
`needs` and a project's `depends_on` are the whole story, and under-declaring
an input is the one way to get a stale cache hit (see
[dependencies.md](../concepts/dependencies.md#caching-interplay)). Nothing is inferred
from source-code analysis.

### Caching is on by default; there is nothing to opt into

In Nx, caching is a per-target opt-in: a target caches because something set
`"cache": true` for it in `targetDefaults`, and forgetting that is why a task
you expected to be instant runs in full. magus inverts this. Every target
caches, keyed by the inputs it declares, with no policy to write. Arriving from
Nx, the instinct is to hunt for where caching gets switched on. There is no such
switch, and its absence is the feature: `magus affected ci` is fast because you
declared inputs correctly, not because you remembered a flag.

The mirror-image trap matters more. `cache: false` in Nx is an ordinary
performance or correctness dial people reach for freely, and its nearest-looking
neighbor here is the `skip_cache` target policy. They are not equivalents.
`skip_cache` is a claim that _replaying this target would produce a wrong
result_ - it signs a fresh artifact, records a screen capture, mutates
`go.mod`, or never returns. Reaching for it because a target "should feel fresh"
disables replay permanently, for every user, on every machine.

If you only distrust the cache for one run, that is `--no-cache`, which still
refreshes the entry afterward. And if a target seems to need `skip_cache`
because it produces no files, it does not: a pure orchestration target caches
correctly with no policy at all. See
[Opting out and busting](../concepts/cache.md#opting-out-and-busting) for the
full set of controls and their scopes.

**A canonical target vocabulary, not free-form names.** Nx targets are
whatever string a plugin or `project.json` names them. magus has seven
canonical names (`build`, `test`, `lint`, `format`, `clean`, `generate`,
`preflight`) plus `ci`, with a stated [litmus test](../concepts/targets.md#when-does-a-name-earn-canonical-status)
for adding an eighth - custom names are allowed, but the vocabulary is
deliberately small so `magus run lint` means the same thing in every project.

**Read-only by default, not mutate-by-default.** Every magus run is read-only
unless you add the `rw` charm (`magus run format:rw`); Nx targets run
whatever their executor does, with no equivalent default-safe mode.

**Sandboxed execution.** On Linux, magus confines spell subprocesses with the
kernel's landlock LSM (see [sandbox.md](../concepts/sandbox.md)); Nx has no equivalent
process-level sandbox.

**Single static binary, no Node runtime required.** magus is a compiled Go
binary; running it does not require Node, npm, or any JS toolchain, even for a
non-JS workspace. Nx is an npm package and requires Node to run.

## What Nx has that magus does not

Said plainly, no hedging:

- A large plugin ecosystem and community, covering most popular frameworks
  out of the box.
- An extensible generator framework (`nx generate`, local generators) for
  scaffolding new projects and files with custom, pluggable templates - magus
  has only the two fixed scaffolds above, not a generator system.
- First-party editor extensions (Nx Console) with rich UI for running tasks
  and visualizing the graph.
- Nx Cloud's managed distributed task execution (Nx Agents) and a flaky-task
  retry service, as a hosted product.
- Years of production maturity across a very wide range of ecosystems.

## What magus has that Nx does not

- A signed remote-cache trust model: every remote artifact carries a
  detached Ed25519 signature verified against a configured trust set, not
  just an access-token-gated store (see [remote-cache.md](../concepts/cache/remote.md)).
- Kernel-level sandboxing of spell subprocesses on Linux (landlock).
- Services as a first-class declarative op kind, with readiness probes,
  shared-instance dedup, and idle teardown (see [services.md](../concepts/services.md)),
  rather than a `run-commands` invocation of a script you write yourself.
- A [knowledge graph](../concepts/knowledge.md) and MCP agent surface: `magus query` /
  `explain` / `path` let an agent (or you) navigate the project/target/spell
  domain instead of grepping.
- [Volatility detection](../concepts/volatility.md): magus tracks and reports
  non-deterministic ("flaky") targets from run history, distinct from a
  hosted retry service.
- Language-neutral single binary, no Node runtime dependency.

## A porting sketch

An Nx `project.json` composing a build that depends on its upstream's build,
with declared inputs and outputs:

```json
{
  "targets": {
    "build": {
      "executor": "@nx/js:tsc",
      "dependsOn": ["^build"],
      "inputs": ["{projectRoot}/src/**/*.ts", "{projectRoot}/tsconfig.json"],
      "outputs": ["{projectRoot}/dist"]
    },
    "test": {
      "executor": "@nx/vite:test",
      "inputs": ["{projectRoot}/src/**/*.ts", "{projectRoot}/vite.config.ts"]
    },
    "lint": {
      "executor": "@nx/eslint:lint"
    }
  }
}
```

The equivalent magusfile, in the same project directory - the upstream
dependency is declared once, at the target that needs it, via a project
import (see [Dependencies](../concepts/dependencies.md#the-fold-a-cross-project-needs-also-declares-depends_on)):

```buzz
import "magus";
import "project/../shared-lib" as shared;
import "magus/spell/ts";

magus\project({ "spells": [ts] });

export fun build(ctx: magus\Context, args: [str]) > void {
    ctx.needs(shared.build);   // folds into depends_on automatically
    ts["tsc-build"](ctx);
}

export fun test(ctx: magus\Context, args: [str]) > void { ts["vitest"](ctx); }

// tsc composes into lint alongside eslint - not a bespoke `typecheck` target.
export fun lint(ctx: magus\Context, args: [str]) > void {
    ts["tsc"](ctx);
    ts["eslint"](ctx);
}

export fun ci(ctx: magus\Context, args: [str]) > void {
    ctx.needs(build, test, lint);
}
```

`ts["tsc-build"]`'s `needs`/`provides` globs and `ts["eslint"]`'s claimed
files are already declared by the spell - see the [`ts` spell reference](../concepts/spells/ts.md)
for the full op list, and [Getting started](../guides/getting-started.md) for a
from-scratch walkthrough.

## See also

- [Getting started](../guides/getting-started.md): install to first `ci` pipeline, magus-native.
- [Dependencies](../concepts/dependencies.md): the `magus\needs` / `depends_on` model this page's `dependsOn` row maps to.
- [Remote caching](../concepts/cache/remote.md): the signed trust model behind the Nx Cloud comparison row.
- [Spells](../concepts/spells.md): the executor/plugin equivalent, and the built-in spell list.
