Manifest schema
This page is the exhaustive reference for the manifest object every package default-exports from manifest.ts. For a task-oriented walkthrough, see Manifest; for the narrative around individual fields, see the other pages under Anatomy.
Fields
| Field | Type | Required? | Description |
|---|---|---|---|
name | string | yes | Human-readable name shown in navigation and the package registry. |
slug | string | yes | URL segment and collection-name prefix. Must match the last segment of the npm package name. |
version | string | yes | The version the in-app package registry shows. Keep in sync with package.json; the release tooling bumps both. |
description | string | yes | One-sentence summary shown in the package registry. |
repository.url | string | no | Where the package lives, for “Report an issue” links. repository.issueTemplate optionally names an issue template. |
routes.directory | string | no | Folder of app screens, re-exported under tinycld/app/a/(app)/<slug>/ (served at /a/<slug>/). Convention: 'screens'. |
publicRoutes.directory | string | no | Folder of public screens, re-exported under tinycld/app/p/<slug>/<path>. Convention: 'public-screens'. |
nav.label | string | if nav set | Text for the rail entry. |
nav.icon | string | if nav set | Lucide icon name. |
nav.order | number | no | Sort priority; lower comes first. |
nav.shortcut | string | no | Single letter: t then this letter jumps to the package. Must be unique across installed packages; the generator rejects duplicates. |
migrations.directory | string | no | Folder of PocketBase migration JS files. Convention: 'pb-migrations'. |
hooks.directory | string | no | Folder of PocketBase JS hooks. Convention: 'pb-hooks'. |
collections.register | string | no | Subpath (no extension) to the module exporting registerCollections. |
collections.types | string | no | Subpath (no extension) to the module exporting {PascalSlug}Schema. |
sidebar.component | string | no | Subpath to a component rendered in the secondary sidebar when this package is active. Omit sidebar entirely and the workspace renders no sidebar container — the package’s screens get the full viewport width next to the nav rail (@tinycld/calc ships this way). |
provider.component | string | no | Subpath to a provider component wrapping the package’s routes. |
settings | Array<{slug, component, label}> | no | Personal Settings panel contributions. Each entry is a link + component pair. |
settings[].slug | string | if settings set | URL segment under /a/settings/. Must be unique across installed packages. |
settings[].component | string | if settings set | Subpath to the panel component. |
settings[].label | string | if settings set | Link text in the settings sidebar. |
systemSettings | Array<{slug, component, label}> | no | Deployment-wide settings panels (a mail provider’s credentials, a webhook secret). Same shape as settings, but rendered in the owner’s system settings and stored once per deployment, not per user. |
slots | string[] | no | Names of sidebar slots this package exposes for other packages to contribute into. Each name must be unique within the manifest; the generator errors on duplicates. See Sidebar slots. |
sidebarContributions | Array<{target, slot, component, order?}> | no | UI contributions this package injects into another package’s sidebar slot. |
sidebarContributions[].target | string | if set | Slug of the host package whose slot is being targeted. Tolerated (warning, not error) when the host isn’t installed. |
sidebarContributions[].slot | string | if set | Name of the slot to render into. Must match a slot declared in the target’s slots array; the generator errors otherwise. |
sidebarContributions[].component | string | if set | Subpath to the React component. Same resolution as settings[].component. Must default-export. |
sidebarContributions[].order | number | no | Sort priority among contributions to the same slot. Default 0; ties broken alphabetically by contributor slug. |
eventSources | Array<{target, id, label, module, color?, order?}> | no | Read-only feeds this package contributes to another package’s grid (Boards’ due dates on the Calendar). target is the host slug — it must declare eventSourceHost: true or generation fails; an absent host leaves the source silently inactive. id is [a-z0-9-], unique per target. module is a subpath to a module exporting useEventSource. See Event sources. |
eventSourceHost | boolean | no | Declares that this package’s grid accepts eventSources contributions. |
seed.script | string | no | Subpath to a module default-exporting an async seed function. |
tests.directory | string | no | Folder of Playwright specs. Convention: 'tests'. Vitest globs tests automatically. |
server.package | string | no | Subdirectory containing a Go module. Convention: 'server'. |
server.module | string | if server set | Go module path declared in that subdirectory’s go.mod. Namespace as tinycld.org/packages/<slug>. |
server.mailListeners | boolean | no | This package serves mail protocols. A self-hosted deployment binds its own ports; under a hosting supervisor the pre-bound sockets are injected and the package discovers them through coreserver.GetTenantContext. |
payloads.package | string | no | Directory of a Go package declaring the HTTP payload contract (e.g. 'server/api'). The generator emits typed TypeScript for it at lib/generated/<slug>-api.ts. |
carddav | object | no | Declarative CardDAV config: the contacts collection, its list filter, owner and UID fields, soft-delete field, and the vCard field map. Core serves the protocol; the package contributes only the config, so a deployment that links no feature Go still gets CardDAV. |
caldav | object | no | Declarative CalDAV config: the calendar and event collections and their field maps, plus defaults for required select fields a minimal client payload omits. |
webdav | object | no | Declarative WebDAV config: the mount prefix, the items collection and its field map, and an optional trash block so a DAV DELETE stamps a per-user soft delete instead of destroying the record. |
quota | Array<{collection, sizeField, ownerField?}> | no | Storage-bearing collections. Core enforces the per-user and deployment ceilings as record hooks, so no write path can skip them. A source with no ownerField counts toward the deployment ceiling only. |
help.directory | string | no | Folder of <id>.md help topics. Convention: 'help'. |
cli.package | string | no | Subdirectory containing a Go module exposing Register(root *cobra.Command, c *client.Client). Convention: 'cli'. See tinycld CLI reference. |
cli.module | string | if cli set | Go module path declared in that subdirectory’s go.mod. Namespace as tinycld.org/packages/<slug>/cli. There is deliberately no command list (Cobra owns the tree) and no scope list (the package’s Go server registers its OAuth scopes with oauth.RegisterPackage). |
search.adapter | string | no | Subpath to a module exporting useSearchActions, which decides what happens when a result from this package is selected in the palette. Rows come from core’s federated /api/search, fed by the Go server’s search.RegisterSources. Omit to stay out of the palette. |
search.label | string | no | Chip and group label in the palette. Defaults to nav.label. |
automation.definitions | string | no | Subpath to a TS module default-exporting an AutomationDefinitions object — pure data, typed against the package’s schema. Declares the triggers and actions users can build rules on. See Automation. |
build.script | string | no | Subpath to a TS module the generator runs before emitting generated files. Use for artifacts the bundler can’t produce on its own (e.g. an embedded webview bundle). dev keeps it alive in watch mode. |
dependencies | string[] | no | Slugs of other packages this one expects. Advisory only, plus seed ordering; not enforced. |
peerVersions | Record<string, string> | no | Enforced semver ranges keyed by slug or @tinycld/core (e.g. { '@tinycld/core': '>=0.0.6 <0.1.0' }). The version-compatibility solver refuses any staged change in Settings → Packages that would leave a declared range unsatisfied, and the server checks again before applying. Every shipping package sets it. |
All the *.directory and *.component / *.script fields are paths relative to the package root. directory values point at a folder; component, script, module, and adapter values are subpaths resolved through the package’s exports map, without the file extension.
TypeScript interface
The source-of-truth interface, with full doc comments, lives in tinycld/core/lib/packages/types.ts; tinycld/scripts/load-manifest.ts carries a copy for the generator. Abbreviated:
interface PackageManifest {
name: string
slug: string
version: string
description: string
repository?: { url: string; issueTemplate?: string }
routes?: { directory: string }
publicRoutes?: { directory: string }
nav?: { label: string; icon: string; order?: number; shortcut?: string }
migrations?: { directory: string }
hooks?: { directory: string }
collections?: { register: string; types: string }
sidebar?: { component: string }
provider?: { component: string }
settings?: { slug: string; component: string; label: string }[]
systemSettings?: { slug: string; component: string; label: string }[]
slots?: string[]
sidebarContributions?: { target: string; slot: string; component: string; order?: number }[]
eventSources?: { target: string; id: string; label: string; module: string; color?: string; order?: number }[]
eventSourceHost?: boolean
seed?: { script: string }
tests?: { directory: string }
server?: { package: string; module: string; mailListeners?: boolean }
payloads?: { package: string }
carddav?: CardDavConfig
caldav?: CalDavConfig
webdav?: WebDavConfig
quota?: { collection: string; sizeField: string; ownerField?: string }[]
help?: { directory: string }
cli?: { package: string; module: string }
search?: { adapter: string; label?: string }
automation?: { definitions: string }
build?: { script: string }
dependencies?: string[]
peerVersions?: Record<string, string>
}
The three protocol config shapes (CardDavConfig, CalDavConfig, WebDavConfig) are field maps from the protocol’s vocabulary onto the package’s own collections; copy the block from contacts/manifest.ts, calendar/manifest.ts, or drive/manifest.ts and rename the fields.
Annotated example
const manifest = {
name: 'Example',
slug: 'example',
version: '0.1.0',
description: 'An example package',
repository: { url: 'https://github.com/acme/example' },
routes: { directory: 'screens' },
publicRoutes: { directory: 'public-screens' },
nav: {
label: 'Example',
icon: 'box',
order: 20,
shortcut: 'e',
},
migrations: { directory: 'pb-migrations' },
hooks: { directory: 'pb-hooks' },
collections: {
register: 'collections',
types: 'types',
},
settings: [
{ slug: 'example', component: 'settings/example', label: 'Example settings' },
],
sidebar: { component: 'sidebar' },
provider: { component: 'provider' },
seed: { script: 'seed' },
tests: { directory: 'tests' },
help: { directory: 'help' },
server: { package: 'server', module: 'tinycld.org/packages/example' },
cli: { package: 'cli', module: 'tinycld.org/packages/example/cli' },
search: { adapter: 'search-adapter' },
automation: { definitions: 'automation' },
quota: [{ collection: 'example_files', sizeField: 'size', ownerField: 'owner' }],
build: { script: 'build' },
peerVersions: { '@tinycld/core': '>=0.0.6 <0.1.0' },
// dependencies: ['other-package-slug'],
}
export default manifest