Integrator guide — authoring a virtual app by hand
This guide walks you through creating a new virtual app in OpenBuilt without using the visual editor (which lives in chain spec openbuilt-page-editor — not yet shipped). At this stage, OpenBuilt is integrator-only: you write JSON and the runtime renders it.
What you author
A virtual app is one record in OpenBuilt's Application OR schema. The shape is:
{
"slug": "permit-tracker", // kebab-case, 2–48 chars
"name": "Permit Tracker",
"description": "Track building permits through review stages.",
"version": "0.1.0",
"status": "draft", // draft → published → archived
"manifest": {
"version": "1.0.0",
"dependencies": ["openregister"],
"menu": [ ... ],
"pages": [ ... ]
}
}
The manifest object validates against @conduction/nextcloud-vue/src/schemas/app-manifest.schema.json. The closed type enum for pages is index | detail | dashboard | logs | settings | chat | files | form | custom.
Step-by-step
- Pick a slug. Must be kebab-case, 2–48 chars, unique within your organisation. The synthetic appId in CnAppRoot becomes
openbuilt-${slug}. - Design your schemas in OpenRegister directly (the OpenBuilt schema editor lands in chain spec
openbuilt-schema-editor). At minimum: one schema per primary entity your app shows. - Author the manifest as JSON. The canonical example is the seeded
hello-worldApplication — open it in OpenBuilt's manifest editor (top-bar OpenBuilt entry → Virtual apps → hello-world) and read its manifest. - Save as
draftwhile iterating. The textarea editor validates each save against the canonical schema; you see the failing JSON path on save error. - Transition to
publishedwhen ready (via OR's lifecycle endpoint or the editor's Publish action — landing in chain specopenbuilt-versioning). On publish, OpenBuilt's lifecycle creates the correspondingBuiltAppRouteso/builder/{slug}becomes reachable.
Manifest checklist
Per ADR-024:
version(semver) — your app's content versiondependencies— list of NC app IDs that must be installed (almost always["openregister"])menu[]— at least one entry; supports one level ofchildren[]pages[]— at least one entry; every page'sidMUST be unique and match a vue-router route namelabel/titlestrings are i18n KEYS, not literals. The consuming app'st()resolves them. Use kebab.dot.notation:myapp.permits.title.list.
Per ADR-007:
- Every translation key MUST exist in
l10n/en.jsonANDl10n/nl.jsonof the OpenBuilt repo (until per-virtual-app translations land in chain specopenbuilt-page-editor).
Reading the seed manifest
The seeded hello-world Application is the canonical reference. Its manifest exercises:
- index page → drives
CnIndexPagewithregister: openbuilt,schema: hello-message, three columns - detail page → drives
CnDetailPagekeyed on:id - form page → drives
CnFormPagewithmode: createandsubmitEndpointgoing to OR's REST
See lib/Repair/SeedHelloWorld.php buildHelloWorldManifest() for the full JSON.
When you hit a limit
The closed type enum can't be extended from a manifest — adding a new page type requires a library-level openspec change in @conduction/nextcloud-vue. If you need something the four built-in types can't express:
- Confirm the requirement isn't satisfied by
form(the most flexible built-in). - Open an issue on
ConductionNL/nextcloud-vuedescribing the new page type's shape. - As an interim, mount a custom Vue component via
type: "custom"+component: "MyCustomPage"and register the component in OpenBuilt'scustomComponentsmap. (Note: spec #1 only ships the built-in types — thecustomComponentsregistry surface lands when a real consumer needs it.)
What does NOT work yet (spec #1 limitations)
- No visual editor — JSON textarea only. Visual editor: chain spec
openbuilt-page-editor. - No schema designer — schemas must be authored in
lib/Settings/openbuilt_register.jsonand imported via the repair step. Runtime schema authoring: chain specopenregister-runtime-schema-api. - No draft preview — only
publishedapps appear at/builder/{slug}. Draft preview: chain specopenbuilt-versioning. - No per-app permissions — auth-only visibility for v1; everyone in your organisation sees every virtual app. Per-app RBAC: chain spec
openbuilt-rbac. - No export to a real Nextcloud app — virtual-only. Export pipeline: chain spec
openbuilt-export-to-real-app.
If any of these limitations block your project, talk to Conduction — chain spec prioritisation can shift.