Integrator guide — authoring a virtual app by hand
This guide walks you through creating a new virtual app in OpenBuild without using the visual editor (which lives in chain spec openbuild-page-editor — not yet shipped). At this stage, OpenBuild is integrator-only: you write JSON and the runtime renders it.
What you author
A virtual app is one record in OpenBuild'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.
Creating a virtual app with the wizard
For most operators the visual wizard at Virtual apps → New application is the quicker path. It walks you through three steps in one round-trip:
- Identity — pick a slug + human-readable name + description.
- Versions — accept the default chain (
development → staging → production) or adjust it. Each version maps to its own per-version register (openbuild-{slug}-{versionSlug}) so production data is physically isolated from staging and development. The wizard's chain editor enforces ADR-002's linear-chain rule (no fan-out, no cycles, exactly one terminalproductiontier). - Permissions — pick the owners / editors / viewers. The caller is
pre-filled into
owners. Groupgroup:*becomes the "all signed-in users" wildcard per REQ-OBRBAC-004.
On submit the wizard atomically creates the Application record, the N
ApplicationVersion rows, and N per-version registers — and seeds each
register with the default schema set (hello-message by default) under the
namespaced slug {appSlug}-{versionSlug}-{originalSchemaSlug}. The manifest's
config.register and config.schema pointers are rewritten to match
(openbuild-{slug}-{tier} and {appSlug}-{tier}-hello-message respectively)
so the insights service and the runtime each address the right per-tier slice.
Empty-state landing — fresh installs no longer auto-seed a hello-world
Application (the legacy SeedHelloWorld repair step was retired by
openbuild-versioning-model). New deploys land the admin on an empty Virtual
apps index with a CTA pointing at the wizard. Pre-existing installs are not
affected; the migration step MigrateToVersionedModel only fires when
pre-spec-C Application rows are present and is idempotent on re-runs.
For further reading on what each step writes through to OR, see
openbuild-runtime.md and the wizard chain spec
openspec/changes/openbuild-app-creation-wizard/.
Step-by-step (manual / integrator path)
- Pick a slug. Must be kebab-case, 2–48 chars, unique within your organisation. The synthetic appId in CnAppRoot becomes
openbuild-${slug}. - Design your schemas in OpenRegister directly (the OpenBuild schema editor lands in chain spec
openbuild-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 OpenBuild's manifest editor (top-bar OpenBuild 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 specopenbuild-versioning). On publish, OpenBuild's lifecycle creates the correspondingBuiltAppRouteso/builder/{slug}becomes reachable.