blueprint.json¶
A blueprint is a portable JSON description of the initial state the Nextcloud Playground should provision inside a browser scope. It is applied on boot, after Nextcloud is installed against SQLite.
- Default blueprint:
assets/blueprints/default.blueprint.json - Schema:
assets/blueprints/blueprint-schema.json - Normalization:
src/shared/blueprint.js - Applied by:
src/runtime/bootstrap.js
Provisioning steps run through Nextcloud's occ command (executed via the
shebang-free console.php wrapper described in
development.md), or through web requests for session-based
steps. Don't declare features that don't work under WebAssembly — see
KNOWN-ISSUES.
Loading a blueprint¶
The shell resolves a blueprint in this order
(resolveBlueprintForShell in src/shared/blueprint.js):
?blueprint=— inline base64 of the JSON (URL-safe alphabet, padding optional), or anhttp(s)URL.?blueprint-url=— a remote URL (primary form).?blueprint-data=— legacy alias for?blueprint=(deprecated).config.defaultBlueprintUrl→assets/blueprints/default.blueprint.json.- The built-in default.
http://localhost:8085/?blueprint=<base64-of-json>
http://localhost:8085/?blueprint-url=https://example.com/demo.blueprint.json
Top-level structure¶
| Property | Use | Notes |
|---|---|---|
$schema |
Schema reference | Optional but recommended. |
meta |
Descriptive metadata | title, author, description. |
debug.enabled |
Show PHP errors | Diagnostics only. |
landingPage |
Entry route | Normalized to start with / (e.g. /index.php/apps/dashboard/). |
siteOptions |
Instance options | title, locale, timezone. |
login |
Effective admin credentials | username, password. |
steps |
Ordered provisioning steps | See the step reference below. |
{
"$schema": "./blueprint-schema.json",
"meta": {
"title": "Nextcloud demo",
"author": "team",
"description": "Baseline demo: a group, two users, an enabled app, a shared file."
},
"debug": { "enabled": false },
"landingPage": "/index.php/apps/dashboard/",
"siteOptions": { "title": "Nextcloud Playground", "locale": "en", "timezone": "UTC" },
"login": { "username": "admin", "password": "admin" },
"steps": [
{ "step": "installNextcloud", "adminUser": "admin", "adminPass": "admin", "adminEmail": "admin@example.com" },
{ "step": "setConfig", "key": "default_phone_region", "value": "ES" },
{ "step": "createGroup", "gid": "teachers" },
{ "step": "createUser", "uid": "alice", "password": "alice-pass", "displayName": "Alice" },
{ "step": "addUserToGroup", "uid": "alice", "gid": "teachers" },
{ "step": "enableApp", "appId": "activity" },
{ "step": "disableApp", "appId": "firstrunwizard" },
{ "step": "writeFile", "path": "/admin/files/welcome.md", "contents": "# Welcome to the playground" },
{ "step": "createShare", "path": "/welcome.md", "shareType": "public" },
{ "step": "login", "username": "admin", "password": "admin" }
]
}
Step reference¶
Steps run sequentially in array order. Each maps to an occ command or a web
request.
installNextcloud¶
Installs Nextcloud against SQLite. In practice this is an idempotent marker: the real install runs once during boot and is skipped if the persisted DB already matches the current bundle.
{ "step": "installNextcloud", "adminUser": "admin", "adminPass": "admin", "adminEmail": "admin@example.com" }
Maps to:
occ maintenance:install --database sqlite --database-name nextcloud --admin-user admin --admin-pass admin --admin-email admin@example.com --data-dir <data>,
followed by the WASM-safe config flags.
login¶
Authenticates a user via a web request to /index.php/login, establishing a
session cookie (captured by the runtime cookie jar). This is not an occ
command.
{ "step": "login", "username": "admin", "password": "admin" }
createUser¶
{ "step": "createUser", "uid": "alice", "password": "alice-pass", "displayName": "Alice", "email": "alice@example.com" }
Maps to OC_PASS=<password> occ user:add --password-from-env --display-name "<displayName>" <uid>.
The password is passed via the OC_PASS env var (no interactive prompt is
possible).
createGroup¶
{ "step": "createGroup", "gid": "teachers" }
Maps to occ group:add <gid>.
addUserToGroup¶
{ "step": "addUserToGroup", "uid": "alice", "gid": "teachers" }
Maps to occ group:adduser <gid> <uid>. The user and group must already exist.
enableApp¶
{ "step": "enableApp", "appId": "activity" }
Maps to occ app:enable <appId>. The app must be present in the trimmed bundle.
disableApp¶
{ "step": "disableApp", "appId": "firstrunwizard" }
Maps to occ app:disable <appId>.
setConfig¶
Sets a config.php system key.
{ "step": "setConfig", "key": "default_phone_region", "value": "ES" }
{ "step": "setConfig", "key": "filelocking.enabled", "value": false, "type": "bool" }
{ "step": "setConfig", "key": "enabledPreviewProviders", "value": [], "type": "json" }
Maps to occ config:system:set <key> [<subkey>...] --value <value> [--type bool|integer|json].
Specify type for non-string values.
writeFile¶
Writes a file into a user's files and indexes it.
{ "step": "writeFile", "path": "/admin/files/welcome.md", "contents": "# Welcome" }
Writes the bytes into the user's data directory, then runs occ files:scan <uid>
so Nextcloud registers the file. contents can also be supplied from a resource
(URL / base64) rather than inline literal text.
createShare¶
Creates a share for an existing path.
{ "step": "createShare", "path": "/welcome.md", "shareType": "public" }
{ "step": "createShare", "path": "/Reports", "shareType": "group", "shareWith": "teachers", "permissions": "read" }
shareType is typically public (link), user, or group. For user/group
shares set shareWith; permissions defaults to read.
installApp¶
Installs a Nextcloud app that is not part of the trimmed bundle by
downloading a ZIP archive, extracting it into the apps directory, and enabling
it. This is what lets an external app repository (e.g. nextcloud-exelearning)
ship a blueprint that boots the playground with its own app pre-installed.
{ "step": "installApp", "appId": "exelearning", "url": "https://github.com/exelearning/nextcloud-exelearning/releases/download/playground/exelearning.zip" }
urlmust point to a ZIP whose contents include the app'sappinfo/info.xml. A single common leading folder is stripped automatically, so both a built app ZIP (exelearning/appinfo/…) and a GitHub source archive (repo-branch/appinfo/…) work.- Files are written into
apps/<appId>inside the readonly core's (writable MEMFS) apps path, so noapps_pathschange is required. - After extraction the app is enabled with
occ app:enable --force <appId>(the--forcebypasses the Nextcloud version requirement). Set"enable": falseto extract without enabling.
The app must be a built artifact: source archives that rely on a compiled
js/ bundle won't render in the browser unless that bundle is included in the
ZIP.
The fetch happens cross-origin from the runtime worker, so the ZIP host must
send Access-Control-Allow-Origin. raw.githubusercontent.com and GitHub Pages
do; GitHub release-asset downloads do not (they redirect to Azure Blob
without CORS headers). To serve a release asset, route it through a CORS proxy,
e.g. the shared zip-proxy worker:
https://zip-proxy.erseco.workers.dev/?repo=<owner/repo>&release=<tag>&asset=<file>.zip
writeFile¶
Writes a file into the instance. path is resolved against the Nextcloud root
(/www/nextcloud) unless it starts with /. The contents come from content
(UTF-8 text, or base64 when "encoding": "base64") or are fetched from url
(handy for binary payloads too large to inline — the host must allow CORS).
Parent directories are created as needed.
{ "step": "writeFile", "path": "config/mimetypemapping.json", "content": "{\"elpx\":[\"application/vnd.exelearning.elpx\",\"application/zip\"]}" }
{ "step": "writeFile", "path": "data/admin/files/sample.elpx", "url": "https://raw.githubusercontent.com/owner/repo/main/fixtures/sample.elpx" }
Useful for registering a custom MIME type (write config/mimetypemapping.json +
config/mimetypealiases.json, then runOcc maintenance:mimetype:update-js and
maintenance:mimetype:update-db). To make a file show up in a user's Files view,
write it under that user's data dir and follow with runOcc files:scan <user>.
runOcc¶
Escape hatch for any occ command without a dedicated step.
{ "step": "runOcc", "args": ["maintenance:mode", "--off"] }
Maps to occ <args...>. Use for one-off commands; prefer the typed steps above
where they exist.
occ execution notes¶
- Every occ step runs through the shebang-free
console.phpwrapper with$_SERVER['REQUEST_URI']unset so patchedbase.phptreats it as CLI. - There is no shell — argv is passed as an array, never string-concatenated.
- Secrets go through env (
OC_PASS), not argv where avoidable. - A non-zero exit code surfaces the occ stdout/stderr for diagnosis.
debug.enabled¶
When true, PHP display_errors is on and error_reporting is E_ALL — useful
when a step fails. Leave it off for normal runs.
Validating a blueprint¶
make test # exercises blueprint normalization
make lint
When you add or change a step type, update assets/blueprints/blueprint-schema.json,
the step handler, this document, and add a unit test under tests/*.test.mjs.