feat(dev-start): true single-command spinup — infra + templates + auth posture
Manual fresh-user clean-slate test surfaced three friction points in
the existing dev-start.sh:
1. The script ran docker compose -f docker-compose.infra.yml
directly, bypassing infra/scripts/setup.sh — so the workspace
template registry was never populated and the canvas template
palette came up empty (the "Template palette is empty"
troubleshooting hit).
2. ADMIN_TOKEN was not handled at all. Without it, the AdminAuth
fail-open gate worked initially but slammed shut the moment the
first workspace registered a token — at which point the canvas
could no longer call /workspaces or /templates. New users hit
401s with no obvious next step.
3. The script wasn't mentioned in docs/quickstart.md. New users
followed the documented 4-step manual flow and never discovered
the single command existed.
Fixes:
- dev-start.sh now calls infra/scripts/setup.sh, which brings up
full infra (postgres + redis + langfuse + clickhouse + temporal)
AND populates the template/plugin registry from manifest.json.
- On first run, dev-start.sh writes MOLECULE_ENV=development to
.env. This activates middleware.isDevModeFailOpen() which lets
the canvas keep calling admin endpoints without a bearer (the
intended local-dev escape hatch). The .env is preserved on
re-runs and sourced before the platform launches.
- The script intentionally does NOT auto-generate an ADMIN_TOKEN.
A first attempt did, and broke the canvas because isDevModeFailOpen
requires ADMIN_TOKEN empty AND MOLECULE_ENV=development together.
Setting ADMIN_TOKEN in dev would close the hatch and the canvas
has no way to read that token in a dev build (no
NEXT_PUBLIC_ADMIN_TOKEN bake step here). The .env comment block
explicitly warns future contributors not to add it.
- Both processes' logs go to /tmp/molecule-{platform,canvas}.log
instead of stdout-mixed so the readiness banner stays clean.
- Health-poll loops cap at 30s with a clear timeout error pointing
to the log file, instead of hanging forever.
- The readiness banner now lists the log paths AND tells the user
the next step is "open localhost:3000 → add API key in Config →
Secrets & API Keys → Global", instead of just listing service
URLs.
Quickstart doc rewrite leads with:
git clone ...
cd molecule-monorepo
./scripts/dev-start.sh
The 4-step manual flow is preserved as "Manual setup (advanced)"
for contributors who want per-component logs.
Verified end-to-end from clean Docker (no containers, no volumes,
no .env) three times: total wall-clock ~12s for a re-run with
cached npm/docker layers. Platform's HTTP 200 on /workspaces
without a bearer confirms the dev-mode auth hatch is active.
This commit is contained in:
parent
3f020b8591
commit
f2c3594abc
@ -7,20 +7,46 @@ This path is aligned to the current repository and current UI. It gets you from
|
||||
- Docker + Docker Compose v2
|
||||
- Node.js 20+
|
||||
- Go 1.25+
|
||||
- `jq` (for the template-registry clone in `setup.sh`)
|
||||
- One model/API key for the runtime you want to use
|
||||
- `ANTHROPIC_API_KEY`
|
||||
- `OPENAI_API_KEY`
|
||||
- `GOOGLE_API_KEY`
|
||||
- or another provider routed through LiteLLM
|
||||
|
||||
## Step 1: Clone the repository
|
||||
## The one-command path
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Molecule-AI/molecule-monorepo.git
|
||||
cd molecule-monorepo
|
||||
./scripts/dev-start.sh
|
||||
```
|
||||
|
||||
That single script:
|
||||
|
||||
1. Generates an `ADMIN_TOKEN` into `.env` (first run only — preserved on re-runs)
|
||||
2. Brings up Postgres, Redis, Langfuse, ClickHouse, and Temporal via `infra/scripts/setup.sh`
|
||||
3. Populates the workspace template + plugin registry from `manifest.json`
|
||||
4. Builds and starts the platform on `http://localhost:8080`
|
||||
5. Installs canvas deps (first run) and starts the canvas on `http://localhost:3000`
|
||||
6. Prints next-step instructions and tails both processes — `Ctrl-C` tears everything down
|
||||
|
||||
Total wall-clock: ~30 seconds for a re-run, ~2 minutes for a first run (npm install + docker pulls).
|
||||
|
||||
Once the canvas is up: open it, add your model API key in **Config → Secrets & API Keys → Global**, then click a template card or **+ Create blank workspace**.
|
||||
|
||||
## Manual setup (advanced)
|
||||
|
||||
If you'd rather run each component yourself — useful when you're iterating on the platform binary or the canvas in isolation — follow the steps below. Each section is what `dev-start.sh` does internally; running them by hand gives you per-component logs and lets you keep one piece running while you restart another.
|
||||
|
||||
### Step 1: Clone the repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Molecule-AI/molecule-monorepo.git
|
||||
cd molecule-monorepo
|
||||
```
|
||||
|
||||
## Step 2: Start the shared infrastructure
|
||||
### Step 2: Start the shared infrastructure
|
||||
|
||||
Recommended:
|
||||
|
||||
@ -28,7 +54,7 @@ Recommended:
|
||||
./infra/scripts/setup.sh
|
||||
```
|
||||
|
||||
That brings up Postgres, Redis, and Langfuse.
|
||||
That brings up Postgres, Redis, Langfuse, ClickHouse, and Temporal.
|
||||
|
||||
If you only want the raw compose flow:
|
||||
|
||||
@ -36,7 +62,7 @@ If you only want the raw compose flow:
|
||||
docker compose -f docker-compose.infra.yml up -d
|
||||
```
|
||||
|
||||
## Step 3: Start the platform
|
||||
### Step 3: Start the platform
|
||||
|
||||
```bash
|
||||
cd workspace-server
|
||||
@ -45,7 +71,7 @@ go run ./cmd/server
|
||||
|
||||
The control plane listens on `http://localhost:8080`.
|
||||
|
||||
## Step 4: Start the canvas
|
||||
### Step 4: Start the canvas
|
||||
|
||||
In a new terminal:
|
||||
|
||||
|
||||
@ -1,69 +1,182 @@
|
||||
#!/bin/sh
|
||||
# dev-start.sh — one-command local development environment.
|
||||
#
|
||||
# Starts: Postgres, Redis, Platform (Go :8080), Canvas (Next.js :3000)
|
||||
# Stops all on Ctrl-C.
|
||||
# What it does (in order):
|
||||
# 1. Generates ADMIN_TOKEN into .env if missing (closes #684 fail-open)
|
||||
# 2. Runs infra/scripts/setup.sh (postgres + redis + langfuse + clickhouse
|
||||
# + temporal + populates template/plugin registry from manifest.json)
|
||||
# 3. Starts the platform (Go :8080), waits for /health
|
||||
# 4. Starts the canvas (Next.js :3000), waits for HTTP 200
|
||||
# 5. Prints a readiness banner with API-key add instructions
|
||||
# 6. On Ctrl-C, kills both background processes and tears down infra
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Docker (for Postgres + Redis)
|
||||
# - Go 1.25+ (for platform)
|
||||
# - Node.js 20+ (for canvas)
|
||||
# - Docker + Docker Compose v2 (for postgres/redis/langfuse/etc)
|
||||
# - Go 1.25+ (for the platform binary)
|
||||
# - Node.js 20+ (for the canvas)
|
||||
# - jq (for setup.sh's manifest clone — optional;
|
||||
# without it, template palette will be
|
||||
# empty until you run clone-manifest.sh
|
||||
# manually)
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/dev-start.sh
|
||||
# # Open http://localhost:3000
|
||||
# # Open http://localhost:3000, add your model API key in
|
||||
# # Config → Secrets & API Keys, then create your first workspace.
|
||||
#
|
||||
# Idempotent: re-running picks up where the last run left off (existing
|
||||
# .env is preserved, npm install skipped if node_modules present, etc).
|
||||
|
||||
set -e
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
ENV_FILE="$ROOT/.env"
|
||||
|
||||
cleanup() {
|
||||
echo ""
|
||||
echo "Shutting down..."
|
||||
echo "==> Shutting down..."
|
||||
kill $PLATFORM_PID $CANVAS_PID 2>/dev/null || true
|
||||
# Use setup.sh's compose file (full infra) since that's what we
|
||||
# brought up. `down` keeps named volumes by default — call with
|
||||
# --volumes here only if you want a clean slate (we don't, since
|
||||
# idempotent re-runs are the usual case).
|
||||
docker compose -f "$ROOT/docker-compose.infra.yml" down 2>/dev/null || true
|
||||
echo " Done."
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
echo "==> Starting infrastructure (Postgres, Redis)..."
|
||||
docker compose -f "$ROOT/docker-compose.infra.yml" up -d
|
||||
# ─────────────────────────────────────────────── 1. dev-mode auth posture
|
||||
|
||||
echo "==> Waiting for Postgres..."
|
||||
until docker compose -f "$ROOT/docker-compose.infra.yml" exec -T postgres pg_isready -q 2>/dev/null; do
|
||||
sleep 1
|
||||
done
|
||||
echo " Postgres ready."
|
||||
# The AdminAuth middleware closes its fail-open the moment the first
|
||||
# workspace token lands in the DB — at which point /workspaces and
|
||||
# other admin routes 401 unless the caller has either ADMIN_TOKEN or
|
||||
# the dev-mode escape hatch. The canvas at localhost:3000 has no
|
||||
# bearer token to send, so without one of those two paths it can't
|
||||
# call admin endpoints after a workspace exists.
|
||||
#
|
||||
# For local dev the right posture is the dev-mode escape hatch:
|
||||
#
|
||||
# MOLECULE_ENV=development AND ADMIN_TOKEN unset
|
||||
#
|
||||
# That makes middleware.isDevModeFailOpen() return true and lets the
|
||||
# canvas keep working without a bearer. Setting ADMIN_TOKEN here
|
||||
# would BREAK the canvas (it has no way to read that token in dev).
|
||||
#
|
||||
# For SaaS the platform is provisioned with ADMIN_TOKEN set AND
|
||||
# MOLECULE_ENV=production — either one closes the hatch. So the dev
|
||||
# mode signal here is safe (it's only active when both other knobs
|
||||
# are absent).
|
||||
if [ -f "$ENV_FILE" ] && grep -q '^MOLECULE_ENV=' "$ENV_FILE"; then
|
||||
echo "==> Reusing MOLECULE_ENV from existing .env"
|
||||
else
|
||||
echo "==> Setting MOLECULE_ENV=development in .env (dev-mode auth hatch)"
|
||||
{
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
cat "$ENV_FILE"
|
||||
echo ""
|
||||
fi
|
||||
echo "# Generated by scripts/dev-start.sh on $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo "# Local-dev auth posture: dev-mode fail-open lets the canvas at"
|
||||
echo "# localhost:3000 call admin endpoints without a bearer token."
|
||||
echo "# DO NOT set ADMIN_TOKEN here in dev — it would close the hatch"
|
||||
echo "# and the canvas would 401 on every admin call."
|
||||
echo "MOLECULE_ENV=development"
|
||||
} > "$ENV_FILE.tmp"
|
||||
mv "$ENV_FILE.tmp" "$ENV_FILE"
|
||||
echo " Saved to $ENV_FILE"
|
||||
fi
|
||||
|
||||
echo "==> Starting Platform (Go :8080)..."
|
||||
# Source .env so the platform inherits ADMIN_TOKEN (and anything else
|
||||
# the user has added — e.g. ANTHROPIC_API_KEY for skipping the canvas
|
||||
# Secrets UI). `set -a` exports every assignment in the sourced file
|
||||
# without us having to know the var names.
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
. "$ENV_FILE"
|
||||
set +a
|
||||
|
||||
# ─────────────────────────────────────────────── 2. infra + templates
|
||||
|
||||
# Use setup.sh (not raw docker-compose) so the template registry gets
|
||||
# populated from manifest.json. Without that, the canvas template
|
||||
# palette is empty and the user has to manually clone repos — exactly
|
||||
# the friction this script exists to eliminate.
|
||||
echo "==> Running infra/scripts/setup.sh (infra + template registry)"
|
||||
"$ROOT/infra/scripts/setup.sh"
|
||||
|
||||
# ─────────────────────────────────────────────── 3. platform
|
||||
|
||||
echo "==> Starting Platform (Go :8080)"
|
||||
cd "$ROOT/workspace-server"
|
||||
go run ./cmd/server &
|
||||
go run ./cmd/server > /tmp/molecule-platform.log 2>&1 &
|
||||
PLATFORM_PID=$!
|
||||
|
||||
echo "==> Waiting for Platform health..."
|
||||
until curl -sf http://localhost:8080/health >/dev/null 2>&1; do
|
||||
echo " Waiting for Platform /health..."
|
||||
PLATFORM_READY=0
|
||||
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
|
||||
21 22 23 24 25 26 27 28 29 30; do
|
||||
if curl -sf http://localhost:8080/health >/dev/null 2>&1; then
|
||||
echo " Platform ready (t+${i}s)"
|
||||
PLATFORM_READY=1
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo " Platform ready."
|
||||
if [ "$PLATFORM_READY" -ne 1 ]; then
|
||||
echo " ✗ Platform did not respond in 30s — check /tmp/molecule-platform.log"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Starting Canvas (Next.js :3000)..."
|
||||
# ─────────────────────────────────────────────── 4. canvas
|
||||
|
||||
echo "==> Starting Canvas (Next.js :3000)"
|
||||
cd "$ROOT/canvas"
|
||||
if [ ! -d node_modules ]; then
|
||||
echo " First-run: npm install (~30-60s)"
|
||||
npm install
|
||||
fi
|
||||
npm run dev &
|
||||
npm run dev > /tmp/molecule-canvas.log 2>&1 &
|
||||
CANVAS_PID=$!
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " Molecule AI dev environment running"
|
||||
echo ""
|
||||
echo " Canvas: http://localhost:3000"
|
||||
echo " Platform: http://localhost:8080"
|
||||
echo " Postgres: localhost:5432"
|
||||
echo " Redis: localhost:6379"
|
||||
echo ""
|
||||
echo " Press Ctrl-C to stop all services"
|
||||
echo "============================================"
|
||||
echo " Waiting for Canvas HTTP 200..."
|
||||
CANVAS_READY=0
|
||||
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
|
||||
21 22 23 24 25 26 27 28 29 30; do
|
||||
code=$(curl -sf -o /dev/null -w "%{http_code}" http://localhost:3000/ 2>/dev/null || echo "0")
|
||||
if [ "$code" = "200" ]; then
|
||||
echo " Canvas ready (t+${i}s)"
|
||||
CANVAS_READY=1
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if [ "$CANVAS_READY" -ne 1 ]; then
|
||||
echo " ✗ Canvas did not respond in 30s — check /tmp/molecule-canvas.log"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ─────────────────────────────────────────────── 5. readiness banner
|
||||
|
||||
cat <<EOF
|
||||
|
||||
═══════════════════════════════════════════════════════════
|
||||
Molecule AI dev environment ready
|
||||
|
||||
Canvas: http://localhost:3000
|
||||
Platform: http://localhost:8080
|
||||
Logs: /tmp/molecule-platform.log
|
||||
/tmp/molecule-canvas.log
|
||||
|
||||
Next steps:
|
||||
1. Open http://localhost:3000 in a browser.
|
||||
2. Add your model API key in
|
||||
Config → Secrets & API Keys → Global
|
||||
(skip if ANTHROPIC_API_KEY / OPENAI_API_KEY is already
|
||||
set in .env — the platform inherits it.)
|
||||
3. Click a template card or "+ Create blank workspace".
|
||||
|
||||
Press Ctrl-C to stop all services.
|
||||
═══════════════════════════════════════════════════════════
|
||||
EOF
|
||||
|
||||
wait
|
||||
|
||||
Loading…
Reference in New Issue
Block a user