Skip to content

Repository files navigation

VisualPlanner

A tiny self-hosted server for storing and browsing self-contained HTML "plans". AI agents push plans as HTML via an authenticated REST API; humans browse them in a React UI. Designed to run on a headless box reachable over a Tailnet.

Each plan is a single .html file stored on disk, plus an index.json catalog. The index is rebuilt from the .html files on startup, so deleting index.json is non-destructive.

Prerequisites

  • Bun (runtime + test runner)

Quick start

cp .env.example .env          # then edit .env and set VISUALPLANNER_TOKEN
bun install
bun run dev                   # API server on :7070 (watches for changes)
bun run dev:ui                # Vite UI on :5173 (proxies /api -> :7070)

Open http://localhost:5173 in development.

For production (builds the UI, then serves it from the API origin on :7070):

bun run start

Scripts

Script Description
bun run dev Run the API server with --watch (port 7070).
bun run dev:ui Run the Vite dev server for the UI (port 5173, proxies /api).
bun run build Build the UI into dist/client/.
bun run start Build, then run the production server (serves UI + API on :7070).
bun run test Run the test suite (bun:test).
bun run typecheck Type-check with tsc --noEmit.
bun run lint Lint with Biome.
bun run format Format the codebase in place with Biome.
bun run check Apply Biome's safe lint + format fixes in place.

Configuration

All settings are optional environment variables (read from .env in the working directory, with real process.env taking precedence):

Variable Default Meaning
VISUALPLANNER_TOKEN change-me (disabled) Bearer token agents must present to write. Unset/change-me = no auth.
VISUALPLANNER_PORT 7070 Port to listen on.
VISUALPLANNER_DATA_DIR ~/.local/share/visualplanner Where plan .html files and index.json are stored. ~ is expanded.
VISUALPLANNER_BIND auto (Tailscale IP, else 127.0.0.1) Hostname to bind. Empty/unset auto-detects a Tailscale 100.x address.

Auth model

  • Writes (POST/PUT/DELETE) require the token, sent as either Authorization: Bearer <token> or X-VisualPlanner-Token: <token>.
  • Reads (GET) are always unauthenticated, even when a token is set.
  • If VISUALPLANNER_TOKEN is unset or literally change-me, auth is disabled entirely. The server then refuses to bind beyond localhost — if no token is set and the resolved bind address is not loopback, startup aborts with an error. Set a real token to listen on a Tailscale interface.
  • The API is same-origin only (no CORS headers). The SPA is served from the same origin, so it works unchanged; other websites cannot read plans cross-origin. Reads are still unauthenticated, so anyone who can reach the host directly can read plans — keep sensitive content out of them.

Generate a token with:

openssl rand -hex 32

REST API

Method Path Auth Description
GET /api/plans none List plan metadata, newest-first.
GET /api/plans/:id none Get one plan's metadata.
GET /api/plans/:id/html none Get a plan's raw HTML (text/html).
POST /api/plans token Create a plan. Returns 201 { plan }.
PUT /api/plans/:id token Update a plan in place (id stable).
DELETE /api/plans/:id token Delete a plan. Returns { ok: true }.

Create / update body

{
  "project": "my-project",        // default "untitled"
  "title": "Refactor the foo",    // default "Untitled plan"
  "sessionId": "abc123",          // optional
  "tags": ["refactor", "backend"], // optional
  "html": "<!doctype html>..."    // required, non-empty
}

The plan id is derived as slugify(project)-slugify(title), with a -N suffix appended on collision (e.g. my-project-refactor-the-foo-2).

Example

curl -X POST http://localhost:7070/api/plans \
  -H "Authorization: Bearer $VISUALPLANNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"project\":\"demo\",\"title\":\"hello\",\"html\":\"<h1>Hi</h1>\"}"

Project layout

src/
  client/        Vite + React SPA (shadcn/ui)
  components/    App components + vendored shadcn ui/ primitives
  server/        Hono + Bun API, disk-backed Store, config, auth
  shared/        Types shared between client and server
skill/           Agent authoring skill + canonical plan stylesheet (plan.css)

The skill/ directory holds the publishing skill that agents use to generate plans in the VisualPlanner house style, plus plan.css (the canonical stylesheet) and gallery.html (an example).

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages