Manifest
Manifest protocol schemas
Source: packages/spec/src/kernel/manifest.zod.ts
TypeScript Usage
import { ManifestSchema, ManifestPermissionsSchema, PluginEnginesSchema, PluginIntegritySchema, PluginPackagingSchema, PluginPermissionsSchema, PluginRuntimeSchema } from '@objectstack/spec/kernel';
import type { ManifestPermissions, PluginEngines, PluginIntegrity, PluginPackaging, PluginPermissions, PluginRuntime } from '@objectstack/spec/kernel';
// Validate data
const result = ManifestSchema.parse(data);Manifest
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| id | string | ✅ | Unique package identifier (reverse domain style) |
| namespace | string | optional | Short namespace identifier; also the mandatory prefix of every object name (e.g. "todo" → object names "todo_task", "todo_project") |
| defaultDatasource | string | optional (default: "default") | Default datasource for all objects in this package |
| version | string | ✅ | Package version (semantic versioning) |
| type | Enum<'plugin' | 'ui' | 'driver' | 'server' | 'app' | 'theme' | 'agent' | 'objectql' | 'module' | 'gateway' | 'adapter'> | ✅ | Type of package |
| scope | Enum<'cloud' | 'system' | 'project'> | optional (default: "project") | Deployment scope: cloud | system | project |
| name | string | ✅ | Human-readable package name |
| description | string | optional | Package description |
| permissions | string[] | { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] } | optional | Required permissions: legacy string[] or structured plugin block (ADR-0025 §3.2) |
| objects | string[] | optional | Glob patterns for ObjectQL schemas files |
| datasources | string[] | optional | Glob patterns for Datasource definitions |
| dependencies | Record<string, string> | optional | Package dependencies |
| configuration | never | optional | [REMOVED] manifest.configuration was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read the block: no settings UI rendered it and no loader resolved a setting from it, so authoring it configured nothing. Worse, properties.*.secret promised "value is encrypted/masked (e.g. API Keys)" while nothing encrypted, masked or even parsed the flag — a false assurance about credential handling. Delete the key. A plugin is configured by the host that composes it: pass options to its constructor in defineStack({ plugins: [new MyPlugin({ … })] }), which is the enforced channel. A declarative settings surface must be designed with an enforcing reader first, not revived here. |
| contributes | { kinds?: object[] } | optional | Platform contributions |
| data | { object: string; externalId?: string | string[]; mode?: Enum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>; env?: Enum<'prod' | 'dev' | 'test'>[]; … }[] | optional | Initial seed data (prefer top-level data field) |
| capabilities | never | optional | [REMOVED] manifest.capabilities was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — no discovery path ever consulted the block: nothing read implements, provides, requires, extensionPoints or extensions, so the declared "interoperability and automatic discovery" never happened. Delete the key. Real dependency resolution runs off top-level manifest.dependencies, which stays. Capability-based discovery must be designed with an enforcing reader first, not revived here. |
| extensions | never | optional | [REMOVED] manifest.extensions was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — an untyped map with zero readers: whatever was parked here was stored and never consulted. Delete the key. Extend the platform through the enforced channels instead: contributes.kinds registers metadata kinds, navigationContributions injects navigation into other packages' apps, and code-level extension happens in the plugin itself (init/start). |
| navigationContributions | { app: string; group?: string; priority?: integer; items: (object | … +8 more)[] }[] | optional | Navigation items this package contributes into apps owned by other packages |
| loading | never | optional | [REMOVED] manifest.loading was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — the entire block (strategy, preload, codeSplitting, dynamicImport, initialization, dependencyResolution, hotReload, caching, sandboxing, monitoring) had no runtime reader in any repo, so authoring it configured nothing. Delete the key. Plugins are composed at boot — defineStack registers them and the kernel runs init then start in an order topologically resolved from each composed plugin's own dependencies / optionalDependencies (resolvePluginOrder); the set is fixed until the process restarts. ⚠️ loading.sandboxing in particular never isolated anything: it did not run plugins in a process, vm, iframe or web-worker, and allowedServices gated no call. If you were relying on it for isolation, you had none — and the plugin trust tier (manifest.runtime) does not give it back: that tier is enforced at the cloud marketplace PUBLISH gate only (an unverified publisher requesting the node tier is rejected with HTTP 422 and forced to manual review), while load-side enforcement is NOT implemented, so a locally installed plugin is not isolated by the tier it declares. Use the permission declarations, which are enforced. |
| engine | { objectstack: string } | optional | Platform compatibility requirements (legacy; superseded by engines) |
| engines | { platform?: string; protocol?: string } | optional | Plugin compatibility ranges (ADR-0025 §3.2; supersedes engine) |
| runtime | Enum<'node' | 'sandbox' | 'worker'> | optional | Plugin trust tier the plugin declares (ADR-0025 §3.6) — enforced at the cloud marketplace publish gate (unverified publisher requesting node → HTTP 422 + manual review); load-side enforcement is NOT implemented, so a locally installed plugin is not isolated by the tier it declares |
| packaging | Enum<'bundled' | 'manifest-deps'> | optional | Dependency packaging strategy (ADR-0025 §3.3) |
| integrity | Record<string, string> | optional | Per-file content digests of the plugin artifact (ADR-0025 §3.2) |
Nested Shape: Manifest.permissions
Structured plugin permission grants (ADR-0025 §3.2)
| Property | Type | Required | Description |
|---|---|---|---|
| services | string[] | optional | Platform services the plugin may resolve (e.g. "object", "http") |
| hooks | string[] | optional | Lifecycle hooks the plugin may register (e.g. "record.beforeInsert") |
| network | string[] | optional | Network hosts the plugin may reach (e.g. "api.acme.com") |
| fs | string[] | optional | Filesystem paths the plugin may access |
Nested Shape: Manifest.contributes
| Property | Type | Required | Description |
|---|---|---|---|
| kinds | { id: string; description?: string }[] | optional | Metadata kind identifiers this package registers |
| events | never | optional | [REMOVED] manifest.contributes.events was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read the list: its only in-repo author already subscribed imperatively in plugin code, so the declaration was decorative. Delete the key. Subscribe to system events in the plugin itself — ctx.hook('kernel:ready', …) (or the events service) from init/start is the enforced channel; record lifecycle hooks register on the data engine. |
| menus | never | optional | [REMOVED] manifest.contributes.menus was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — no renderer ever read it; two alias maps already redirected this spelling to navigation. Delete the key. Declare navigation in the app's navigation tree, or inject items into another package's app via manifest.navigationContributions (ADR-0029 D7), which the engine registers. |
| themes | never | optional | [REMOVED] manifest.contributes.themes was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — it never had an effect: theme registration reaches the registry only through the stack-level themes collection (a ThemeSchema surface, unrelated to this { id, label, path } shape), never through contributes.themes. Delete the key; declare themes in the stack themes collection instead. |
| translations | never | optional | [REMOVED] manifest.contributes.translations was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — no loader ever read these { locale, path } entries; authoring them registered no translations. Delete the key. Declare translations as translation metadata: defineTranslationBundle({ … }) in the stack's translations collection (defineStack({ translations: […] })), which the engine registers and the i18n pipeline serves. |
| actions | never | optional | [REMOVED] manifest.contributes.actions was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it; actions declared here were never invocable. Delete the key. Declare actions in the stack actions collection (registered by the engine) or register imperatively via engine.registerAction. |
| drivers | never | optional | [REMOVED] manifest.contributes.drivers was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — it never had an effect: a storage driver is wired by registering a kernel SERVICE named driver.* (the objectql plugin picks it up and calls registerDriver), and its only in-repo author was registered that way, not by this declaration. Delete the key. |
| fieldTypes | never | optional | [REMOVED] manifest.contributes.fieldTypes was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — there is no registerFieldType seam anywhere: the declaration advertised an extension point the platform does not have, so authoring it configured nothing. Delete the key. The field-type vocabulary is the spec FieldType enum; extending it is a spec change, not a manifest declaration. |
| functions | never | optional | [REMOVED] manifest.contributes.functions was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it; ObjectQL functions declared here were never registered. Delete the key. Declare functions on the stack (defineStack({ functions: […] })), which the hook binder registers via engine.registerFunction. |
| routes | never | optional | [REMOVED] manifest.contributes.routes was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: the HttpDispatcher never registered a prefix from the declaration, so an entry here parsed cleanly and served nothing while published material kept recommending it. Delete the key. A route that needs real handler CODE is mounted imperatively: resolve the http.server service from the plugin context and register the handler on kernel:ready. A declarative endpoint over a pipeline the platform already runs (query/return records, trigger a flow) is defineStack({ apis }). |
| commands | never | optional | [REMOVED] manifest.contributes.commands was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — the CLI never resolved commands from this declaration: commands are auto-discovered through oclif's native plugin system (the plugin package declares an oclif section in its own package.json; see cli-extension.zod.ts), and the objectstack.config.ts plugins array no longer determines CLI commands. Delete the key. |
Nested Shape: Manifest.data[number]
| Property | Type | Required | Description |
|---|---|---|---|
| object | string | ✅ | Target Object Name |
| externalId | string | string[] | optional (default: "name") | Field (or composite list of fields) matched for the uniqueness check |
| mode | Enum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'> | optional (default: "upsert") | Conflict resolution strategy |
| env | Enum<'prod' | 'dev' | 'test'>[] | optional (default: ["prod","dev","test"]) | Applicable environments |
| records | Record<string, any>[] | ✅ | Data records |
| _lock | Enum<'none' | 'no-overlay' | 'no-delete' | 'full'> | optional | Item-level lock — controls overlay & delete (ADR-0010). |
| _lockReason | string | optional | Human-readable reason shown when a write is refused by _lock. |
| _lockSource | Enum<'artifact' | 'package' | 'env-forced'> | optional | Layer that set _lock (artifact | package | env-forced). |
| _provenance | Enum<'package' | 'org' | 'env-forced'> | optional | Origin of the item (package | org | env-forced). |
| _packageId | string | optional | Owning package machine id. |
| _packageVersion | string | optional | Owning package version. |
| _lockDocsUrl | string | optional | Optional documentation link surfaced next to _lockReason. |
Nested Shape: Manifest.navigationContributions[number]
A navigation contribution: a package injecting nav items into an app it does not own (ADR-0029 D7)
| Property | Type | Required | Description |
|---|---|---|---|
| app | string | ✅ | Target app name to contribute navigation into (e.g. "setup") |
| group | string | optional | Target group nav-item id to append into (e.g. "group_integrations"); omit to append at the app top level |
| priority | integer | optional (default: 200) | Merge priority within the target group — lower applied first (matches object extender priority) |
| items | ({ id: string; label: string | Record<string, string>; icon?: string; order?: number; … } | { type: 'separator'; id?: string; order?: number } | … +7 more)[] | ✅ | Navigation items contributed into the target app/group |
Nested Shape: Manifest.engine
| Property | Type | Required | Description |
|---|---|---|---|
| objectstack | string | ✅ | ObjectStack platform version requirement (SemVer range, e.g. ">=3.0.0") |
Nested Shape: Manifest.engines
| Property | Type | Required | Description |
|---|---|---|---|
| platform | string | optional | ObjectStack platform release range (SemVer, e.g. ">=4.0 <5") |
| protocol | string | optional | Runtime/metadata protocol range, checked first (ADR §3.10 #3) |
ManifestPermissions
Union Options
This schema accepts one of the following structures:
Option 1
Type: string[]
Option 2
Structured plugin permission grants (ADR-0025 §3.2)
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| services | string[] | optional | Platform services the plugin may resolve (e.g. "object", "http") |
| hooks | string[] | optional | Lifecycle hooks the plugin may register (e.g. "record.beforeInsert") |
| network | string[] | optional | Network hosts the plugin may reach (e.g. "api.acme.com") |
| fs | string[] | optional | Filesystem paths the plugin may access |
PluginEngines
Plugin compatibility ranges (ADR-0025 §3.2)
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| platform | string | optional | ObjectStack platform release range (SemVer, e.g. ">=4.0 <5") |
| protocol | string | optional | Runtime/metadata protocol range, checked first (ADR §3.10 #3) |
PluginIntegrity
Per-file content digests of the plugin artifact (ADR-0025 §3.2)
Type: Record<string, string>
PluginPackaging
Dependency packaging strategy (ADR-0025 §3.3)
Allowed Values
bundledmanifest-deps
PluginPermissions
Structured plugin permission grants (ADR-0025 §3.2)
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| services | string[] | optional | Platform services the plugin may resolve (e.g. "object", "http") |
| hooks | string[] | optional | Lifecycle hooks the plugin may register (e.g. "record.beforeInsert") |
| network | string[] | optional | Network hosts the plugin may reach (e.g. "api.acme.com") |
| fs | string[] | optional | Filesystem paths the plugin may access |
PluginRuntime
Plugin trust tier the plugin declares (ADR-0025 §3.6) — enforced at the cloud marketplace publish gate (an unverified publisher requesting node is rejected with HTTP 422 and forced to manual review); load-side enforcement is NOT implemented, so a locally installed plugin is not isolated by the tier it declares
Allowed Values
nodesandboxworker