Appearance
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
| Store | Role |
|---|---|
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/globalmx 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.jsonAtomicity
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
- Installing → Install from registry
- Installing → Install from local folder
- Installing → Uninstall
- Local Development → Installed source
- Reference → CLI reference
Source:
projects/matrix-3/packages/mx-cli/src/utils/package-store.tsdefines paths andlistInstalledPackageNames;packages/hivecast/bin/hivecast.mjs:205-216is the seed step.