Skip to content
Open
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
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,13 @@ tests
benchmarks
skills
proposals
# webapp/ is copied by demo/Dockerfile — ship only its source, never the
# host's node_modules (breaks cross-arch: npm ci reinstalls in-image) or the
# generated dist/. The root vouch image ignores webapp entirely.
webapp/node_modules
webapp/dist
webapp/test-results
webapp/playwright-report
webapp/.superpowers
**/__pycache__
**/*.py[cod]
18 changes: 18 additions & 0 deletions demo/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# vouch demo — environment (optional). Copy to .env to override defaults.
#
# cp .env.example .env

# Bearer token vouch requires on its HTTP transport. The console injects it
# server-side, so the browser never sees it. Any non-empty value works for the
# local demo; change it if you like.
VOUCH_HTTP_TOKEN=vouch-demo

# Optional — your own Anthropic API key. Set it to turn on the two LLM-backed
# actions in the console: "Compile" (approved claims -> topic pages) and
# "Summarize session". Leave it blank and the demo still runs; those two just
# report "not configured". The key stays in this container, calls go straight
# to Anthropic, and nothing is committed.
ANTHROPIC_API_KEY=

# Optional — override the model the shim uses (defaults to a current Sonnet).
# ANTHROPIC_MODEL=claude-sonnet-4-5
58 changes: 58 additions & 0 deletions demo/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# syntax=docker/dockerfile:1
#
# vouch demo — one image, one command. Bundles the vouch server (built from
# THIS checkout, so it carries the newest kb.* surface incl. delete / archive /
# supersede), the vouch-ui web console, and seeds a starter knowledge base on
# first run. `docker compose up` in this folder, open the browser, and explore
# a populated, review-gated KB.
#
# Two runtimes in one image on purpose: python runs `vouch serve`, node serves
# the console via `vite preview` (which reuses the console's own /proxy/*
# middleware, pinned at the in-container vouch endpoint).

# ---- stage 1: build the console to static (keep the tree for vite preview) ---
FROM node:22-slim AS web
WORKDIR /web
COPY webapp/package.json webapp/package-lock.json ./
RUN npm ci
COPY webapp/ ./
RUN npm run build

# ---- stage 2: runtime = node (console) + python (vouch from source) ---------
FROM node:22-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
LANG=C.UTF-8 \
NODE_ENV=production \
VOUCH_UI_ALLOW_REMOTE=1 \
VOUCH_TARGET=http://127.0.0.1:8731 \
VOUCH_HTTP_TOKEN=vouch-demo \
VOUCH_DATA_DIR=/data \
ANTHROPIC_MODEL=claude-sonnet-4-5

RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-venv curl \
&& rm -rf /var/lib/apt/lists/*

# vouch from this checkout — the [web] extra brings the HTTP transport in.
COPY pyproject.toml README.md /src/
COPY src /src/src
COPY adapters /src/adapters
RUN python3 -m venv /opt/venv && /opt/venv/bin/pip install --no-cache-dir "/src[web]"
ENV PATH="/opt/venv/bin:$PATH"

# the built console tree (vite preview needs vite + plugins + dist/ at runtime)
COPY --from=web /web /app/webapp

# bring-your-own-key LLM shim: reads a prompt on stdin, calls the Anthropic
# Messages API from ANTHROPIC_API_KEY. Wired in as compile.llm_cmd by the
# entrypoint only when a key is present. Stdlib only — runs on the venv python.
COPY demo/vouch-llm.py /usr/local/bin/vouch-llm
RUN chmod +x /usr/local/bin/vouch-llm

COPY demo/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

VOLUME ["/data"]
EXPOSE 5173
ENTRYPOINT ["/entrypoint.sh"]
121 changes: 121 additions & 0 deletions demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# vouch demo — try it in one command

A self-contained Docker demo of [vouch](https://github.com/vouchdev/vouch), the
git-native, **review-gated** knowledge base for LLM agents. One image bundles
the vouch server and the vouch-ui web console, and seeds a starter knowledge
base on first run — so you open the browser and immediately have something to
explore.

## Run it (no clone needed)

One command pulls the published image and starts everything:

```bash
docker run --rm -p 127.0.0.1:5173:5173 -v vouch-demo-data:/data \
ghcr.io/plind-junior/vouch-demo
```

Then open **http://localhost:5173** and connect with the pre-filled endpoint.

That's it. The first run seeds a starter KB (a claim, a page, a source), so the
console opens onto a populated, review-gated knowledge base — not an empty one.
Your data persists in the `vouch-demo-data` volume between runs; `Ctrl-C` stops
the demo (the `--rm` removes only the container, never the volume).

## Update to the latest

The image is updated in place, so pulling gets you the newest build (and any
fixes). Stop the demo, then:

```bash
docker pull ghcr.io/plind-junior/vouch-demo # fetch the latest image
```

Re-run the command from "Run it" — Docker now starts the updated image, and
your `vouch-demo-data` volume carries over. To start completely fresh instead,
reset the data with `docker volume rm vouch-demo-data` before running.

## Build from source (to hack on it)

Working in a clone of this repo? Build the image from the checkout instead of
pulling — it picks up your local changes to `webapp/` and `src/`:

```bash
cd demo
cp .env.example .env # optional: edit VOUCH_HTTP_TOKEN, add ANTHROPIC_API_KEY
docker compose up --build
```

## Turn on the LLM features (optional)

Two console actions call a language model: **Compile** (turn approved claims
into topic pages) and **Summarize session**. They're off by default because
the demo ships no API key. Everything else — browsing, propose → approve,
delete / archive / supersede, clear queue — works without one.

To switch them on, give the demo *your own* Anthropic key. With the pulled
image, pass it in with `-e`:

```bash
docker run --rm -p 127.0.0.1:5173:5173 -v vouch-demo-data:/data \
-e ANTHROPIC_API_KEY=sk-ant-... \
ghcr.io/plind-junior/vouch-demo
```

Building from source? Put it in `.env` instead:

```bash
cp .env.example .env # then set ANTHROPIC_API_KEY=sk-ant-...
docker compose up --build
```

The key never reaches the browser — vouch runs a tiny stdlib shim
(`vouch-llm`) inside the container that calls the Anthropic Messages API
directly. Override the model with `ANTHROPIC_MODEL` if you want a newer Sonnet.
Without a key, Compile / Summarize simply report "not configured" — that's the
review gate telling you the step is unavailable, not a crash.

## What you can do in the console

- **Browse / Claims** — the seeded knowledge, with citations and provenance
("why does this claim exist?").
- **Pending** — the propose → approve review gate, made visible.
- **Delete / Archive / Supersede** — retire a claim through the gate: delete
files a review-gated proposal (refused if other pages still cite it, which is
the point), archive hides it from retrieval, supersede replaces it.
- **Clear queue** — reject the whole pending queue at once.

## How it works

One container runs two processes (managed by `entrypoint.sh`):

1. `vouch serve --transport http` on `127.0.0.1:8731` inside the container, with
a bearer token.
2. the console via `vite preview`, whose `/proxy/*` middleware forwards to the
in-container vouch endpoint. The token is **pinned and injected server-side**
(`VOUCH_TARGET` / `VOUCH_HTTP_TOKEN`), so the browser never sees it.

Only the console port is published, and only on `127.0.0.1` — nothing leaves
your machine. Your KB lives in the `vouch-demo-data` volume:

```bash
# pulled image (docker run):
Ctrl-C # stop (keeps your data)
docker volume rm vouch-demo-data # reset the demo KB

# built from source (docker compose):
docker compose down # stop (keeps your data)
docker compose down -v # stop and reset the demo KB
```

## Notes

- The published image (`ghcr.io/plind-junior/vouch-demo`) carries the newest
`kb.*` surface, including delete / archive / supersede — a released `vouch`
from PyPI or `ghcr.io/vouchdev/vouch` would not yet advertise those. Building
from source picks up whatever is in your checkout.
- The LLM-backed actions (Compile, Summarize session) need an
`ANTHROPIC_API_KEY` — see "Turn on the LLM features" above. The rest of the
console is fully functional without one.
- The console's "Chat / Claude Code" mode is a dev-server feature and is not
wired up in this preview build; everything else works.
39 changes: 39 additions & 0 deletions demo/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Try vouch in one command:
#
# cd demo
# docker compose up --build # then open http://localhost:5173
#
# A starter, review-gated knowledge base is seeded on first run. Your data
# persists in the `vouch-demo-data` volume; `docker compose down -v` resets it.
# Only the console port is published, and only on 127.0.0.1 — nothing leaves
# your machine.
name: vouch-demo

services:
vouch-demo:
build:
context: ..
dockerfile: demo/Dockerfile
image: vouch-demo:latest
ports:
- "127.0.0.1:5173:5173"
environment:
# in-container bearer token; injected server-side, never sent to the browser
VOUCH_HTTP_TOKEN: ${VOUCH_HTTP_TOKEN:-vouch-demo}
# optional: your own Anthropic key turns on page compile & session
# summaries (Claude). Left empty, the demo still runs — those two actions
# just report "not configured". Never commit a real key.
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-claude-sonnet-4-5}
volumes:
- vouch-demo-data:/data
healthcheck:
test: ["CMD", "curl", "-fsS", "-m", "3", "http://127.0.0.1:5173/proxy/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
restart: unless-stopped

volumes:
vouch-demo-data:
71 changes: 71 additions & 0 deletions demo/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
#
# vouch demo entrypoint: seed a starter KB on first run, start the vouch server
# on loopback inside this container, then serve the console. Both processes live
# in one container; killing the container stops both.
set -euo pipefail

DATA="${VOUCH_DATA_DIR:-/data}"
TOKEN="${VOUCH_HTTP_TOKEN:-vouch-demo}"
# actor recorded in the seeded KB's audit log (avoids getpass in a bare image)
export VOUCH_USER="${VOUCH_USER:-demo}"

# First run: an empty /data volume gets a seeded, review-gated starter KB so the
# console has something to show. Idempotent — skipped once .vouch/ exists.
if [ ! -d "$DATA/.vouch" ]; then
echo "[demo] seeding a starter knowledge base in $DATA ..."
vouch init --path "$DATA"
fi

# vouch's LLM features (page compile, session summaries) shell out to the
# command in `compile.llm_cmd`. Point it at the demo shim only when the user
# supplied a key — otherwise leave it unset so those actions return vouch's
# clean "not configured" message instead of a dead command. Re-run every start
# so an existing volume lights up the moment a key appears (and goes dark if it
# is removed). Everything else — browse, propose→approve, delete/archive — works
# with no key at all.
CONFIG="$DATA/.vouch/config.yaml"
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
LLM_CMD="vouch-llm" python3 - "$CONFIG" <<'PY'
import os, sys, yaml
path = sys.argv[1]
with open(path) as f:
cfg = yaml.safe_load(f) or {}
cfg.setdefault("compile", {})["llm_cmd"] = os.environ["LLM_CMD"]
with open(path, "w") as f:
yaml.safe_dump(cfg, f, sort_keys=False)
PY
echo "[demo] LLM features ENABLED — Claude via ANTHROPIC_API_KEY (model=${ANTHROPIC_MODEL:-claude-sonnet-4-5})."
else
# Drop any llm_cmd a previous keyed run wrote, so state matches the missing key.
python3 - "$CONFIG" <<'PY'
import sys, yaml
path = sys.argv[1]
with open(path) as f:
cfg = yaml.safe_load(f) or {}
if isinstance(cfg.get("compile"), dict):
cfg["compile"].pop("llm_cmd", None)
if not cfg["compile"]:
cfg.pop("compile")
with open(path, "w") as f:
yaml.safe_dump(cfg, f, sort_keys=False)
PY
echo "[demo] LLM features DISABLED — set ANTHROPIC_API_KEY to enable page compile & session summaries; everything else works without it."
fi

# vouch on loopback (same container). A token is set so the console's proxy can
# inject it server-side; the browser never sees it.
echo "[demo] starting vouch server on 127.0.0.1:8731 ..."
( cd "$DATA" && exec vouch serve --transport http --host 127.0.0.1 --port 8731 --token "$TOKEN" ) &
VOUCH_PID=$!
trap 'kill "$VOUCH_PID" 2>/dev/null || true' EXIT INT TERM

# Wait for vouch to answer its public liveness probe before starting the UI.
for _ in $(seq 1 30); do
if curl -fsS -m 2 "http://127.0.0.1:8731/health" >/dev/null 2>&1; then break; fi
sleep 1
done

echo "[demo] starting the vouch console on :5173 — open http://localhost:5173"
cd /app/webapp
exec npm run preview -- --host 0.0.0.0 --port 5173
Loading
Loading