Docs / Releases and partners
Partner demo (5 min)
Partner loop: edit on draft, then promote.
Prove EdgeCMS’s wedge: edit → publish snapshot → channel promote → rollback on the same Worker, with Storyblok-class click-to-edit on a real demo route.
Audience: design-partner eng + marketer. No rebuild, no CDN purge dance.
Staging dry-run (operator, ~90s)
Live staging already has tenant partner + seeded baseline. Prefer this before a partner call:
# From edgecms-saas tip
bun run demo:partner:dry-run:staging
# or:
bun scripts/partner-demo-dry-run.ts \
--base-url https://edgecms-api-staging.cheongnanfeng.workers.dev \
--website-url https://edgecms-website-staging.cheongnanfeng.workers.dev \
--tenant partner \
--email smoke.admin@example.com \
--password 'SmokePassw0rd!'
# After website deploy with short aliases:
# … --check-aliasesExpect every beat PASS (ensure → visual-edit → draft fidelity → promote → zh-CN provenance → locale-readiness → synergy → abac-deny → rollback). Auth requires an Origin header matching the API host (the script sets it).
| Surface | URL |
|---|---|
| 1. Configure | https://edgecms.skyvis.ai/demo/ |
| 2. Waiting room | https://edgecms.skyvis.ai/demo/waiting-room/ |
| 3. Protected site | https://edgecms.skyvis.ai/demo/site/ |
| Legacy studio URL | …/api/demo/studio → 302 to /demo/ |
| Admin sign-in | https://edgecms-api-staging.cheongnanfeng.workers.dev/sign-in |
| Releases UI | https://edgecms-api-staging.cheongnanfeng.workers.dev/tenants/partner/releases |
| Published page | …/api/tenants/partner/api/demo/pages/home?locale=en |
| Locale provenance | …/demo/pages/home?locale=zh-CN |
| Docs (5 min) | https://edgecms.skyvis.ai/docs/partner-demo-5min/ |
| Docs (promote) | https://edgecms.skyvis.ai/docs/atomic-edge-promote/ |
| Short aliases | /docs/partner-demo/ → 5 min; /docs/promote/ → Atomic Edge Promote |
Product flow (same-site UI on edgecms.skyvis.ai): Configure brand/locale/channel → join waiting room (POST …/waiting-room/join) → gate opens after admitAt → admit (POST …/waiting-room/admit) → protected site (/demo/site/) loads JSON content and edits/promotes in page — no iframe studio.
Protection: marketing pages never show admin passwords. Waiting ticket is HMAC-scoped waiting only; admit upgrades to visual-edit + release (~15 min) on tenant partner / pages/home. Content/promote/rollback require the admitted capability.
Inventory: store docs/partner-staging.md. Do not quote public SLO numbers; no certification claims.
Prep (once, ~60s — local)
cd edgecms-saas
bun install
bun run --filter edgecms-api db:migrate:local
# first-time local DB only — use the prepare-local-dev SUPER_ADMIN_EMAILS default
# so Admin dashboard tenant fleet does not 403 (role=admin ≠ allowlisted super-admin)
bun run db:seed:smoke -- --email smoke.admin@example.com --password 'SmokePassw0rd!' \
--tenant-slug partner --tenant-name 'Partner Demo'
bun run dev:api # :8787
bun run dev:admin # :5173 (prefer explicit; `bun run dev` also runs website :8790)- Sign in to Admin (
http://localhost:5173) → open tenant partner → Releases. - Click Ensure demo content (idempotent: creates
pages/home+zh-CN → zh → encascade; backfills thinzhbody when missing). - Label a release
baseline, leave Promote to published checked → Publish snapshot.
Sanity:
curl -i "http://localhost:8787/api/tenants/<tenant>/api/demo/pages/home?locale=en"
# expect 200, X-EdgeCMS-Perspective: published, X-EdgeCMS-Release-Id present
curl -i "http://localhost:8787/api/tenants/<tenant>/api/demo/pages/home?locale=zh-CN"
# expect X-Content-Locale-Resolved + X-Content-Locale-Provenance (title→en, body→zh)Script (≈5 minutes)
| Min | You do | Partner sees |
|---|---|---|
| 0:00 | Open Published page from Releases. | Live page from the channel release (immutable KV snapshot). |
| 0:30 | Open Visual edit host (same tenant). Click a field (title / hero heading), change copy, blur. | Overlay highlight → status Saved … → D1 (draft). Host loads draft perspective with a preview token. |
| 1:30 | Refresh Published page (no new snapshot yet). | Old copy still live — DB is truth for draft; channel still points at baseline. |
| 2:00 | Releases → label partner-edit-1 → Publish snapshot (promote on). | New release id; channel pointer flips (<~2s). |
| 2:30 | Refresh Published page. | New copy live. Note X-EdgeCMS-Release-Id changed. |
| 3:00 | Optional: ?locale=zh-CN on the demo page. | Cascade headers: fallible title→en, body→zh; locale-exact legal→zh-CN (no silent fallback). |
| 3:10 | Optional: GET …/admin/releases/<id>/locale-readiness after snapshot. | ready: true (or LOCALE_EXACT_NOT_READY gaps block promote). |
| 3:15 | ABAC deny beat (same sitting): POST …/admin/releases/demo/abac-deny (default locale=fr). Or open GET …/admin/releases/demo/synergy for the composed checklist. | 403 Attribute policy denied; audit chain / SIEM sink shows abac.denied next to entry.visual_edited / release.promoted. |
| 3:30 | On the published channel card → Rollback. | Pointer returns to baseline; previous snapshot still in KV; audit release.rolled_back. |
| 4:00 | Refresh published page. | Baseline copy restored — no rebuild. |
| 4:30 | Close on the line: “Same Worker, draft and live; promote is a pointer, not a pipeline — and SecOps sees the loop.” |
URLs (tenant-scoped)
Admin Releases: /tenants/<tenant>/releases
Visual host: /api/tenants/<tenant>/api/demo/host/home?locale=en
Published page: /api/tenants/<tenant>/api/demo/pages/home?locale=en
Draft overlays: …/pages/home?visualEdit=1&perspective=draft&previewToken=<token>
(host mints token; prefer opening via Visual edit host)Failure checklist
| Symptom | Fix |
|---|---|
| 404 on demo page | Ensure demo content, then publish a snapshot. |
| Visual host save fails / CSRF | Stay signed in; host fetches /api/csrf and sends x-csrf-token. |
| Edits don’t show on published page after save | Expected until Publish snapshot + promote. Draft host should show edits immediately. |
| Rollback disabled | Need a previousReleaseId — publish at least two promoted releases. |
| Releases UI empty / wrong tenant | Admin calls must use /admin/releases (tenant-prefixed), not bare /api/admin/releases. |
Composed synergy path (VE × locale-exact × release × env×release × locale × ABAC)
One partner sitting that multiplies the wedge bets:
# Checklist + URLs (auth required) — includes locale-exact-readiness beat
curl -s -b <session> "http://localhost:8787/api/tenants/<tenant>/api/admin/releases/demo/synergy"
# After snapshot: locale-exact readiness (promote gate preview)
curl -s -b <session> "http://localhost:8787/api/tenants/<tenant>/api/admin/releases/<releaseId>/locale-readiness"
# ABAC deny → abac.denied on the tenant audit chain (and AUDIT_SIEM_* sink if set)
curl -i -b <session> -X POST "http://localhost:8787/api/tenants/<tenant>/api/admin/releases/demo/abac-deny" \
-H 'content-type: application/json' -d '{}'Lifecycle audit actions SecOps should see on the chain: entry.visual_edited, release.created, release.promoted, environment.release_pushed (when a linked-env push is release-sourced / promoteChannel), release.rolled_back (category content). Optional: set ENABLE_RELEASE_SEMANTIC=true → promote also indexes + emits release.semantic_indexed; search via GET …/admin/releases/:releaseId/semantic-search?q=… (spike, not GA). Environments carry a channel release across instances — they are not a second publish UX (atomic-edge-promote.md).
The visual-edit host forwards x-edgecms-locale + x-edgecms-channel=published on PATCH so custom roles with locale conditions can deny real edits; built-in unrestricted roles stay RBAC-only unless you use the abac-deny probe.
Out of scope for this script
Packaged Datadog/Splunk connectors (hookable webhook/NDJSON sink already exists — see operations-security.md), GA semantic search UX, release history pagination redesign, full invite→SIEM SecOps sitting (use security-walkthrough-10min.md).
Deep dive: atomic-edge-promote.md.