serve

Serves a locally built custom front-end over HTTP while proxying API calls to a remote workspace. The fastest way to iterate on a custom UI without re-uploading after every change.

The folder being served must look like a compiled Mosaik.FrontEnd bundle — the CLI checks for a mosaik.app marker file in the root. With --watch the CLI compiles that folder for you.

Syntax

Usage: curiosity-cli serve --server <url> [--path <dir>] [--watch <project>] [--name <name>] [--port <port>] [options]

curiosity-cli serve \
  --server https://my-workspace.example.com/ \
  --path   ./MyApp/bin/Debug/netstandard2.0/tps/

If neither --path nor --watch is given, the current working directory is used. With --watch, the CLI compiles the project itself and serves what the compiler produced, so --path is optional.

If --server is omitted, the CLI tries to guess the workspace URL by scanning the *.js files in the bundle for a serverURL / apiURL variable — don't rely on this in real workflows, pass --server explicitly.

Options

Option Description
--server Workspace URL to proxy API calls to. Strongly recommended to set explicitly. Alias -s. Default: guessed from the bundle.
--path Local path to the compiled tps/ folder. Alias -p. Default pwd, or the folder produced by --watch.
--watch Path to a Transpose front-end project (a .csproj, or the folder holding one) to compile and keep watching. See Watch mode. Alias -w.
--configuration Build configuration used by --watch. Default Debug.
--name App name shown in the dev UI's nav bar. Default Mosaik.
--ignore-certificate-errors Disable TLS validation against the proxied workspace. Dev only. Default false.
--port Local TCP port to bind. Default 5000.
--cert-file Path to a certificate to enable HTTPS on the local listener.
--cert-password Password for the cert file, if encrypted.
--listen-to-any Bind to 0.0.0.0 instead of localhost. Useful for testing from another device on the same network. Default false.
--enable-cors Enable CORS on the local server. Default false.
--path-base Mount the dev server under a base path (e.g. /myapp).

How it works

Under the hood, serve:

  1. Starts a local Kestrel server pointing at your tps/ folder.
  2. Patches the served index.html so the browser loads your JS bundle, with the proxied server address, the loading splash, and cache-busting asset hashes injected. The folder is watched, so the injection is re-applied when its files change on disk.
  3. Proxies all /api/... calls to the configured --server — including text/event-stream responses, which are streamed through rather than buffered, and WebSocket connections, which are relayed as a real HTTP upgrade in both directions.
  4. With --watch, also compiles the front-end project and reloads the browser after each rebuild. Without it, serve only hosts what is already on disk — rebuild and refresh yourself.

For this to work, the remote workspace must allow CORS from the dev server's origin. Add http://localhost:5000 (or whatever port you used) to the MSK_CORS environment variable and restart the workspace process.

Watch mode (--watch)

serve normally hosts an already-compiled output folder. Pass --watch with a front-end project and it also compiles that project, serves what it produced, and keeps rebuilding as you edit:

curiosity-cli serve \
  --server https://my-workspace.example.com/ \
  --watch  ./MyApp/MyApp.csproj

What this changes:

  • --path becomes optional. The compiled site is what gets served, so there is nothing to point at on a fresh checkout. Pass --path anyway if you serve a folder assembled from more than just this project's output.
  • Referenced projects are watched too. The project's own sources and those of every project it references are watched, so editing a shared front-end library rebuilds the app. One save triggers one rebuild.
  • The browser reloads itself. The served index.html carries an injected script that opens a WebSocket back to the dev server; after each successful rebuild the page reloads. A client that reconnects while behind the current build is caught up immediately instead of waiting for the next edit.
  • A CSS-only change skips the compiler. When a change is confined to stylesheets, the CSS is re-copied and swapped into the page without a reload, so the running app keeps whatever state it was in.
  • A compile error leaves the previous build in place. The errors are printed in MSBuild's format; fix and save again. If the first build fails, or the project produces no site (no tps.json), serve reports it and exits with code 1.
  • No tps tool needed. The compiler runs in process, through the Transpose.Compiler.Library package, so the standalone Transpose compiler does not have to be installed for this path. Builds are incremental — an edit confined to method bodies reuses the cached JavaScript of every type it didn't touch, and an untouched referenced project isn't recompiled at all.

Everything else about serve still applies while watching: API proxying, CORS, --path-base, and the HTTPS options.

--configuration selects the build configuration (Debug by default). Use --configuration Release to reproduce a release build's output locally.

Typical workflow

See Custom Front-End development workflow for the full loop. The short version:

# Compile once, serve the output, rebuild and refresh by hand:
dotnet build
curiosity-cli serve -s http://localhost:8080 -p bin/Debug/netstandard2.0/tps

# Or let the CLI compile and rebuild for you:
curiosity-cli serve -s http://localhost:8080 -w .

# Iterate, then when ready:
curiosity-cli upload-front-end -s http://localhost:8080 -t $CURIOSITY_TOKEN -p bin/Debug/netstandard2.0/tps

Remarks

  • Use serve to iterate on a custom front-end locally while talking to a remote workspace, instead of re-uploading after each change. Add --watch to also compile the project and reload the page on every change.
  • Without --watch, the target folder must already be a compiled Mosaik.FrontEnd bundle (it must contain a mosaik.app marker). Either way, the remote workspace must allow CORS from the dev server's origin.
  • Pass --server explicitly; the URL-guessing fallback is for convenience only and shouldn't be relied on. --ignore-certificate-errors is for local development only.

See also

© 2026 Curiosity. All rights reserved.