Skip to main content

lumio deploy

Upload a built extension bundle to Lumio as a new version. The version starts in draft status and must go through the submission wizard to enter the review process.

Usage​

lumio deploy [-v <version>] [-d <description>] [--breaking] [--draft] [--token <token>]

Example​

# Build first, then deploy
lumio build
lumio deploy -v 1.2.0 -d "Added live score updates from ESPN"

Or let it read the version from lumio.config.json automatically:

lumio deploy -d "Fixed score display alignment"

Options​

FlagAliasDescription
--version <semver>-vVersion string (default: reads from lumio.config.json)
--description <text>-dOverride the uploaded release notes
--breaking-Flag this version as a breaking change (disables auto-update for existing installs)
--draft-Upload as draft without triggering the submission flow (default behavior - this flag is explicit)
--token <token>-API token for authentication (skips interactive login). Also reads from LUMIO_TOKEN env var
--outdir <path>-Location of the built bundle (default: dist)

Pre-flight checks​

Before uploading, the CLI runs these validation checks:

  1. Sound file format - each file in dist/sounds/ is validated against the supported audio formats (MP3, WAV, OGG, WebM, M4A, AAC, FLAC) and checked for existence
  2. Sound count - the number of sound files is checked against the resolved max_sounds limit (fetched from the API via GET /v1/developer/extensions/{id}/limits)
  3. Per-file size - each sound file is checked against the resolved max_sound_file_size limit
  4. Total storage - the combined size of all sound files is checked against the resolved max_sound_storage_bytes limit
  5. Manifest validation - lumio.config.json is validated against the extension schema; a --version override is written into the uploaded manifest so its version matches the request
  6. Data schema - when the project has server/schema.ts (or server/schema.js / server/schema.mjs), it is bundled and its default export must be a defineSchema(...) result; a schema file that does not build or exports something else stops the deploy
  7. Functions manifest - when an extension with "server": true (not an automation node) has server/functions.ts, dist/functions.json must exist; run lumio build first
  8. Changelog entry - the release notes come from the ## [x.y.z] - YYYY-MM-DD section of CHANGELOG.md for the deployed version, unless -d overrides the uploaded text. A missing changelog is allowed. If one exists but has no matching version entry, a local deploy requires -d; CI can upload without notes

If any check fails, the deploy is blocked with a descriptive error message.

What happens on deploy​

  1. CLI reads lumio.config.json and validates the extension
  2. All built files from dist/ are collected (JS, CSS, HTML, images, fonts, video, audio, JSON, Lottie, source maps)
  3. Sound files placed in dist/sounds/ are registered as extension-bundled sounds - they appear in the account's sound library when the extension is installed
  4. Each file is uploaded individually as a multipart files field, named by its /-separated path relative to dist/ (also on Windows) (including functions.json, the functions manifest) alongside source.tar.gz, manifest, release notes and, when the project has a server/schema.ts, the data_schema field (the defineSchema result as JSON). Lumio checks functions.json against that data schema (format version, names, filter operators, fixed row ids, and every table must be declared); a failed check rejects the upload with 400 and the reason, e.g. functions.json: `getRules` uses table `missing`, which the version's data schema does not declare
  5. A new ExtensionVersion record is created with status draft
  6. The server derives each bundle and source storage key from the extension ID and validated manifest version; the client does not supply storage keys
  7. Each file gets a row in extension_version_files with content-type, size, and content hash

Supported file types​

CategoryExtensions
Code.js, .css, .html, .map
Images.png, .jpg, .jpeg, .gif, .svg, .webp, .avif, .ico
Video/Audio.mp4, .webm, .ogg, .mp3, .wav
Fonts.woff, .woff2, .ttf, .otf, .eot
Data.json, .lottie

Every served bundle file uses X-Content-Type-Options: nosniff. HTML, SVG, JSON, source maps, Lottie, and other content outside JavaScript, CSS, raster images, fonts, audio, and video also use Content-Security-Policy: sandbox and Content-Disposition: attachment. The dashboard bundle proxy applies the same headers. SVG still works as an image resource.

Size limits​

LimitValue
Per file10 MB
Total upload50 MB
Max files per version200
Deploying Sports Scoreboard v1.2.0...

Uploading 8 files (1.2 MB total)...
layer.js 312 KB application/javascript
editor.js 525 KB application/javascript
styles.css 24 KB text/css
assets/logo.png 89 KB image/png
...
Upload successful. Status: draft
Submit for review via the dashboard or `lumio status`.

Data schema​

The tables in server/schema.ts travel with the version as data_schema (see Database schema). Lumio checks them at upload and creates them when the version is approved; a draft gets no tables. The upload fails with 422 when the schema is rejected, and the CLI prints why:

  • INVALID_DATA_SCHEMA - the server's message, for example a name outside ^[a-z][a-z0-9_]{0,39}$, a pg_* name or an ext_* table name, an unsupported type, a .storage() scope other than instance, more than 32 tables or 64 columns per table, or a schema over 64 KiB.
  • DATA_SCHEMA_NOT_ADDITIVE - one line per violation of the additive-only rule:
Deploy failed (HTTP 422):
Data schema change is not additive. Published tables only allow new tables, new optional columns and relaxing NOT NULL:
- points.score: type_change (applied: double precision, declared: text)

rule is type_change, new_not_null_column or not_null_tightening. Fix the schema and deploy the version again.

Backward compatibility​

Older CLI versions that send a single bundle field continue to work. The API treats the single bundle as bundle.js and processes it through the same pipeline.

Direct API clients creating a version with POST /v1/developer/extensions/\{id\}/versions must supply a valid manifest whose extension ID and version match the request. Submitting a draft revalidates its stored manifest. Team-owned version creation and upload require team-versions:create; submission requires team-extensions:publish.

CI/CD deployment​

In CI environments, authenticate with either the LUMIO_TOKEN environment variable or the --token flag:

# Via environment variable
LUMIO_TOKEN=${{ secrets.LUMIO_TOKEN }} lumio deploy -v 1.2.0 -d "Automated release"

# Via --token flag
lumio deploy -v 1.2.0 -d "Automated release" --token ${{ secrets.LUMIO_TOKEN }}

The CLI detects CI=true and skips interactive prompts. When --token is provided, it takes precedence over the stored credential.

Breaking changes​

Mark a version as breaking to prevent auto-updates for users who have not manually reviewed the breaking change notes:

lumio deploy -v 2.0.0 --breaking -d "Storage format changed — manual migration required"

Breaking versions show a notice in the extension store and require users to manually accept the update.

Exit codes​

Exit codeMeaning
0Deployment succeeded
1Authentication error (run lumio login)
2Validation error (missing build, invalid version, etc.)
3Upload error (network failure, rate limit)
4Extension not found (invalid Extension-ID)