Appearance
Metadata
Each package version has one record in discovery-index.json. This page is the field-by-field definition of that record as it is read by SystemPackageRegistryActor.
Top-level shape
typescript
// SystemPackageRegistryActor.ts:21-51
interface IPackageDiscoveryMetadata {
packageName: string; // required, e.g. "@open-matrix/chat"
version: string; // required, e.g. "0.2.3"
displayName?: string; // human-friendly label
description?: string; // one-line summary
inferredKind?: string; // "app" | "webapp" | "service" | "library"
declaredKind?: string; // package-asserted kind, takes precedence
tags: readonly string[]; // free-form tags
capabilities: readonly string[]; // capability strings (see capabilities page)
webapp?: WebappDeclaration; // only if package has a browser surface
components: readonly Component[]; // every actor/component declared
}If packageName or version is missing or blank, the entry is dropped at read time (readMetadata returns null).
Webapp declaration
typescript
interface WebappDeclaration {
appName?: string; // URL segment, e.g. "chat" → /apps/chat/
displayName?: string;
description?: string;
shells: readonly string[]; // ["platform"], ["edge"], ["developer"], or combinations
icon?: string;
routePrefix?: string; // typically "/apps/<appName>/"
}A package with no browser surface omits webapp entirely; it does not have an empty object.
Component shape
typescript
interface Component {
type?: string; // class name, e.g. "ChatActor"
exportName?: string; // exported binding for the loader
mount?: string; // logical mount, e.g. "chat"
surface?: string; // "headless", "browser", "ui"
description?: string;
kind?: string; // optional explicit kind hint
accepts: readonly string[]; // op names handled
emits: readonly string[]; // event names published
skills: readonly string[]; // higher-level skill bundles
tags: readonly string[];
capabilities: readonly string[];
}Components are ordered as they appear in matrix.json.components[]. The order is preserved through indexing and is visible to search results.
Where each field comes from
The publish-time index updater extracts these fields from a package's matrix.json and package.json:
| Field | Source | Notes |
|---|---|---|
packageName | package.json.name | Authoritative. |
version | package.json.version | Authoritative. |
displayName | matrix.json.displayName | Falls back to packageName if absent. |
description | matrix.json.description ?? package.json.description | Either is acceptable. |
inferredKind | computed from manifest signals | "webapp" if webapp present, "service" if components have surface: headless, etc. |
declaredKind | matrix.json.kind | Optional. Wins over inferredKind when set. |
tags | matrix.json.tags[] ∪ package.json.keywords[] | Deduplicated. |
capabilities | matrix.json.capabilities[] | See Capabilities. |
webapp.* | matrix.json.webapp | See App surfaces. |
components[] | matrix.json.components[] | Each component's static accepts, emits, skills, tags, capabilities are materialized. |
Required fields
Three fields are required for a package to appear in search results:
packageNameversioninferredKind(becausereadPackageMetadatarejects entries with a blankinferredKind)
The publish pipeline fills inferredKind from manifest signals; manually authored entries that omit it disappear.
Field-level vs. component-level
Note the duplication in accepts/emits/skills:
- Component-level values are first-class. Searches against
accepts: 'chat.*'match component-level values. - Top-level package
tags/capabilitiesare searched separately; they are not aggregated from component values.
This is intentional: a package can declare aspirational capabilities at the top level even if no single component emits them, and component-level values remain precise descriptions of an individual actor's surface.
See also
Source:
projects/matrix-3/packages/system-catalog/src/SystemPackageRegistryActor.ts:21-51.