Deployment
The informer-deploy command builds your project and uploads it to Informer in a single step.
Quick Start
npm run deploy
This runs npm run build followed by informer-deploy, which handles the entire deployment pipeline.
Deploying to Different Environments
Use --mode to target a specific Informer server. The command loads .env.<mode> for connection settings (see Environment Variables):
# Deploy to test server (loads .env.test)
npm run deploy -- --mode test
# Deploy to production (loads .env.production)
npm run deploy -- --mode production
What Happens During Deploy
The deploy command executes these steps in order:
1. Find or Create the App
The command looks up your app by the UUID stored in package.json (informer.id). It tries the /api/apps endpoint first, falling back to the legacy /api/reports endpoint for older servers.
If no app exists yet (first deploy), it creates one and saves the UUID to package.json for future deploys.
2. Update Metadata
If the app name, description, or icon in package.json differs from the server, the command updates them.
3. Create Snapshot
Takes a snapshot of the app's current library contents. This provides a rollback point — you can restore the previous version from the Informer UI if something goes wrong.
4. Stage the Deploy
Opens a staging library and points the app at it. Everything uploaded next lands there while the app goes on serving its current library, complete and untouched, so people using the app see the previous version for the whole deploy rather than a half-uploaded one.
The staging library's id belongs to this deploy, and the command presents it on
every file it uploads next, which is what routes those writes into the staging
library. Everyone else's content writes are locked out for the duration: a save
in the builder, or any API write that does not present the id, returns 409
instead of being accepted and then discarded when the deploy publishes.
Installing or updating a marketplace pack onto the app is refused on the same
grounds, before it applies anything. Reading is never affected, so people keep
using the app normally.
If a deploy is interrupted, the command discards its own staged upload and the
app unlocks. When the process dies before it can (a killed CI job, a dropped
connection), the app stays locked, and the next deploy stops with a 409 rather
than staging over an upload that may still be in flight. Discard the old one
yourself, then re-run the deploy:
curl -u "${INFORMER_USER}:${INFORMER_PASS}" \
-X DELETE "${INFORMER_URL}/api/apps/${APP_ID}/files/_stage"
See DELETE /apps/{id}/files/_stage
for the full contract, including the optional stagingLibraryId guard that
discards only your own upload.
A staged upload nobody clears is eventually swept by the app-sandbox-cleanup
system job, which discards any staged upload older than 24 hours. Because that
job runs once a day, an upload staged just after a sweep waits for the one after
next, so the worst case before collection is about 48 hours. Treat the sweep as a
backstop, not a recovery plan: discarding it yourself takes effect immediately.
5. Upload Built Assets
Uploads all files from dist/ into the staging library:
- Text files (
.html,.css,.js,.json,.svg,.md,.yaml, etc.) are uploaded as UTF-8 - Binary files (images, fonts, etc.) are uploaded as Base64
- Large files (>512KB) use chunked upload via the Flow.js protocol
6. Upload Config Files
Uploads informer.yaml (and data-access.yaml for backwards compatibility) from the project root, if they exist.
7. Upload Migrations
Uploads all files from migrations/ to the app's library, preserving the directory structure.
8. Upload Server Handlers and Webhooks
Uploads all files from server/ (authenticated routes) and webhooks/ (public routes) to the app's library. Channel handlers under channels/ (see Channels) belong in the library alongside them.
9. Upload Tools
Uploads all files from tools/ to the app's library. Tools are JavaScript files that agents can call during execution.
10. Run Deploy
Calls POST /api/apps/{id}/_deploy, which runs the full deploy pipeline:
- Migrations — runs any pending SQL migrations against the app's workspace
- Route scanning — discovers handler files in
server/,webhooks/, andchannels/ - Handler bundling — bundles all handlers with esbuild
- Route table — registers routes in the database
- Widget and channel extraction — parses widget definitions from
informer.yaml, then validates and stores thechannels:relay block (a bad channel or event name fails with 400); on a server that is not in origin mode, a non-fatalchannels_require_origin_modepartial failure is added - Area extraction — parses the
areas:block into the app's feedback Area choices - Integration materialization — creates or updates the Integrations declared in the
integrations:block, each owned by the app, and seeds anyenv:keys as unset placeholders - Resource resolution — resolves dataset, query, datasource, integration, and toolkit references from
informer.yamlinto junction tables (fails with 400 if any referenced resource doesn't exist) - Tool scanning — discovers and bundles tool files from
tools/ - Agent extraction — creates/updates agent records from
informer.yaml, stops agents removed from config
On success, the command sends { "managed": true } with this request, which
claims the App as platform-managed (origin: "deployed"). Your project becomes
the version of record: Informer locks the App out of the builder so nobody can
make an edit your next deploy would silently overwrite.
The claim is opt-in on that flag, not automatic. Other callers of the same route (the Redeploy button in the App's settings panel, for instance) post without it and leave the App editable, because re-running migrations and rescanning routes is useful on any App and shouldn't hand ownership away.
The claim only applies to an App that nothing else has claimed, meaning one
still at origin: "ui". Deploying against an App installed from a marketplace
runs the pipeline normally but leaves it at origin: "marketplace", because a
marketplace install stays bound to its listing and keeps receiving version
upgrades. Nothing in the deploy fails; the provenance simply doesn't move.
If a deploy fails, the claim is skipped: an App that never finished deploying stays editable rather than being locked to a project that couldn't publish it.
An App can be released back to origin: "ui" from its settings panel or via
POST /api/apps/{id}/_unmanage, but that only
changes what Informer users may do. Your project keeps the App's UUID in
package.json and still points at the same server, so the next deploy
re-claims the App and replaces its files.
A failed deploy fails the whole command. The staged upload is discarded and
the app is left exactly as it was, on its previous version and editable, so the
command reports the failure and exits non-zero rather than printing a success
line. That matters most in CI, where a publish that reported
success over a half-applied App would go green. The same applies when the
endpoint answers 404: rather than assuming an older server, the command says
so and names the likelier causes (a wrong App id, or a proxy rewriting the
path).
11. Print URL
Prints the app's URL and the total number of files uploaded. This line only appears when every earlier step succeeded.
Configuration
package.json
{
"informer": {
"name": "Sales Dashboard",
"description": "Regional sales overview",
"id": "a1b2c3d4-5678-90ab-cdef-123456789abc"
}
}
| Field | Description |
|---|---|
name | Display name in Informer (falls back to package name) |
description | App description (optional) |
id | App UUID — auto-generated and saved after first deploy |
.env
The deploy command reads connection settings from .env:
INFORMER_URL=http://localhost:3000
INFORMER_API_KEY=your-api-key
Or with basic auth:
INFORMER_URL=http://localhost:3000
INFORMER_USER=admin
INFORMER_PASS=yourpassword
CI/CD
The deploy command works in non-interactive environments. Set environment variables instead of using a .env file:
export INFORMER_URL=https://informer.example.com
export INFORMER_API_KEY=$INFORMER_API_KEY
npm run deploy
GitHub Actions Example
name: Deploy App
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run deploy
env:
INFORMER_URL: ${{ secrets.INFORMER_URL }}
INFORMER_API_KEY: ${{ secrets.INFORMER_API_KEY }}
GitLab CI Example
deploy:
stage: deploy
image: node:20
script:
- npm ci
- npm run deploy
variables:
INFORMER_URL: $INFORMER_URL
INFORMER_API_KEY: $INFORMER_API_KEY
only:
- main
Rollback
If a deploy introduces issues, you can restore the previous version from the Informer UI:
- Open the app in the admin panel
- Navigate to the library's snapshot history
- Restore the snapshot created before the last deploy
The deploy command creates a snapshot automatically before uploading, so there's always a rollback point.
Legacy Server Support
The deploy command automatically detects whether your Informer server supports the new /api/apps endpoint or only the legacy /api/reports endpoint. It tries the new API first and falls back transparently. You don't need to configure anything — it just works.