Skip to main content

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:

  1. Migrations — runs any pending SQL migrations against the app's workspace
  2. Route scanning — discovers handler files in server/, webhooks/, and channels/
  3. Handler bundling — bundles all handlers with esbuild
  4. Route table — registers routes in the database
  5. Widget and channel extraction — parses widget definitions from informer.yaml, then validates and stores the channels: relay block (a bad channel or event name fails with 400); on a server that is not in origin mode, a non-fatal channels_require_origin_mode partial failure is added
  6. Area extraction — parses the areas: block into the app's feedback Area choices
  7. Integration materialization — creates or updates the Integrations declared in the integrations: block, each owned by the app, and seeds any env: keys as unset placeholders
  8. Resource resolution — resolves dataset, query, datasource, integration, and toolkit references from informer.yaml into junction tables (fails with 400 if any referenced resource doesn't exist)
  9. Tool scanning — discovers and bundles tool files from tools/
  10. 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"
}
}
FieldDescription
nameDisplay name in Informer (falls back to package name)
descriptionApp description (optional)
idApp 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:

  1. Open the app in the admin panel
  2. Navigate to the library's snapshot history
  3. 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.