Skip to content

Package store

The package store is where installed packages live on disk. Two stores exist per Host home: a system store and a global store. Both follow npm's node_modules layout so existing tooling (Node.js module resolution, IDE jumping) works unchanged.

Layout

<MATRIX_HOME>/                  ← e.g. /tmp/matrix-home or ~/.matrix
├── host.json
├── credentials/
├── runtimes/
├── logs/
├── bin/
└── packages/
    ├── system/
    │   └── node_modules/
    │       └── @open-matrix/
    │           ├── system/
    │           ├── system-gateway-http/
    │           ├── host-control/
    │           ├── system-runtimes/
    │           ├── system-platform/
    │           ├── system-bindings/
    │           ├── system-auth/
    │           └── core/
    └── global/
        └── node_modules/
            └── @open-matrix/
                ├── chat/
                ├── director/
                ├── flowpad/
                ├── inference-settings/
                ├── matrix-edge/
                ├── matrix-web/
                └── smithers/

Source: packages/mx-cli/src/utils/package-store.ts:13-22.

Two stores, two roles

StoreRole
packages/system/node_modules/Packages required for the Host itself to function. Seeded by hivecast install. Removing a package here breaks the Host.
packages/global/node_modules/User-installable packages. The default target for mx install. Safe to add/remove with mx install/mx uninstall.

hivecast install seeds both (packages/hivecast/bin/hivecast.mjs:205-216:seedBundledHostPackages):

ts
function seedBundledHostPackages(matrixHome) {
  const systemNodeModules = join(matrixHome, "packages", "system", "node_modules");
  const globalNodeModules = join(matrixHome, "packages", "global", "node_modules");
  for (const packageName of listBundledPackageNames()) {
    copyBundledPackageToStore(packageName, systemNodeModules);
    copyBundledPackageToStore(packageName, globalNodeModules);
  }
}

That double seeding is deliberate: the same package can be loaded through system or global resolution depending on which runtime asks for it. The Host process itself resolves through the system store; user-installed packages resolve through the global store.

Defaults

packages/mx-cli/src/utils/package-store.ts:13-14:

ts
const DEFAULT_GLOBAL_PACKAGES_ROOT = path.join(os.homedir(), ".matrix", "packages");
const DEFAULT_GLOBAL_REGISTRY_ROOT = path.join(os.homedir(), ".matrix", "registry");

Override with --packages-dir <path> per CLI command, or MATRIX_HOME environment variable.

Reading the store

bash
# List installed packages
mx list --packages-dir /tmp/matrix-home/packages/global

# Inspect one package
mx info @open-matrix/chat --packages-dir /tmp/matrix-home/packages/global

mx list (packages/mx-cli/src/commands/list.ts) walks <packages-dir>/node_modules/ and prints every directory it finds, honoring the @scope/name convention.

mx info reads the resolved matrix.json and reports:

@open-matrix/chat
path: /tmp/matrix-home/packages/global/node_modules/@open-matrix/chat
manifest: matrix.json

Atomicity

mx install writes to a unique staging dir, renames it into the target, and only removes the backup of the previous version after the lifecycle hooks complete (packages/mx-cli/src/commands/install.ts:341-408). Concurrent installs of the same package collide on the rename and one will fail; reads are not blocked by ongoing installs.

A failure during install restores the backup. A failure between "backup created" and "stage renamed" leaves both directories present until the OS cleans them up; the restore logic handles all relevant orderings.

Don't hand-edit

CLAUDE.md "ONE DEPLOY PATH": never hand-copy into the package store. Always go through mx install, hivecast install, or hivecast operator refresh-host-package. Manual copies skip lifecycle hooks and leave the install state inconsistent with what mx info and system.runtimes report.

See also

Source: projects/matrix-3/packages/mx-cli/src/utils/package-store.ts defines paths and listInstalledPackageNames; packages/hivecast/bin/hivecast.mjs:205-216 is the seed step.