Appearance
Package vs actor
A package and an actor live at different layers of Matrix and have different lifecycles. Package authors who collapse them invariably get stuck on naming, mounts, and registration.
The two definitions, side by side
| Aspect | Package | Actor |
|---|---|---|
| Layer | Distribution | Runtime bus |
| Lives in | A directory + tarball | A process / browser tab |
| Identity | npm name (@open-matrix/chat) | Mount path (chat.conversation) |
| Stored in | Registry, package store on disk | The bus address space |
| Lifecycle | install → uninstall | mount → unmount |
| Plural | A directory contains 0..n actor classes | A package may mount 0..n actor instances |
| Versioned by | semver (package.json:version) | implicit in package version + actor contract |
A package is what you publish. An actor is what runs.
Where the bridge lives in matrix.json
The bridge from package to actor is the components array. Each entry declares one actor class the package exports and where it mounts:
json
{
"type": "ChatConversationProxy",
"export": "ChatConversationProxy",
"mount": "conversation",
"surface": "headless",
"autoStart": true
}Validator rules from manifestValidator.ts:158-244:
componentsMUST be an array (may be empty)typeis required, non-empty stringexportis optional but must be a non-empty string when providedmountis optional but must be a non-empty string when providedautoStartis booleanpropsis an objectfsPolicy(per-component override) is one ofnone | matrix-only | project-readonly | project-readwrite | global-readonly
The mount in matrix.json is relative. The full bus address comes from prepending the package mount root (or the runtime root) at mount time — see Authoring → Actor declarations.
A concrete example: chat
projects/matrix-3/packages/chat/matrix.json is one package, but it declares nine actor components, each mounted under a different relative name (security-realm, topic-claims, conversation, component-library, identity, preferences, session-state, mcp, export). The package is @open-matrix/chat. The actors are nine different bus participants. They all ship together because they collaborate.
A concrete counter-example: a one-actor package
A package may declare zero actor components if it is a web app whose client-side custom elements are the actors:
json
{
"name": "@open-matrix/director",
"components": [],
"webapp": { ... }
}The browser tab mounts the actors itself when the user loads /apps/director/. The headless runtime here has nothing to do.
Practical implications for authors
- Name your package once, name your actors locally. The package name is global; component mounts are relative inside the package namespace.
- A new actor does not justify a new package. Add a
components[]entry instead. Making a new package is appropriate when the actors serve a different audience, ship on a different cadence, or have a different dependency footprint. - The npm version is the package version. Actors are versioned only through the package and through additive contract evolution (CLAUDE.md Rule 9: never remove fields, only add optional ones).
See also
- What is a Matrix package?
- Authoring → Actor declarations
- Authoring → Package manifest
- Reference → Manifest schema
Source:
projects/matrix-3/packages/mx-cli/src/utils/manifestValidator.tsvalidates thecomponents[]shape; sample manifests inpackages/system/matrix.jsonandpackages/chat/matrix.json.