8.5 KiB
Installed application packages — host API v1
Conductor v1.1.0 can install browser applications independently of its image. An installed package contains browser assets, application metadata, checksums and optional API route declarations. It contains no server-side module that Conductor executes.
This interface is for trusted Skeleton Works packages installed by the VM administrator. Package JavaScript runs on the Conductor origin and has the signed-in user's browser privileges. It is not a sandbox or a marketplace for untrusted uploads. Customers who can administer the VM already control its software.
Files and identity
A package directory contains conductor-app.json and its declared assets. See the minimal example and its browser entry.
- packageVersion and hostApiVersion are both 1.
- application uses the existing included-app metadata: schemaVersion, stable id, name, description, version, launchPath, optional configurationPath, and audience.
- launchPath is one non-reserved top-level path such as /example-application. Query views and descendant paths are handled by that app.
- entry.script names an ES module. entry.style and entry.icon are optional.
- files maps each packaged asset path to its SHA-256 checksum.
- api, when present, declares the app's own /api/APP_ID namespace and allowlisted routes.
Limits: manifest 64 KiB, 1–200 declared assets, 10 MiB per asset and 20 MiB total. Relative paths and checksums are checked, symlinks are rejected, and package route collisions are refused. A malformed update leaves the currently selected package in place.
The browser entry exports mount(element, context). It returns an unmount function to release timers, listeners and its UI. Context contains hostApiVersion, public application metadata and the signed-in user's id, displayName and role. It contains no backend token, origin, connection configuration or social credentials.
Conductor loads the package's module and optional stylesheet, retains the shared title/favicon behavior, and removes the app UI/style on unmount. Give each version its own content checksums. Packages should scope their styles and bundle their runtime dependencies; the host does not supply a particular React version. Native ES-module loading uses webpack's documented import exclusion.
Install using the running container
Stage a verified package through the running container. This works with the read-only root filesystem and its writable /tmp mount:
docker exec CONDUCTOR_CONTAINER mkdir -m 700 /tmp/example-package
tar -C /path/to/package -cf - . | docker exec -i CONDUCTOR_CONTAINER tar -xf - -C /tmp/example-package
Then install it:
docker exec -i -w /app CONDUCTOR_CONTAINER \
node dist/scripts/installedApps.js install /tmp/example-package < connection.json
For an app without an API, provide empty standard input instead of a connection file. No Node.js installation is needed on the VM host.
The connection file is private server configuration:
{
"origin": "http://application-backend:8080/service",
"token": "REPLACE_WITH_THE_EXISTING_INTERNAL_SERVICE_TOKEN",
"values": {"workspaceId": "customer"}
}
The origin is a fixed HTTP(S) backend address. It cannot contain URL credentials, a query or a fragment. Keep this input out of shell arguments, logs and browser assets. Conductor encrypts it in SQLite using its existing persistent secret key. Social-network credentials remain in the backend that owns them, such as Node-RED.
Installation verifies every declared asset, stages immutable files beneath /data/applications/APP_ID/DIGEST, and atomically selects the package, connection and catalog metadata in SQLite. When updating, omitted connection input preserves the existing connection. Installing identical content again reuses its digest. An installer should health-check its backend before selecting the package. No Conductor restart or rebuild is needed.
Other commands:
docker exec -w /app CONDUCTOR_CONTAINER node dist/scripts/installedApps.js capabilities
docker exec -w /app CONDUCTOR_CONTAINER node dist/scripts/installedApps.js list
docker exec -w /app CONDUCTOR_CONTAINER node dist/scripts/installedApps.js remove APP_ID
Capabilities reports hostApiVersion 1. List returns package IDs, versions and digests without connection secrets. Remove unselects the package, removes its connection and catalog entry, and retains immutable files for recovery. It does not delete the application's own database, accounts or posts. Automatic removal of unused package files is deferred.
API declaration
Each route declares path, methods and access. Literal path segments match exactly. A segment such as :postId permits letters, digits, underscores and hyphens. A segment such as {workspaceId} must equal the named value in the server-side connection. Encoded separators, traversal and unlisted operations are rejected.
Access modes:
| Mode | Enforcement |
|---|---|
| user | An enabled Conductor session; mutation requests also need its CSRF token. |
| admin | The same checks plus Conductor administrator role. |
| public | Read-only GET routes explicitly intended for public metadata. |
| bearer | A caller-supplied Bearer token is forwarded to the application backend, which validates its scopes. Conductor's internal token and user identity are not added. |
An admins-only application also requires administrator access for its signed-in API routes and executable assets. Public decorative icons may be read without a session.
The optional headers map permits X- headers bound to principal:userId, principal:role or config:VALUE_NAME. The bridge supplies these from trusted server context, ignoring caller attempts to spoof identity. For signed-in/public routes it supplies the internal service token. It forwards only Content-Type, Idempotency-Key and X-Upload-Name from other caller headers.
Optional query lists the only query parameters forwarded. JSON is the default response; response: image permits JPEG, PNG and WebP responses. Requests/responses are bounded to approximately 2 MB and backend requests time out after 60 seconds. Upstream redirects are rejected. There is no automatic retry after an uncertain request.
A signed-in GET route with redirect: true handles an OAuth-style callback. Its allowlisted query is sent to the backend, the response body is discarded, and Conductor redirects only to that app's launch path, with connection=failed on failure. Callback requests bypass access logging and the redirect uses a no-referrer policy.
Application packages do not bypass Node-RED authorization. The backend remains responsible for account access, token scopes, scheduling rules and social-network behavior.
Persistence and recovery
The database contains active package metadata and encrypted connections; /data/applications contains immutable browser files. Both reside inside the existing persistent Conductor data volume.
The v1.1.0 backup tools use conductor-backup-v2 and include the exact app assets referenced by their SQLite snapshot, plus the persistent keys and Compose metadata. Restore supports v1 and v2; old v1 backups contain no independently installed app assets. Keep matching images, data, packages and keys together. App removal retains files, so restoring metadata does not depend on a remote package registry.
Verification and migration
After building Conductor and Social Scheduler's package, run:
node scripts/test-installed-apps.mjs /absolute/path/to/social-scheduler
This verifies a fresh empty host, the real scheduler frontend, a second independent app, updates/removal/restarts, access roles, CSRF, bearer delegation, fixed workspaces, query filtering, response bounds, callback privacy and encrypted connection storage. The production lifecycle check additionally tests app-file backup/restore and container recreation.
Social Scheduler 0.5.0 uses this interface. Its /social-scheduler and /api/social-scheduler paths are unchanged. The previous 0.4.0 kit built a combined Conductor image; simply replacing that image is not a complete migration. Preserve its configuration and data, update the core through the supported upgrade procedure, then install the 0.5.0 package with the existing Node-RED connection. Fresh setup changes are tracked in SLICE12; the existing-VM upgrade procedure is SLICE13.
The bridge rejects a JSON response that reflects its private connection token, including backend error/echo responses. It returns a generic service error and never logs the upstream body.