Spin up the EmDash simple demo
This is a one-shot recipe: from a fresh checkout of the EmDash monorepo to a logged-in admin in your browser. Total runtime ~30s on warm caches, ~3 minutes on a cold one.
The default monorepo path is ~/Projects/web design/emdash. If the user is in a different EmDash checkout, treat that as the repo root.
The recipe
All commands run from the repo root unless noted.
# 1. Make sure pnpm is on PATH at the version the repo pins
corepack enable
# (No-op if pnpm@10.28.0 is already shimmed.)
# 2. Install workspace deps (~20s on warm caches)
COREPACK_ENABLE_DOWNLOAD_PROMPT=0 pnpm install
# 3. Build all packages — REQUIRED before bootstrap
pnpm build
# 4. Bootstrap the demo's database (creates data.db, applies seed,
# downloads 7 Unsplash images into demos/simple/uploads/)
cd demos/simple && pnpm bootstrap
# 5. Start the dev server (long-running; run in background)
pnpm dev
Once the dev server prints astro v6.x.x ready in ... ms and Local http://localhost:4321/, the user logs in via:
http://localhost:4321/_emdash/api/setup/dev-bypass?redirect=/_emdash/admin
This creates dev@emdash.local (role 50 = ADMIN) and sets a session cookie automatically. Works only on a dev server (import.meta.env.DEV === true); returns 403 in prod.
Why each step exists
| Step | Why |
| --- | --- |
| corepack enable | The repo pins "packageManager": "pnpm@10.28.0" in package.json. Corepack ships with Node and is the canonical way to honor that pin. A globally-installed pnpm at a different version will at best print warnings and at worst miscompute the lockfile. |
| pnpm install before pnpm build | workspace:* deps need node_modules/ linked first. |
| pnpm build before pnpm bootstrap | demos/simple/astro.config.mjs imports emdash/astro, emdash/db, emdash/runtime — these resolve to packages/core/dist/*.mjs per packages/core/package.json exports. Without dist/, the demo can't even start, and pnpm bootstrap invokes the emdash CLI which lives at packages/core/dist/cli/index.mjs. |
| pnpm bootstrap (= emdash init && emdash seed) | init runs migrations against data.db. seed re-runs migrations (idempotent), then applies demos/simple/seed/seed.json and downloads media. Default conflict strategy is skip, so re-running is safe. |
| dev-bypass URL | Fastest auth path. EmDash's normal login is passkey-based and can't be automated. The endpoint is dev-only and creates a session in one round-trip. |
Warnings that are EXPECTED — do not chase them
If the user reports any of these, reassure them. Don't go fix them — they're known and harmless for this demo.
During pnpm install (40+ lines):
WARN Failed to create bin at .../node_modules/.bin/emdash. ENOENT:
no such file or directory, open '.../emdash/dist/cli/index.mjs'
dist/ doesn't exist yet on a clean install. The repo's postbuild hook (scripts/relink-bins-if-needed.mjs) detects missing bins and re-links them after pnpm build. Confirm with ls demos/simple/node_modules/.bin/emdash after the build.
WARN Ignored build scripts: @parcel/watcher@2.5.4, sharp@0.34.5.
Not in the root's pnpm.onlyBuiltDependencies allowlist. They ship prebuilt binaries; ignore.
WARN cyclic workspace dependencies:
packages/auth-atproto, packages/core
Known and accepted in this repo.
During pnpm dev startup:
[content] Content config not loaded
Live collection loads on demand on first request. Harmless.
[hooks] Plugin "audit-log" declares content:beforeSave hook without
write:content capability — skipping
[hooks] Plugin "audit-log" declares media:afterUpload hook without
read:media capability — skipping
The simple demo wires auditLogPlugin() without granting capabilities, so its hooks no-op. The CMS works fine; only the audit log is silent. Don't "fix" this in astro.config.mjs — it's the demo's intentional posture.
End-to-end verification (curl)
After step 5, run these to confirm everything works without opening a browser:
# Frontend
curl -sI http://localhost:4321/ | head -1 # → HTTP/1.1 200 OK
curl -s http://localhost:4321/ | grep -oE '<title>[^<]+</title>' # → <title>My Blog</title>
# Auth via dev-bypass — creates session, saves cookie
rm -f /tmp/emdash-cookies.txt
curl -sL -c /tmp/emdash-cookies.txt -o /dev/null \
"http://localhost:4321/_emdash/api/setup/dev-bypass?redirect=/_emdash/admin"
# Admin loads
curl -sI -b /tmp/emdash-cookies.txt http://localhost:4321/_emdash/admin | head -1
# → HTTP/1.1 200 OK
# Whoami
curl -s -b /tmp/emdash-cookies.txt http://localhost:4321/_emdash/api/auth/me
# → {"data":{"email":"dev@emdash.local","role":50,...}}
Two parsing gotchas when reading the cookie file:
- The session cookie name is
astro-session(HttpOnly). EmDash piggybacks on Astro's session, not a customemdash_*cookie. - In curl's Netscape cookie format, lines starting with
#HttpOnly_ARE the cookies, not comments. A naivegrep -v '^#'filter will hide them.
Expected output of pnpm bootstrap on a fresh DB
The seed produces:
- 35 migrations applied
- 2 collections (posts, pages)
- 6 fields, 7 taxonomy terms (3 categories + 4 tags)
- 2 bylines, 1 menu (3 items), 2 widget areas (6 widgets)
- 9 content items (8 posts including 1 draft + 1 page)
- 7 media files downloaded from Unsplash
If counts are noticeably off, the seed file or schema diverged — investigate before continuing.
Cleanup / reset to clean state
# From the repo root:
rm -f demos/simple/data.db
rm -rf demos/simple/uploads
# Re-run pnpm bootstrap to repopulate.
To stop a backgrounded pnpm dev, either kill its task in the harness or:
pkill -f "astro dev"
Common confusions
templates/blankis not a runnable demo. Templates are starter scaffolds meant to be copied intodemos/(per CONTRIBUTING.md) or used bycreate-emdash. Trying topnpm devinsidetemplates/blankmay work but isn't the intended flow.- There is no root
pnpm devscript. Alwayscd demos/<name>first. The rootpackage.jsononly has build/test/check/format/lint scripts. create-emdashis for scaffolding a new project outside the monorepo. It's not part of this recipe — use it when the goal is to start a real site, not when the goal is to try EmDash.- The
data.dbanduploads/dirs are gitignored indemos/simple. Recreating them via bootstrap is always cheap.
When this skill does NOT apply
- The user wants to scaffold a new EmDash project from scratch → use
create-emdash(pnpm newfrom the monorepo root, orpnpm dlx create-emdashoutside it). - The user wants to add features, define collections, render content → use the
building-emdash-siteskill. - The user wants to author a plugin → use the
creating-pluginsskill. - The user wants to manage content via CLI → use the
emdash-cliskill.