Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ vx mcp # Model Context Protocol server (stdio
vx coordinator build test --workers 4 # start a distributed-CI coordinator
vx run --worker ws://coord:5180 # pull tasks from a coordinator and execute them

vx serve --ui --open # unified backend + bundled insights SPA + open browser
vx serve --ui --open # unified backend + embedded dashboard + open browser
# /v1/* JSON, SSE events, WS run protocol, CORS *
```

Expand All @@ -86,12 +86,12 @@ vx serve --ui --open # unified backend + bundled insights S
natively; every event flows to Grafana / Honeycomb / Datadog /
Tempo with zero bridge package.
- **Self-host vx serve.** Same backend everywhere — laptop, Docker,
any container runtime. JSON `/v1/*` insights API + WebSocket run
any container runtime. JSON `/v1/*` metrics API + WebSocket run
protocol + SSE event stream + permissive CORS. One stack.
- **Insights dashboard built in.** `vx serve --ui` bundles a Solid
- **Dashboard embedded in the binary.** `vx serve --ui` serves a Solid
SPA at `/` — task averages, p50/p99, cache savings, recent runs,
flamegraphs. Connection picker switches between local and hosted
backends; same UI for both.
flamegraphs — compiled into `vx` itself, nothing to install.
Connection picker switches between local and hosted backends.

Each lives behind a one-paragraph design doc under
`docs/design/*-2026-06.md`. Phase-by-phase implementation log:
Expand Down Expand Up @@ -309,7 +309,7 @@ Production readiness for the **2026-06 platform layer**:
| `vx coordinator` + `vx run --worker` | **shippable for self-hosted CI** | content-addressed assignment, disconnect recovery |
| Plugin API | **shippable** | crash-isolated, lifecycle hooks fire end-to-end |
| Predictive scheduling | **shippable as opt-in** | gated on `predictive: true` + observed data |
| `apps/insights/` (Solid SPA → vx serve HTTP) | **scaffold** | connection picker, HTTP /v1/\* reads; pages need real-world iteration |
| `apps/ui/` (Solid dashboard, embedded in binary) | **scaffold** | connection picker, HTTP /v1/\* reads; pages need real-world iteration |
| OTel native emit (`src/orchestrator/otel-emit.ts`) | **shippable** | env-var auto-attach in `run()`; ships event stream to any OTLP backend |

## Development
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default defineConfig({
{ label: 'Distributed CI execution', link: '/guides/distributed-ci/' },
{ label: 'Writing a vx plugin', link: '/guides/plugins/' },
{ label: 'Predictive scheduling', link: '/guides/predictive-scheduling/' },
{ label: 'Insights dashboard', link: '/guides/insights/' },
{ label: 'Dashboard', link: '/guides/dashboard/' },
{ label: 'Self-host vx serve', link: '/guides/self-hosting/' },
{ label: 'OpenTelemetry CI/CD spans', link: '/guides/otel-bridge/' },
{ label: 'vx serve wire protocol', link: '/guides/wire-protocol/' },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
---
title: Insights dashboard
description: A Solid SPA bundled into vx serve. Run history, per-task averages, cache stats. One flag, no daemon.
title: Dashboard
description: A Solid SPA bundled into the vx binary. Run history, per-task averages, cache stats. One flag, no daemon, nothing to install.
---

The insights dashboard ships inside `vx serve` itself. Pass `--ui`
and the same backend that drives `vx run` delegation also serves
the SPA at `/`. One process, one stack.
The dashboard ships **inside the `vx` binary**. Pass `--ui` to
`vx serve` and the same backend that drives `vx run` delegation also
serves the dashboard at `/`. No separate install, no asset directory
on disk — the SPA is embedded in the executable.

## Quick start

Expand All @@ -17,11 +18,11 @@ vx serve --ui --open
That:

1. boots `vx serve` on a kernel-assigned port,
2. serves the bundled insights SPA at `/`,
2. serves the embedded dashboard at `/`,
3. opens your default browser at the same origin.

`Ctrl-C` stops it. Drop `--open` if you'd rather not auto-launch
a browser; drop `--ui` to get just the JSON API + WS + SSE.
`Ctrl-C` stops it. Drop `--open` if you'd rather not auto-launch a
browser; drop `--ui` to get just the JSON API + WS + SSE.

Pin a port with `--port`:

Expand Down Expand Up @@ -57,7 +58,7 @@ host the SPA once and let everyone aim it at their own backend.
```
Browser
┌─────────────────────────────┐
│ apps/insights SPA (Solid) │
│ apps/ui SPA (Solid)
│ • connection picker │
│ • fetch over HTTP │
└────────┬────────────────────┘
Expand All @@ -67,7 +68,7 @@ host the SPA once and let everyone aim it at their own backend.
┌─────────────────────────────┐
│ vx serve --ui (Bun.serve) │
│ • /v1/* JSON over cache.db │
│ • SPA static at /
│ • SPA embedded in binary
│ • CORS * │
│ • SSE event stream │
│ • WS run protocol │
Expand All @@ -80,6 +81,10 @@ host the SPA once and let everyone aim it at their own backend.
└─────────────────────────────┘
```

The dashboard builds to a single self-contained `index.html` (JS +
CSS inlined), which the binary embeds via Bun's `with { type: 'file' }`.
A compiled `vx` carries it with nothing else on disk.

## HTTP surface

`vx serve` exposes:
Expand Down Expand Up @@ -107,28 +112,28 @@ All routes ship `Access-Control-Allow-Origin: *`.

## Host the SPA once, point it anywhere

You can also build `apps/insights/dist/` and deploy it to any static
host. The connection picker means the same hosted bundle works
against any reachable `vx serve` — browsers allow HTTPS pages to
call `http://localhost:*` per the Secure Context exception, so a
hosted `https://insights.example.com` reading from a local
`http://localhost:4321` Just Works.
You can also build `apps/ui/dist/` and deploy the single
`index.html` to any static host. The connection picker means the
same hosted bundle works against any reachable `vx serve` — browsers
allow HTTPS pages to call `http://localhost:*` per the Secure
Context exception, so a hosted `https://dash.example.com` reading
from a local `http://localhost:4321` Just Works.

## Privacy

When you run `vx serve --ui` locally, nothing leaves your machine.
A hosted SPA pointed at `http://localhost:*` is also entirely
local — the picker is just configuration; the page reads from
your machine, not a third party.
local — the picker is just configuration; the page reads from your
machine, not a third party.

## Known limits

- **No real-time view yet.** SSE event streaming exists on the
server (`/v1/events`) but the SPA doesn't subscribe yet. Reload
to see new runs.
- **No auth.** `vx serve` binds to localhost by default; trust is
by network reachability. Add a reverse proxy with auth for
hosted deployments.
by network reachability. Add a reverse proxy with auth for hosted
deployments.

See also: [`Self-hosting`](/vx/guides/self-hosting/),
[`Wire protocol`](/vx/guides/wire-protocol/).
23 changes: 13 additions & 10 deletions apps/docs/src/content/docs/guides/self-hosting.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,25 +84,28 @@ vx.example.com {
}
```

## Point the SPA at it
## Dashboard

Build `apps/insights/` once and host the resulting `dist/`. Users
open it, paste the server origin into the connection picker, and
the SPA reads via `/v1/*`. No build step per user, no per-user
config — same SPA, any backend.
`vx serve --ui` serves the dashboard at `/` directly from the binary
(it's embedded — no asset directory to deploy). For a hosted
deployment behind a proxy, that's all you need.

If you'd rather host the dashboard separately, build the single-file
bundle and drop it on any static host. The connection picker means
the same bundle works against any reachable `vx serve`:

```sh
cd apps/insights
cd apps/ui
bun install
bun run build
# Deploy dist/ to any static host (S3 + CloudFront, Vercel, GitHub
# Pages, your own nginx — anywhere).
# Deploy the single dist/index.html anywhere (S3 + CloudFront, Vercel,
# GitHub Pages, your own nginx).
```

## Browser → localhost gotcha

The Secure Context exception in WHATWG lets HTTPS pages call
`http://localhost:*`. So a hosted `https://insights.example.com`
`http://localhost:*`. So a hosted `https://dash.example.com`
can read from `http://localhost:4321` without breaking the mixed-
content rule — that's intentional, and what the connection picker
exploits.
Expand All @@ -120,5 +123,5 @@ story. We unified on `vx serve`:
- The hosted SPA sees the same `/v1/*` shape locally or against a
multi-tenant deployment — no shimming.

See also: [`insights`](/vx/guides/insights/),
See also: [`dashboard`](/vx/guides/dashboard/),
[`wire protocol`](/vx/guides/wire-protocol/).
7 changes: 4 additions & 3 deletions apps/docs/src/content/docs/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ require additional services:
- **[Self-host vx serve](../guides/self-hosting/)** — drop the binary
in Docker, get a hosted backend with the same JSON `/v1/*` shape
the local one has. One stack, no separate cloud project.
- **[Insights dashboard](../guides/insights/)** — Solid SPA bundled
into `vx serve --ui`. Run history, per-task averages, cache stats.
Connection picker switches between local and hosted servers.
- **[Dashboard](../guides/dashboard/)** — Solid SPA embedded in the
`vx` binary, served by `vx serve --ui`. Run history, per-task
averages, cache stats. Connection picker switches between local and
hosted servers.
- **[OpenTelemetry CI/CD spans](../guides/otel-bridge/)** — set
`OTEL_EXPORTER_OTLP_ENDPOINT`, install the three OTel peers; every
event lands in Grafana / Honeycomb / Datadog / Tempo natively, no
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const platform = [
icon: 'cloud',
tone: 'var(--plasma)',
title: 'Self-hostable, Docker-ready',
body: 'vx serve runs locally or in Docker — same backend everywhere. The hosted insights SPA points at any reachable origin via a connection picker. No separate cloud stack, no CF lock-in.',
body: 'vx serve runs locally or in Docker — same backend everywhere. The dashboard is embedded in the binary and points at any reachable origin via a connection picker. No separate cloud stack, no CF lock-in.',
},
{
icon: 'brain',
Expand Down
54 changes: 0 additions & 54 deletions apps/insights/README.md

This file was deleted.

128 changes: 0 additions & 128 deletions apps/insights/src/components/Shell.tsx

This file was deleted.

Loading
Loading