Appearance
Package storage
The Host has two on-disk package stores, both node_modules-style:
ts
// projects/matrix-3/packages/host-service/src/types.ts:11-14
interface IHostPackageStorageConfig {
readonly globalDir: string; // default "packages/global"
readonly systemDir: string; // default "packages/system"
}Resolved as <home>/packages/global/node_modules/... and <home>/packages/system/node_modules/....
Why two stores
| Store | Owner | Purpose | Survives hivecast install? |
|---|---|---|---|
system | the wrapper | Bundled defaults: system, host-control, gateway, web, edge, director, chat, inference-settings, flowpad, smithers | reseeded every install |
global | the user | Anything installed via matrix install <ref> | preserved across installs |
The wrapper's hivecast install and hivecast seed write into system/ only; user installs go into global/. This means an upgrade (apt upgrade hivecast or hivecast install on top of an existing home) refreshes the bundled defaults without touching the user's add-ons.
Resolution order
When matrix up <package-ref> (or any cli.ts resolver) needs a package directory, it tries — in this order (projects/matrix-3/packages/host-service/src/cli.ts:432-449):
<home>/packages/global/node_modules/<package-ref>/package.json<home>/packages/system/node_modules/<package-ref>/package.jsonrequire.resolve('<package-ref>/package.json')(Node module resolution from the Host's ownnode_modules— this is how dev checkouts work)
The <package-ref> can be @open-matrix/foo, @matrix/foo, or just foo. Aliases in packageTargetRefs (cli.ts:416-430) translate ambiguous targets:
system->@open-matrix/systemchat->@open-matrix/chat->@matrix/chat->chat@open-matrix/foo->@matrix/foo(and vice versa)
This forgives operators who type the wrong scope.
Layout
<home>/packages/system/
node_modules/
@open-matrix/
system/ # the system package
package.json
matrix.json
dist/
host-control/
system-gateway-http/
matrix-web/
matrix-edge/
director/
chat/
inference-settings/
flowpad/
smithers/
<home>/packages/global/
node_modules/
@open-matrix/
claude-agent/ # installed by `matrix install`
codex-agent/
@some-org/
custom-package/The wrapper's hivecast seed rsync's dist/node_modules/ into packages/system/. User matrix install writes into packages/global/node_modules/ via the Matrix package CLI.
Required package shape
For the Host to start a package as a default runner, the package must contain at least one of (assertPackageSupportsDefaultRunCommand, cli.ts:451-465):
matrix.service.json— declares a service-style runtime entry pointmatrix.jsonwith awebappblock — declares a browser-served app
If neither exists, matrix up refuses with:
Cannot start "<target>" with the default Host runner because <packageDir>
does not declare matrix.service.json or matrix.json webapp metadata.
Install or build a Host-runnable Matrix package, or pass an explicit
runtime command after --.You can still spawn arbitrary processes by passing -- followed by the exact command:
bash
matrix up /path/to/package -- node my-runner.js --port 8080The Host's role is supervision, not opinion: if you give it a command, it runs it.
Layout creation
HostStateStore.ensureLayout (host-state-store.ts:55-68) creates these directories on every start and init. They will exist as empty node_modules-shaped trees even on a freshly-init'd home; populating them is the wrapper's or user's job.
Customizing storage paths
json
// host.json
{
"packageStorage": {
"globalDir": "vendor/global",
"systemDir": "vendor/system"
}
}Both are joined relative to <home>. Absolute paths are not supported. Cross-Host package sharing is not a feature; if you want shared packages, use a shared parent <home>.