Audience: Developer
All Install / Unbind / settings endpoints require manage access (assertCanManageProject).
POST /api/project/:id/app/installBody: { "templateId": number }
appreplaceTheme (template brand) → when pack.manifest.json exists: assert requiredDataSources, copy pack JS → project_plugin → load runtime → write bind lastDELETE /api/project/:id/appUnload runtime → delete project_plugin rows → delete shared/project/{id}/ → clear properties.app. Pages / theme / data remain. Extension routes fail closed afterward.
GET /api/project/:id/app/settingsPure GET. Returns bind snapshot plus catalogMissing: true when the catalog template id is gone.
PUT /api/project/:id/app/settingsBody: { "settings": { ... } } — codec encode against snapshotted settingsSchema.
project_plugin)| Column | Notes |
|---|---|
project_id + package_key |
UNIQUE |
template_id |
Catalog id at install |
version |
From pack manifest |
entry_path |
Relative to sharedRoot, under project/{id}/js/{version}/ |
asset_root |
project/{id} |
checksum |
sha256 of installed JS tree; verified on boot (mismatch skips that plugin) |
Presence of a row means installed/active (no disabled status in v1). Process boot selects only these rows — never scans shared/project/**.
/api/app)Convention: /api/app/:projectId/<relative-path>
project_plugin for that projectId.projectId must match the stamped project (anti-IDOR).shared/project/{id}/js/{version}/. Host API is capability-style (DatasetRecordService, guest token sign, bind snapshot, logger) — not a sandbox. Live settings for handlers come from request ctx.binding.auth |
Meaning |
|---|---|
public |
Anonymous / client-key / guest world; still project-scoped |
projectUser |
Valid project JWT (signed-in app user) |
lokakrtMember |
LokAkrt workspace user with project membership |
lokakrtManage |
Owner or workspace Admin |
POST /api/app/:projectId/guest/session (auth: public)
settings['guest.enabled']_dv / deviceKey; TTL from guest.tokenExpiresInguest-sessions via privileged host writes (dataset is not publicly writable){ guestId, restored } + Access-Token guest JWT (guestId claim; Context.isGuestAuth())Hosted/preview clients fingerprint with _dv and store the guest JWT separately from end-user AuthStatus.
| Status | When |
|---|---|
| 400 | Not an app / no bind / codec validation / missing required data source / guest disabled |
| 401 | Auth class failed / projectId stamp mismatch |
| 404 | Project or template missing; unbound / no runtime / unknown extension route |
| 409 | Already bound, or project has pages |
App templates rejected on page materialize paths return 400 with an Install hint (use the Install API).
assertNotAppTemplate on:
POST /api/template/instantiate (page multipage scaffold only)instantiatePageNo agent Install tool yet.
properties.app bind visible to managersmaskProjectPropertiesForClient — client settings keys + packageKey / ids; no settingsSchema / admin TTLmergeProjectProperties preserves app when omitted from a partial update.