ObjectStackObjectStack

Package Api Assembled — API Protocol reference

The Package API declarations that carry the ASSEMBLED package body. Published from @objectstack/spec/api-assembled, never from @objectstack/spec/api.

The Package API declarations that carry the ASSEMBLED package body.

Published from @objectstack/spec/api-assembled, never from @objectstack/spec/api. Everything here is part of the Package API (/api/v1/packages, ./package-api.zod.ts); what sets these five apart is that each one embeds the assembled package body, RecordStagePackageBodySchema from ../stack.zod — or, for the route map, names a schema that does:

  • AssembledInstalledPackageSchema — the installed row at the assembled stage;
  • InstalledPackageAtEitherStageSchema — the union the read doors serve;
  • ListInstalledPackagesResponseSchema / GetInstalledPackageResponseSchema — the two read responses, bound to that union;
  • PackageApiContracts — the route map, which names both read responses.

Why they have their own entry

The assembled body is the WHOLE metadata vocabulary: ../stack.zod reaches every collection schema, the datasource declaration and, behind it, the driver-config validators and the server-only pg URL grammar. Declared inside @objectstack/spec/api (#17517), that tree became part of every bundle of the entry — and a browser module that imported two string constants from ./sortability.zod paid for all of it, roughly doubling its gzipped bundle, because the entry ships as one self-contained bundle and little of that tree can be dropped by a consumer's tree-shaking. The maintainer ruling on #18576 (letter B) removed the cost rather than watching it: the browser-facing ./api no longer carries these declarations, and this entry does.

⛔ Their MEANING did not change with the move — same schemas, same refusals, same JSON Schema ids (api/..., still published under json-schema/api/, because they are API-protocol declarations; only the import path moved).

⛔ Only a declaration that genuinely needs the assembled body belongs here. Everything else in the Package API stays in ./package-api.zod.ts, which @objectstack/spec/api publishes; ./api-entry-graph.pin.test.ts pins that ./api reaches neither ../stack.zod nor the datasource declaration.

Source: packages/spec/src/api/package-api-assembled.zod.ts

TypeScript Usage

import { AssembledInstalledPackageSchema, GetInstalledPackageResponseSchema, InstalledPackageAtEitherStageSchema, ListInstalledPackagesResponseSchema } from '@objectstack/spec/api-assembled';
import type { AssembledInstalledPackage, GetInstalledPackageResponse, InstalledPackageAtEitherStage, ListInstalledPackagesResponse } from '@objectstack/spec/api-assembled';

// Validate data
const result = AssembledInstalledPackageSchema.parse(data);

AssembledInstalledPackage

Installed package row whose manifest is the assembled package body

Properties

PropertyTypeRequiredDescription
manifest{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }✅The ASSEMBLED package body this row carries, at the stage the registry records it
statusEnum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>optional (default: "installed")Package state: installed, disabled, installing, upgrading, uninstalling, or error
enabledbooleanoptional (default: true)Whether the package is currently enabled
installedAtstringoptionalInstallation timestamp
updatedAtstringoptionalLast update timestamp
installedVersionstringoptionalCurrently installed version for quick access
previousVersionstringoptionalVersion before the last upgrade
statusChangedAtstringoptionalStatus change timestamp
errorMessagestringoptionalError message when status is error
settingsRecord<string, any>optionalUser-provided configuration settings
upgradeHistory{ fromVersion: string; toVersion: string; upgradedAt: string; status: Enum<'success' | 'failed' | 'rolled_back'>; … }[]optionalVersion upgrade history
registeredNamespacesstring[]optionalNamespace prefixes registered by this package

Nested Shape: AssembledInstalledPackage.manifest

PropertyTypeRequiredDescription
idstring✅Unique package identifier — must match reverse-domain notation (e.g. com.acme.crm)
namespacestringoptionalShort namespace identifier; also the mandatory prefix of every object name (e.g. "todo" → object names "todo_task", "todo_project")
defaultDatasourcestringoptional (default: "default")Default datasource for all objects in this package
versionstring✅Package version (SemVer 2.0.0 — e.g. 1.2.3, 2.0.0-beta.1, 1.0.0+20230101)
typeEnum<'plugin' | 'ui' | 'driver' | 'server' | 'app' | 'theme' | 'agent' | 'objectql' | …>✅Type of package
scopeEnum<'cloud' | 'system' | 'project'>optional (default: "project")Deployment scope: cloud | system | project
namestring✅Human-readable package name
descriptionstringoptionalPackage description
permissions{ name: string; label?: string; description?: string; packageId?: string; … }[]optionalPermission Sets — the ADR-0090 collection half of permissions; at the manifest/authoring stage the same key is the ADR-0025 capability grant instead (ManifestSchema.permissions)
objects{ name: string; label?: string; pluralLabel?: string; description?: string; … }[]optionalBusiness Objects definition (owned by this package)
datasources{ name: string; label?: string; driver: string; config: Record<string, any>; … }[]optionalExternal Data Connections
dependenciesRecord<string, string>optionalPackage dependencies
configurationneveroptional[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[] }optionalPlatform contributions
data{ object: string; externalId?: string | string[]; mode?: Enum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>; env?: Enum<'prod' | 'dev' | 'test'>[]; … }[]optionalSeed Data / Fixtures for bootstrapping
capabilities{ name: string; label?: string; description?: string; scope?: Enum<'platform' | 'org'>; … }[]optional[ADR-0066 D1] Authorization capabilities this package defines (seeded with package provenance)
extensionsneveroptional[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 | … +9 more)[] }[]optionalNavigation items this package contributes into apps owned by other packages
loadingneveroptional[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. ⛔ Nor do the permission declarations give it back: the install-time granted set is REGISTERED on the PluginPermissionEnforcer at load and queried by nothing, so it refuses no operation. Neither surface confines a plugin today — do not author either one expecting isolation.
engine{ objectstack: string }optionalPlatform compatibility requirements (legacy; superseded by engines)
engines{ platform?: string; protocol?: string }optionalPlugin compatibility ranges (ADR-0025 §3.2; supersedes engine)
runtimeEnum<'node' | 'sandbox' | 'worker'>optionalPlugin 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
packagingEnum<'bundled' | 'manifest-deps'>optionalDependency packaging strategy (ADR-0025 §3.3)
mainstringoptionalEntry module of a code-bearing plugin, relative to the plugin root; os plugin build bundles it and writes dist/index.mjs here in the compiled manifest (ADR-0025 §3.4)
integrityRecord<string, string>optionalPer-file content digests of the plugin artifact (ADR-0025 §3.2)
functionsRecord<string, string | { handler?: string; effect?: Enum<'pure' | 'writes'> }> | { name: string; handler?: string; packageId?: string; effect?: Enum<'pure' | 'writes'> }[]optionalNamed handler functions, lowered to the refs a JSON document carries
datasourceMapping{ namespace?: string; package?: string; objectPattern?: string; default?: boolean; … }[]optionalCentralized datasource routing rules for packages/namespaces/objects
translationsRecord<string, { objects?: Record<string, object>; picklists?: Record<string, object>; apps?: Record<string, object>; messages?: Record<string, string>; … }>[]optionalI18n Translation Bundles
objectExtensions{ extend: string; fields?: Record<string, object>; label?: string; pluralLabel?: string; … }[]optionalExtensions to objects owned by other packages
picklists{ name: string; label: string; description?: string; options: object[]; … }[]optionalShared option lists that select fields reference by name
picklistExtensions{ extend: string; options: object[] }[]optionalOptions added to picklists owned by other packages (additive only)
apps{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; icon?: string; … }[]optionalApplications
views{ name?: string; label?: string | Record<string, string>; object?: string; list?: object; … }[]optionalList Views
viewItemsneveroptional[MACHINE-ASSEMBLED] Non-container view artifacts of a runtime-assembled manifest (standalone ViewItems, flattened overlays) — written by package export and artifact factories, refused in authored stack sources.
pages{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; icon?: string; … }[]optionalCustom Pages
dashboards{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; header?: object; … }[]optionalDashboards
reports{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; type?: Enum<'tabular' | 'summary' | 'matrix' | 'joined'>; … }[]optionalAnalytics Reports
datasets{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; object: string; … }[]optionalAnalytics semantic-layer datasets (ADR-0021)
actions{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; objectName?: string; … }[]optionalGlobal and Object Actions. Unique per scope, not per stack: the runtime keys every action by its owning object's name (or 'global' when object-less), a colon, then the action name, and defineStack refuses two declarations that resolve to one key — both here, both on one object's actions, or one in each position, identical twins included (an embedded action is keyed by the object it is written on, not by its own objectName). One global and one object-bound action may share a name; on that object's route the object's own actions take precedence for by-name readers. composeStacks runs the same key rule across its input stacks (counting distinct stacks, not sites) and names both source stacks on a collision.
flows{ name: string; label: string; description?: string; successMessage?: string; … }[]optionalScreen Flows
jobs{ name: string; label?: string; description?: string; schedule: object | object | object; … }[]optionalBackground / Scheduled Jobs (run by IJobService on cron/interval/once schedules)
emailTemplates{ name: string; label: string; category?: Enum<'auth' | 'notification' | 'workflow' | 'marketing' | 'custom'>; locale?: string; … }[]optionalEmail Templates resolved by IEmailService.sendTemplate({ template, locale })
docs{ name: string; label?: string; description?: string; content: string; … }[]optionalPackage documentation — flat Markdown items compiled from src/docs/*.md (ADR-0046)
books{ name: string; label?: string; description?: string; slug?: string; … }[]optionalDocumentation navigation spines — ordered groups with derived membership (ADR-0046 §6)
positions{ name: string; label: string; description?: string; delegatable?: boolean; … }[]optionalPositions — flat capability-distribution groups (ADR-0090 D3)
sharingRules{ name: string; label?: string; description?: string; object: string; … }[]optionalRecord Sharing Rules
apis{ name: string; path: string; method: Enum<'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'>; summary?: string; … }[]optionalAPI Endpoints — declared endpoints are live from protocol 17; each is gated at publish (ADR-0121)
webhooks{ name: string; label?: string; object?: string; triggers?: Enum<'create' | 'update' | 'delete' | 'bulk_update' | 'bulk_delete'>[]; … }[]optionalOutbound Webhooks
agents{ name: string; label: string; avatar?: string; role: string; … }[]optionalAI Agents — platform-internal (ADR-0063 §2): the kernel ships exactly two (ask/build); third parties extend via skills, not agents
tools{ name: string; label: string; description: string; parameters: Record<string, any>; … }[]optionalAI Tool metadata records — optional refinement layer, never required: the default path is skills referencing platform tools or materialised action_<name> tools (ADR-0109)
skills{ name: string; label: string; description?: string; surface?: Enum<'ask' | 'build' | 'both'>; … }[]optionalAI Skills (reusable capability bundles — the third-party AI extension primitive, ADR-0063)
hooks{ name: string; label?: string; object: string | string[]; events: Enum<'beforeFind' | 'afterFind' | 'beforeInsert' | 'afterInsert' | 'beforeUpdate' | …>[]; … }[]optionalObject Lifecycle Hooks, as a JSON document carries them
mappings{ name: string; label?: string; sourceFormat?: Enum<'csv' | 'json' | 'xml' | 'sql'>; targetObject: string; … }[]optionalData Import/Export Mappings
analyticsCubes{ name: string; title?: string; description?: string; sql: string; … }[]optionalAnalytics Semantic Layer Cubes
connectors{ name: string; label: string; type: Enum<'saas' | 'database' | 'file_storage' | 'message_queue' | 'api' | 'custom'>; description?: string; … }[]optionalExternal System Connectors. A provider-bound entry (has provider: openapi/mcp/rest) is materialized into a live, dispatchable connector at boot and referenced by flows via connector_action; credentials are auth.credentialRef references, never inline secrets. An entry with no provider is a catalog descriptor only (NOT dispatchable) — set enabled: false on deliberate descriptors. Unknown provider / unresolvable credentialRef / name conflict ⇒ hard boot error (ADR-0097).
requiresstring[]optionalCapability names this stack requires from the platform (canonical kebab-case tokens from PLATFORM_CAPABILITY_TOKENS; an unknown token is a defineStack error, declared-but-missing ⇒ fail-fast at startup)
tiersstring[]optionalPlugin tier presets to enable; overrides --preset

Nested Shape: AssembledInstalledPackage.upgradeHistory[number]

PropertyTypeRequiredDescription
fromVersionstring✅Version before upgrade
toVersionstring✅Version after upgrade
upgradedAtstring✅Upgrade timestamp
statusEnum<'success' | 'failed' | 'rolled_back'>✅Upgrade outcome
migrationLogstring[]optionalMigration step logs

GetInstalledPackageResponse

Get installed package response

Properties

PropertyTypeRequiredDescription
successboolean✅Operation success status
error{ code: Enum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>; declaredCode?: string; message: string; userMessage?: string; … }optionalError details if success is false
meta{ timestamp: string; duration?: integer; requestId?: string; traceId?: string }optionalResponse metadata
data{ manifest: object; status?: Enum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>; enabled?: boolean; installedAt?: string; … } | … +1 more✅Installed package details

Nested Shape: GetInstalledPackageResponse.error

PropertyTypeRequiredDescription
codeEnum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>✅Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages)
declaredCodestringoptionalThe producer-declared code, verbatim, when it is not a member of the closed code vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112)
messagestring✅Readable error message
userMessagestringoptionalProducer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces message.
refusaltrueoptionalProducer-declared: the 5xx this envelope carries is a deliberate refusal whose message is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose message is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — true is the only value.
categorystringoptionalError category (e.g. validation, authorization)
httpStatusintegeroptionalHTTP status of the response carrying this error
detailsanyoptionalAdditional error context (e.g. field validation errors)
requestIdstringoptionalRequest ID for tracking

Nested Shape: GetInstalledPackageResponse.meta

PropertyTypeRequiredDescription
timestampstring✅
durationintegeroptionalServer-side processing duration in milliseconds
requestIdstringoptional
traceIdstringoptional

Nested Shape: GetInstalledPackageResponse.data[option 1]

Installed package with runtime lifecycle state

PropertyTypeRequiredDescription
manifest{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }✅Package manifest at the AUTHORING stage; a row installed by a defineStack() host carries the assembled body instead — see AssembledInstalledPackageSchema / InstalledPackageAtEitherStageSchema
statusEnum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>optional (default: "installed")Package state: installed, disabled, installing, upgrading, uninstalling, or error
enabledbooleanoptional (default: true)Whether the package is currently enabled
installedAtstringoptionalInstallation timestamp
updatedAtstringoptionalLast update timestamp
installedVersionstringoptionalCurrently installed version for quick access
previousVersionstringoptionalVersion before the last upgrade
statusChangedAtstringoptionalStatus change timestamp
errorMessagestringoptionalError message when status is error
settingsRecord<string, any>optionalUser-provided configuration settings
upgradeHistory{ fromVersion: string; toVersion: string; upgradedAt: string; status: Enum<'success' | 'failed' | 'rolled_back'>; … }[]optionalVersion upgrade history
registeredNamespacesstring[]optionalNamespace prefixes registered by this package

Nested Shape: GetInstalledPackageResponse.data[option 2]

Installed package row whose manifest is the assembled package body

PropertyTypeRequiredDescription
manifest{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }✅The ASSEMBLED package body this row carries, at the stage the registry records it
statusEnum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>optional (default: "installed")Package state: installed, disabled, installing, upgrading, uninstalling, or error
enabledbooleanoptional (default: true)Whether the package is currently enabled
installedAtstringoptionalInstallation timestamp
updatedAtstringoptionalLast update timestamp
installedVersionstringoptionalCurrently installed version for quick access
previousVersionstringoptionalVersion before the last upgrade
statusChangedAtstringoptionalStatus change timestamp
errorMessagestringoptionalError message when status is error
settingsRecord<string, any>optionalUser-provided configuration settings
upgradeHistory{ fromVersion: string; toVersion: string; upgradedAt: string; status: Enum<'success' | 'failed' | 'rolled_back'>; … }[]optionalVersion upgrade history
registeredNamespacesstring[]optionalNamespace prefixes registered by this package

InstalledPackageAtEitherStage

Installed package row at whichever manifest stage it was installed at

Union Options

This schema accepts one of the following structures:

Option 1

Installed package with runtime lifecycle state

Properties

PropertyTypeRequiredDescription
manifest{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }✅Package manifest at the AUTHORING stage; a row installed by a defineStack() host carries the assembled body instead — see AssembledInstalledPackageSchema / InstalledPackageAtEitherStageSchema
statusEnum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>optional (default: "installed")Package state: installed, disabled, installing, upgrading, uninstalling, or error
enabledbooleanoptional (default: true)Whether the package is currently enabled
installedAtstringoptionalInstallation timestamp
updatedAtstringoptionalLast update timestamp
installedVersionstringoptionalCurrently installed version for quick access
previousVersionstringoptionalVersion before the last upgrade
statusChangedAtstringoptionalStatus change timestamp
errorMessagestringoptionalError message when status is error
settingsRecord<string, any>optionalUser-provided configuration settings
upgradeHistory{ fromVersion: string; toVersion: string; upgradedAt: string; status: Enum<'success' | 'failed' | 'rolled_back'>; … }[]optionalVersion upgrade history
registeredNamespacesstring[]optionalNamespace prefixes registered by this package

Nested Shape: InstalledPackageAtEitherStage[option 1].manifest

PropertyTypeRequiredDescription
idstring✅Unique package identifier — must match reverse-domain notation (e.g. com.acme.crm)
namespacestringoptionalShort namespace identifier; also the mandatory prefix of every object name (e.g. "todo" → object names "todo_task", "todo_project")
defaultDatasourcestringoptional (default: "default")Default datasource for all objects in this package
versionstring✅Package version (SemVer 2.0.0 — e.g. 1.2.3, 2.0.0-beta.1, 1.0.0+20230101)
typeEnum<'plugin' | 'ui' | 'driver' | 'server' | 'app' | 'theme' | 'agent' | 'objectql' | …>✅Type of package
scopeEnum<'cloud' | 'system' | 'project'>optional (default: "project")Deployment scope: cloud | system | project
namestring✅Human-readable package name
descriptionstringoptionalPackage description
permissionsstring[] | { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }optionalRequired permissions at the AUTHORING stage: legacy string[] or structured plugin block (ADR-0025 §3.2) — at the assembled stage the same key is the ADR-0090 PermissionSet[] collection instead (AssembledPackageBodySchema)
objectsstring[]optionalGlob patterns for ObjectQL schemas files
datasourcesstring[]optionalGlob patterns for Datasource definitions
dependenciesRecord<string, string>optionalPackage dependencies
configurationneveroptional[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[] }optionalPlatform contributions
data{ object: string; externalId?: string | string[]; mode?: Enum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>; env?: Enum<'prod' | 'dev' | 'test'>[]; … }[]optionalInitial seed data (prefer top-level data field)
capabilitiesneveroptional[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.
extensionsneveroptional[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 | … +9 more)[] }[]optionalNavigation items this package contributes into apps owned by other packages
loadingneveroptional[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. ⛔ Nor do the permission declarations give it back: the install-time granted set is REGISTERED on the PluginPermissionEnforcer at load and queried by nothing, so it refuses no operation. Neither surface confines a plugin today — do not author either one expecting isolation.
engine{ objectstack: string }optionalPlatform compatibility requirements (legacy; superseded by engines)
engines{ platform?: string; protocol?: string }optionalPlugin compatibility ranges (ADR-0025 §3.2; supersedes engine)
runtimeEnum<'node' | 'sandbox' | 'worker'>optionalPlugin 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
packagingEnum<'bundled' | 'manifest-deps'>optionalDependency packaging strategy (ADR-0025 §3.3)
mainstringoptionalEntry module of a code-bearing plugin, relative to the plugin root; os plugin build bundles it and writes dist/index.mjs here in the compiled manifest (ADR-0025 §3.4)
integrityRecord<string, string>optionalPer-file content digests of the plugin artifact (ADR-0025 §3.2)

Nested Shape: InstalledPackageAtEitherStage[option 1].upgradeHistory[number]

PropertyTypeRequiredDescription
fromVersionstring✅Version before upgrade
toVersionstring✅Version after upgrade
upgradedAtstring✅Upgrade timestamp
statusEnum<'success' | 'failed' | 'rolled_back'>✅Upgrade outcome
migrationLogstring[]optionalMigration step logs

Option 2

Installed package row whose manifest is the assembled package body

Properties

PropertyTypeRequiredDescription
manifest{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }✅The ASSEMBLED package body this row carries, at the stage the registry records it
statusEnum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>optional (default: "installed")Package state: installed, disabled, installing, upgrading, uninstalling, or error
enabledbooleanoptional (default: true)Whether the package is currently enabled
installedAtstringoptionalInstallation timestamp
updatedAtstringoptionalLast update timestamp
installedVersionstringoptionalCurrently installed version for quick access
previousVersionstringoptionalVersion before the last upgrade
statusChangedAtstringoptionalStatus change timestamp
errorMessagestringoptionalError message when status is error
settingsRecord<string, any>optionalUser-provided configuration settings
upgradeHistory{ fromVersion: string; toVersion: string; upgradedAt: string; status: Enum<'success' | 'failed' | 'rolled_back'>; … }[]optionalVersion upgrade history
registeredNamespacesstring[]optionalNamespace prefixes registered by this package

Nested Shape: InstalledPackageAtEitherStage[option 2].manifest

PropertyTypeRequiredDescription
idstring✅Unique package identifier — must match reverse-domain notation (e.g. com.acme.crm)
namespacestringoptionalShort namespace identifier; also the mandatory prefix of every object name (e.g. "todo" → object names "todo_task", "todo_project")
defaultDatasourcestringoptional (default: "default")Default datasource for all objects in this package
versionstring✅Package version (SemVer 2.0.0 — e.g. 1.2.3, 2.0.0-beta.1, 1.0.0+20230101)
typeEnum<'plugin' | 'ui' | 'driver' | 'server' | 'app' | 'theme' | 'agent' | 'objectql' | …>✅Type of package
scopeEnum<'cloud' | 'system' | 'project'>optional (default: "project")Deployment scope: cloud | system | project
namestring✅Human-readable package name
descriptionstringoptionalPackage description
permissions{ name: string; label?: string; description?: string; packageId?: string; … }[]optionalPermission Sets — the ADR-0090 collection half of permissions; at the manifest/authoring stage the same key is the ADR-0025 capability grant instead (ManifestSchema.permissions)
objects{ name: string; label?: string; pluralLabel?: string; description?: string; … }[]optionalBusiness Objects definition (owned by this package)
datasources{ name: string; label?: string; driver: string; config: Record<string, any>; … }[]optionalExternal Data Connections
dependenciesRecord<string, string>optionalPackage dependencies
configurationneveroptional[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[] }optionalPlatform contributions
data{ object: string; externalId?: string | string[]; mode?: Enum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>; env?: Enum<'prod' | 'dev' | 'test'>[]; … }[]optionalSeed Data / Fixtures for bootstrapping
capabilities{ name: string; label?: string; description?: string; scope?: Enum<'platform' | 'org'>; … }[]optional[ADR-0066 D1] Authorization capabilities this package defines (seeded with package provenance)
extensionsneveroptional[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 | … +9 more)[] }[]optionalNavigation items this package contributes into apps owned by other packages
loadingneveroptional[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. ⛔ Nor do the permission declarations give it back: the install-time granted set is REGISTERED on the PluginPermissionEnforcer at load and queried by nothing, so it refuses no operation. Neither surface confines a plugin today — do not author either one expecting isolation.
engine{ objectstack: string }optionalPlatform compatibility requirements (legacy; superseded by engines)
engines{ platform?: string; protocol?: string }optionalPlugin compatibility ranges (ADR-0025 §3.2; supersedes engine)
runtimeEnum<'node' | 'sandbox' | 'worker'>optionalPlugin 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
packagingEnum<'bundled' | 'manifest-deps'>optionalDependency packaging strategy (ADR-0025 §3.3)
mainstringoptionalEntry module of a code-bearing plugin, relative to the plugin root; os plugin build bundles it and writes dist/index.mjs here in the compiled manifest (ADR-0025 §3.4)
integrityRecord<string, string>optionalPer-file content digests of the plugin artifact (ADR-0025 §3.2)
functionsRecord<string, string | { handler?: string; effect?: Enum<'pure' | 'writes'> }> | { name: string; handler?: string; packageId?: string; effect?: Enum<'pure' | 'writes'> }[]optionalNamed handler functions, lowered to the refs a JSON document carries
datasourceMapping{ namespace?: string; package?: string; objectPattern?: string; default?: boolean; … }[]optionalCentralized datasource routing rules for packages/namespaces/objects
translationsRecord<string, { objects?: Record<string, object>; picklists?: Record<string, object>; apps?: Record<string, object>; messages?: Record<string, string>; … }>[]optionalI18n Translation Bundles
objectExtensions{ extend: string; fields?: Record<string, object>; label?: string; pluralLabel?: string; … }[]optionalExtensions to objects owned by other packages
picklists{ name: string; label: string; description?: string; options: object[]; … }[]optionalShared option lists that select fields reference by name
picklistExtensions{ extend: string; options: object[] }[]optionalOptions added to picklists owned by other packages (additive only)
apps{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; icon?: string; … }[]optionalApplications
views{ name?: string; label?: string | Record<string, string>; object?: string; list?: object; … }[]optionalList Views
viewItemsneveroptional[MACHINE-ASSEMBLED] Non-container view artifacts of a runtime-assembled manifest (standalone ViewItems, flattened overlays) — written by package export and artifact factories, refused in authored stack sources.
pages{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; icon?: string; … }[]optionalCustom Pages
dashboards{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; header?: object; … }[]optionalDashboards
reports{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; type?: Enum<'tabular' | 'summary' | 'matrix' | 'joined'>; … }[]optionalAnalytics Reports
datasets{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; object: string; … }[]optionalAnalytics semantic-layer datasets (ADR-0021)
actions{ name: string; label: string | Record<string, string>; description?: string | Record<string, string>; objectName?: string; … }[]optionalGlobal and Object Actions. Unique per scope, not per stack: the runtime keys every action by its owning object's name (or 'global' when object-less), a colon, then the action name, and defineStack refuses two declarations that resolve to one key — both here, both on one object's actions, or one in each position, identical twins included (an embedded action is keyed by the object it is written on, not by its own objectName). One global and one object-bound action may share a name; on that object's route the object's own actions take precedence for by-name readers. composeStacks runs the same key rule across its input stacks (counting distinct stacks, not sites) and names both source stacks on a collision.
flows{ name: string; label: string; description?: string; successMessage?: string; … }[]optionalScreen Flows
jobs{ name: string; label?: string; description?: string; schedule: object | object | object; … }[]optionalBackground / Scheduled Jobs (run by IJobService on cron/interval/once schedules)
emailTemplates{ name: string; label: string; category?: Enum<'auth' | 'notification' | 'workflow' | 'marketing' | 'custom'>; locale?: string; … }[]optionalEmail Templates resolved by IEmailService.sendTemplate({ template, locale })
docs{ name: string; label?: string; description?: string; content: string; … }[]optionalPackage documentation — flat Markdown items compiled from src/docs/*.md (ADR-0046)
books{ name: string; label?: string; description?: string; slug?: string; … }[]optionalDocumentation navigation spines — ordered groups with derived membership (ADR-0046 §6)
positions{ name: string; label: string; description?: string; delegatable?: boolean; … }[]optionalPositions — flat capability-distribution groups (ADR-0090 D3)
sharingRules{ name: string; label?: string; description?: string; object: string; … }[]optionalRecord Sharing Rules
apis{ name: string; path: string; method: Enum<'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'>; summary?: string; … }[]optionalAPI Endpoints — declared endpoints are live from protocol 17; each is gated at publish (ADR-0121)
webhooks{ name: string; label?: string; object?: string; triggers?: Enum<'create' | 'update' | 'delete' | 'bulk_update' | 'bulk_delete'>[]; … }[]optionalOutbound Webhooks
agents{ name: string; label: string; avatar?: string; role: string; … }[]optionalAI Agents — platform-internal (ADR-0063 §2): the kernel ships exactly two (ask/build); third parties extend via skills, not agents
tools{ name: string; label: string; description: string; parameters: Record<string, any>; … }[]optionalAI Tool metadata records — optional refinement layer, never required: the default path is skills referencing platform tools or materialised action_<name> tools (ADR-0109)
skills{ name: string; label: string; description?: string; surface?: Enum<'ask' | 'build' | 'both'>; … }[]optionalAI Skills (reusable capability bundles — the third-party AI extension primitive, ADR-0063)
hooks{ name: string; label?: string; object: string | string[]; events: Enum<'beforeFind' | 'afterFind' | 'beforeInsert' | 'afterInsert' | 'beforeUpdate' | …>[]; … }[]optionalObject Lifecycle Hooks, as a JSON document carries them
mappings{ name: string; label?: string; sourceFormat?: Enum<'csv' | 'json' | 'xml' | 'sql'>; targetObject: string; … }[]optionalData Import/Export Mappings
analyticsCubes{ name: string; title?: string; description?: string; sql: string; … }[]optionalAnalytics Semantic Layer Cubes
connectors{ name: string; label: string; type: Enum<'saas' | 'database' | 'file_storage' | 'message_queue' | 'api' | 'custom'>; description?: string; … }[]optionalExternal System Connectors. A provider-bound entry (has provider: openapi/mcp/rest) is materialized into a live, dispatchable connector at boot and referenced by flows via connector_action; credentials are auth.credentialRef references, never inline secrets. An entry with no provider is a catalog descriptor only (NOT dispatchable) — set enabled: false on deliberate descriptors. Unknown provider / unresolvable credentialRef / name conflict ⇒ hard boot error (ADR-0097).
requiresstring[]optionalCapability names this stack requires from the platform (canonical kebab-case tokens from PLATFORM_CAPABILITY_TOKENS; an unknown token is a defineStack error, declared-but-missing ⇒ fail-fast at startup)
tiersstring[]optionalPlugin tier presets to enable; overrides --preset

Nested Shape: InstalledPackageAtEitherStage[option 2].upgradeHistory[number]

PropertyTypeRequiredDescription
fromVersionstring✅Version before upgrade
toVersionstring✅Version after upgrade
upgradedAtstring✅Upgrade timestamp
statusEnum<'success' | 'failed' | 'rolled_back'>✅Upgrade outcome
migrationLogstring[]optionalMigration step logs


ListInstalledPackagesResponse

List installed packages response

Properties

PropertyTypeRequiredDescription
successboolean✅Operation success status
error{ code: Enum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>; declaredCode?: string; message: string; userMessage?: string; … }optionalError details if success is false
meta{ timestamp: string; duration?: integer; requestId?: string; traceId?: string }optionalResponse metadata
data{ packages: (object | object)[]; total?: integer; nextCursor?: string; hasMore: boolean }✅

Nested Shape: ListInstalledPackagesResponse.error

PropertyTypeRequiredDescription
codeEnum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>✅Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages)
declaredCodestringoptionalThe producer-declared code, verbatim, when it is not a member of the closed code vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112)
messagestring✅Readable error message
userMessagestringoptionalProducer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces message.
refusaltrueoptionalProducer-declared: the 5xx this envelope carries is a deliberate refusal whose message is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose message is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — true is the only value.
categorystringoptionalError category (e.g. validation, authorization)
httpStatusintegeroptionalHTTP status of the response carrying this error
detailsanyoptionalAdditional error context (e.g. field validation errors)
requestIdstringoptionalRequest ID for tracking

Nested Shape: ListInstalledPackagesResponse.meta

PropertyTypeRequiredDescription
timestampstring✅
durationintegeroptionalServer-side processing duration in milliseconds
requestIdstringoptional
traceIdstringoptional

Nested Shape: ListInstalledPackagesResponse.data

PropertyTypeRequiredDescription
packages({ manifest: object; status?: Enum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>; enabled?: boolean; installedAt?: string; … } | … +1 more)[]✅Installed packages
totalintegeroptionalTotal matching packages
nextCursorstringoptionalCursor for the next page
hasMoreboolean✅Whether more packages are available — this door serves one page, so always false

On this page