---
title: Running Apps
---

# Running Apps

## What it is

Omniscio ships a built-in **Running Apps** panel as a sidebar virtual project. Click **Running Apps** in the Omniscio sidebar group (emerald AppWindow icon) and the right pane swaps to a full-width list of the dev servers Omniscio is currently running for you — no sessions list, no chat composer, just the live apps.

It exists so that when a Claude session spins up a dev server (a Next.js app, a Vite frontend, an API), you have one place to see every one that's live, open it in-pane, and stop it — without hunting for ports or terminal tabs.

## Where to find it

### Enabling it

Running Apps is **off by default**. Turn it on at **Settings → Features → Running Apps** (or search settings for "running apps" / "portless" / "dev server"). Toggling it off hides the sidebar row and pauses the live-apps poll; toggling back on restores both.

## How it behaves

### What powers it: portless

Running Apps is a thin UI over [**portless**](https://www.npmjs.com/package/portless), a small dev proxy. Instead of each dev server grabbing a random port you have to remember (`localhost:3000`, `localhost:5173`, …), Omniscio launches it **behind the portless proxy**, which gives it a stable, human-readable hostname:

```
https://my-feature.localhost
https://api.localhost
```

The `<slug>.localhost` name is derived from the session/app name. `.localhost` always resolves to your own machine (it's a reserved loopback name), so these URLs only ever reach local dev servers — nothing leaves your computer.

### Requirements

Two hard prerequisites (Omniscio probes both before offering to use portless):

1. **portless installed and on your PATH** — `npm i -g portless`. You don't have to run this by hand: when portless is missing the panel's empty state shows a one-click **Install portless** button (see "The empty state is availability-aware" below).
2. **System Node 24 or newer** — portless declares `engines.node: ">=24"`. The relevant Node is the **system** `node` that runs the global `portless` binary, not Omniscio's bundled Electron runtime. When the system Node is below 24, the panel shows an "update Node first" message + a nodejs.org link and deliberately does **not** offer the install button (npm rejects the install below the `engines.node` floor).

### The empty state is availability-aware

When no apps are running, the panel probes readiness once on mount (the read-only `portless:availability` IPC — gated on `runningAppsEnabled` like every other handler, so it never spawns a probe while the feature is off) and shows one of:

- **portless installed** → plain **"No apps running"** (dev servers launched through portless appear here).
- **portless missing + Node ≥ 24** → an **Install portless** button. One click runs the install with a spinner; on success the panel flips to ready (no app restart). The install reuses Omniscio's existing toolchain installer — portless is a registered opt-in `npm-global` tool — so it also gets an Install button in **Settings → Connected Tools** and the **Tools** screen.
- **system Node < 24** → an **"update Node first"** message + a **Get Node 24+** link, no install button.
- **still probing** → the plain empty state (it never flashes the Install button at someone who already has portless).

The post-install detection reads the **fresh** system PATH (and routes the Windows `.cmd` shim through `cmd.exe`), so a just-installed global `portless` is found without relaunching Omniscio.

### What you see

A simple list, one row per live app:

- **Slug** — the app's short name (e.g. `my-feature`), taken from its `.localhost` hostname.
- **Port pill** — a green chip showing the dev server's local port (portless assigns one in the 4000-4999 range).
- **URL** — the browsable `https://<slug>.localhost` link. Click it to open the app **inside Omniscio** in an embedded browser view (a `<webview>`), so you stay in the app. A **Back** button returns you to the list.
- **Stop** — stops that preview (terminates the portless route). The list refreshes immediately.

A toolbar **Refresh** button re-reads the list on demand, and the panel also **live-updates on its own**: a background poll watches portless's route table and pushes changes to the panel every few seconds, so apps appear and disappear without you clicking anything.

If nothing is running you get an availability-aware empty state — a plain "No apps running", a one-click **Install portless** button, or an "update Node first" message (see "The empty state is availability-aware" above).

### HTTPS `.localhost` and certificates

portless serves previews over HTTPS using a **local certificate authority**. Chromium doesn't trust that CA by default, so Omniscio adds a narrow `certificate-error` exception scoped to `.localhost` hosts only — that lets the embedded preview load without a security warning. The exception is deliberately tight: it applies **only** to `localhost` / `*.localhost` names; any other site with a bad certificate is still rejected normally.

### Privacy & scope

- The embedded preview uses a **fresh, in-memory browser partition** per panel open — it doesn't share cookies or storage with the rest of Omniscio or with the persistent Browser pane.
- The desktop `.localhost` preview is loopback-only (`.localhost` never resolves off your machine) and the live-apps push is desktop-only. Viewing an app **on your phone** is a separate, opt-in path — the authenticated `/app-preview/` proxy over your Tailscale address (see "View a running app on your phone" above), reachable only by your logged-in, device-approved phone.

### Controlling it from an agent (CLI control server)

A spawned agent or external script can interact with Running Apps over Omniscio's bearer-authed CLI control server (`http://127.0.0.1:19519`), not just the in-app panel:

- `GET /portless/apps` — list the live previews (every running portless app, Omniscio- or agent-launched).
- `POST /portless/launch` — launch a preview for a dev server (`{ sessionId, sessionName, workDir, devCommand[], proxyPort?, tls? }`) → returns `{ slug, url }`.
- `POST /portless/stop` — stop a tracked preview (`{ slug }`).

Every route is **gated on the same `runningAppsEnabled` flag** — when the feature is off (the default) they all return **403**, so the agent surface is dead until you enable Running Apps. `launch` validates that `workDir` is a real folder before spawning and runs the dev command shell-safe; routes are bearer-authed and rate-limited like every other CLI endpoint. (An agent can also just run `portless` directly in its own shell — it still appears in the panel, because Omniscio reads portless's route table.)

### View a running app on your phone

When **mobile access over Tailscale** is on (Settings → the Smartphone button → enable Tailscale access; private to your own Tailscale devices by default, public only when you allow it), each running app also gets a **phone-openable preview link**. In the Running Apps panel a Smartphone icon appears on every row; click it to copy `https://<your-machine>.ts.net/app-preview/<slug>/`, then open that on your phone to see the **live dev server** — the mobile version of "build on the machine, preview in your hand".

How it works: Omniscio already carries its web app to your phone over Tailscale (private `serve` by default, public Funnel only when you turn that on), behind your phone login + device approval. The preview link rides that SAME secure path — an authenticated reverse-proxy forwards the phone's request to the running dev server's local port (looked up from portless's route table). It is **not a public link**: an unlogged-in or unapproved device is refused before any app bytes are returned, exactly like the rest of mobile access.

**One setup requirement — the dev server's base path.** For the live app to render correctly through the sub-path link, its dev server must serve under that base path. For Vite, start it with `--base=/app-preview/<slug>/` (the slug is the app's short name shown in the panel); other frameworks have an equivalent base-path setting. The proxy forwards requests transparently — it does not rewrite your app — so without the matching base path the page's absolute-root asset URLs won't resolve. Live reload (HMR) works too once the base path is set (its websocket is proxied through the same link).

**Security note (v1).** The preview is served on the same web address (origin) as the Omniscio web app, so a previewed app's scripts share that origin. This is fine for previewing **your own agents' dev servers** (your own code, reachable only by your own approved phone) and is the accepted v1 scope; Omniscio does not yet isolate previews on a separate origin, so don't use it to host untrusted third-party apps. Turn the whole feature off any time with `AMC_DISABLE_MOBILE_APP_PREVIEW=1`, or by turning Running Apps off.

## For agents

### Where it lives in the code

- Sidebar entry + icon: `src/renderer/src/integrations/ui-registry.ts` (`id: 'running-apps'`, AppWindow icon, `panelOwnsLayout: true`).
- Manifest + virtual project sentinel: `src/shared/integrations/running-apps.ts` and `RUNNING_APPS_PROJECT_ID = '__running_apps__'` in `src/shared/virtual-project-ids.ts`.
- Panel UI: `src/renderer/src/features/running-apps/RunningAppsPanel.tsx`.
- IPC surface (list / launch / stop + the `portless:apps-changed` live push): `src/shared/ipc-channels/portless.ts`, handlers in `src/main/ipc/handlers-portless.ts`.
- CLI control-server routes (`/portless/apps|launch|stop`): `src/main/services/cli/cli-server-portless-routes.ts`, registered in `src/main/app/startup/register-cli-routes.ts`.
- Backend manager + portless launch / route reader / availability probe + the live-apps poll: `src/main/services/portless/`.
- Availability-aware empty state: the read-only probe handler `PORTLESS_AVAILABILITY` in `src/main/ipc/handlers-portless.ts` → its system probe `src/main/services/portless/portless-availability-probe.ts` (fresh PATH + `.cmd` shim) → pure UI decision `src/renderer/src/features/running-apps/portless-empty-state.ts`. The **Install portless** button reuses `TOOLCHAIN_INSTALL_TOOL`; portless is registered as an opt-in `npm-global` tool in `src/main/services/toolchain/toolchain-registry.ts` (+ `toolchain-checks.ts` / `toolchain-install.ts` / `src/shared/tool-provenance.ts` / the `ToolId` union).
- `.localhost` certificate trust: `src/main/localhost-cert-trust.ts` (wired into `hardenWebContents` in `src/main/webcontents-hardening.ts` — index.ts is intentionally untouched).
- Mobile app-preview reverse proxy (view a running app on your phone): `src/main/services/web/web-access-preview-proxy.ts`, wired into `web-access-http-routes.ts` (`dispatchAuthedRoute`, HTTP) + `web-access-server.ts` (`handleUpgrade`, WS); the panel's phone link reuses `IPC.TUNNEL_STATUS`. The `preview-*` invariants in `.claude/memory/contracts/running-apps-contract.md`.

## Related

No sibling library page covers the dev-server preview surface. [INDEX.md](INDEX.md) is the library index, the way to find the neighbouring pages on sessions and on the CLI control server that this panel's routes belong to.
