Work for bside-ms/bside_website and bside-ms/bside_payload. Implement in small PRs, not as one change. Check a box when the PR is merged and live.
Architecture context: AGENTS.md here and AGENTS.md in the CMS.
- Editors: publish in Payload means the live page is correct within seconds.
- Devs: sibling clones, copy
.env.skel, start Mongo + CMS + website, preview and revalidate locally. - Later: safer deps, Mongo, and deploy pins. Do not mix those with the editor fixes.
sequenceDiagram
participant Editor
participant CMS
participant Website
Editor->>CMS: Publish document
CMS->>Website: POST /api/revalidate with secret
Website->>Website: res.revalidate paths
Editor->>Website: Hard reload sees new content
Do this first. Website PR can merge before the CMS PR. Deploy website, then CMS.
Live on b-side.ms. POST /api/revalidate without key → 401. With x-revalidation-key → 200, core DE/EN paths revalidated: true.
- Add
pages/api/revalidate.tsusing Pages Routerres.revalidate. - Auth: header
x-revalidation-keymust matchREVALIDATION_KEY. - Add that env name to
types/environment.d.ts. - Core path list for
deanden. Dynamic slugs stay on 60s ISR until A2. - Keep existing
revalidate: 60on pages. - Runtime only:
REVALIDATION_KEYin serverwebsite.env. Not a Docker build-arg.
Implemented on a2-revalidate-after-publish (CMS + website). Check the boxes when both PRs are merged and live.
In bside_payload:
- Add
src/utilities/revalidateWebsite.ts:POST ${NEXT_PUBLIC_SITE_URL}/api/revalidatewith headerx-revalidation-key=REVALIDATION_KEYand body{ paths?: string[] }. - Log failures. Never fail the Payload save if the website is down.
- Skip draft-only saves. Run when
doc._status === 'published'or the previous doc was published (unpublish / delete). - Globals have no drafts: revalidate on every update.
-
afterChange/afterDeleteonpages,events,news,circles,organisations,redirects. - Same on globals
start-page,about-bside,event-page,event-archive,banner. - Send extra paths when known (page breadcrumb, event/news slug, circle kebab name, org landings). Website always includes the core set and expands
/en.
In this repo:
- Accept extra
pathsfrom the CMS body and revalidate them in addition to the core set.
- Confirm CMS
.envhas a realREVALIDATION_KEY. - Set the same value on
website.envasREVALIDATION_KEY. - Deploy website, then CMS:
docker compose up -d --pull always/... payload. - Test: publish a page, hard-reload within a few seconds.
- If a page is still stuck from before this work: restart the website container (ISR cache in
./cache).
- CMS
src/utilities/createEventSlug.tsmust match the website:{id.slice(-4)}-{kebabCase(slug || title)}. - Keep website
createEventSlugOldso old URLs still resolve. - Fix missing
/in CMS live-preview URLs for Banner and AboutBside (SITE_URL+bsidetoday).
Today lib/redirect/redirect.js fetches redirects at image build.
- Add website
middleware.ts: look up CMS/api/redirects, short in-memory cache (~60s), applyfrom→to. - Keep static
/bioand the IE redirect innext.config.js. - Stop merging CMS redirects into
next.config.js. - Leave Traefik hostname redirects on the server as they are.
- After this,
next buildmust not need a running CMS for redirects.
- Page
bside: intro and four tiles come from global Über die B-Side; body blocks are this page. - Locale hint: DE and EN layouts are separate. Publish the language you edited.
- Distinguish Live-Seite vs Vorschau.
- Redirects collection: replace “needs frontend restart” with “live within a minute” after B2.
Assume a sibling checkout (bside_website next to bside_payload). No personal machine paths.
Website .env.skel is wrong for local work today.
-
NEXT_PUBLIC_PAYLOAD_URL=${PAYLOAD_URL}(code does not readNEXT_PUBLIC_IMAGE_URL). -
PAYLOAD_API_COLLECTION=api-users(slug, not the labelApi User). - Same example revalidation secret in both skels (
EXAMPLE_REVALIDATION_KEY). - Comment
PREVIEW_TOKENas unused. - CMS skel:
REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY.
- Document in both
AGENTS.md+ README: CMSdocker compose up -dthenyarn devon 3000; websitenpm run devon 3001. - Document that admin login needs Keycloak (
CLIENT_SECRET). Empty secret means no admin UI. Do not re-enable local passwords here. - Document that
/kultur,/quartier,/bside/kollektiv404 on an empty local DB (hardcoded production Mongo ids). - CMS script
yarn sync:types:generate:types, copy to../bside_website/types/payload/payload-types.tsif that path exists, otherwise print the GitHub target.
- Both workflows:
actions/checkout@v4. - Website CI: add
npm run tsc. - Optional: rename
docker-image.yml. Website CI onmainnow builds and pushes the image (see G0).
Not the production Dockerfiles. Each repo keeps its own compose. Start the stacks separately. No shared project, no sibling build context.
- This repo:
docker-compose.dev.ymlwithnpm run devinnode:20, port 3001, source bind-mounted,node_modulesin a named volume.PAYLOAD_URL=http://host.docker.internal:3000. - CMS repo: its own
docker-compose.dev.ymlwith Mongo plus Payload asyarn devinnode:20, port 3000, same volume pattern. - Website may start alone. Pages 404 if the CMS stack is down.
- Do not use the production image (Chrome, amd64,
next start) for daily coding.
Do this after A and B so the text matches how publish actually works.
- Update both
AGENTS.mdfiles: revalidation flow, secrets, runtime redirects, event slug format, local boot. - Leave production
PREVIEW_TOKEN/ unused keys on the server until someone removes them by hand.
A page inside the admin, German, linked from the nav. Not Confluence, not a public website page.
- How the loop works: Speichern vs Veröffentlichen, Vorschau vs Live-Seite, a few seconds after publish, hard-reload.
- Locales: DE and EN are separate. Publish the language you edited.
- What lives where: Page
bsidevs global Über die B-Side, organisations vs circles vs events vs news, redirects, banner (bannerId). - Block catalog: every block slug editors can add, what it does, when to use it, image size hints (event 1080², circle 1280x720, etc.).
- Tips: drafts, slugs / last-4 event URLs, circle names become kebab URLs, do not delete the three organisation docs, redirects are live without a website rebuild (after B2).
- Keep B3 field-level hints. The handbook is the long version.
Own PRs, after editors trust publish again. Never mix with A–D.
Worth doing. Admin bugfixes, keep Next 15.
- Bump
payloadand all@payloadcms/*from3.68.1to current 3.8x together. - Align website
@payloadcms/live-preview-react(already3.73) with that CMS version. -
yarn generate:typesandyarn generate:importmap. -
yarn sync:typesor manual copy into this repo. - Click through admin, Keycloak login, draft/publish, media upload.
Do not jump the CMS to Next 16 just because Payload templates did.
- Stay on Pages Router until there is a real reason to move.
- A Next 15/16 bump is its own project: ISR,
next.configredirects/middleware, i18n, Dockerstandalone, Puppeteer image.
- Remove unused
next-authfrom the website if still unused. - Drop the stray
yarnpackage (2.0.0-rc.24) from the CMSpackage.jsonif it is unused. - Align CMS
eslint-config-nextwith the installed Next 15 (it is already on 16).
- Today
identifierisafterReadonly (id.slice(-4)). Website querieswhere[identifier][equals=...]and then falls back to scanning slugs. - Write it in
beforeChange, or stop querying it.
Website hardcodes:
-
Kollektiv
647e605b7054a955522b2471→/bside/kollektiv -
Kultur
647e60a67054a955522b24ad→/kultur -
GmbH
647e60bd7054a955522b24cb→/quartier -
Look up by slug or
shortNameinstead. -
Do not delete or recreate those three docs until this ships.
- Confirm backups of
/srv/docker/b-side.ms/cms/dataandcms/media. - Plan a staged upgrade (4.4 → 5 → 6, or restore on a new volume). Do not only change the image tag.
- Test restore before touching production.
Hub account: bsidems. Not leftbit, not seebruecke.
Website and CMS images are public. GitHub is public. The image may only bake NEXT_PUBLIC_* (already in the browser). Secrets stay in server env files (website.env, CMS .env). Do not pass real secrets as Docker build args. bsidems has one private-repo slot; keep it for something that must stay private. Public images also mean the server can pull without docker login.
- Website: GitHub Actions on
mainpushesbsidems/bside-website:latestandbsidems/bside-website:<sha>. Hub autobuilds stay unused. Public build args live in repository variables (PAYLOAD_URLmust behttps://cms.b-side.ms, not the old.ovhhosts). OnlyDOCKERHUB_TOKENis a secret. - CMS Actions on
mainpushesbsidems/bside-cms:latestand:<sha>. First green run:6953cc5(Actions31974317155). Follow-ups in the CMS ROADMAP G6. - CMS server compose at
/srv/docker/b-side.ms/cms/pullsbsidems/bside-cms:latest(16 Aug 2026). Mongo stayed up. Leaveleftbit/…only as rollback.
- Run
bsidems/bside-website:<gitsha>andbsidems/bside-cms:<gitsha>on the server. - Keep
:latestas an alias only. - Rollback = previous sha, not
docker-compose.rollback.ymlas the only story.
- Remove unused
PAYLOAD_CONFIG_PATH(Payload v2 leftover) from CMS compose. - Make website listen port explicit (
3001vs DockerfilePORT=3000). - Never
docker compose down -von CMS.
- Remove unused
PREVIEW_TOKEN,NEXT_PUBLIC_IMAGE_URLfrom serverwebsite.envafter code no longer mentions them. - Remove unused
REVALIDATION_KEYnaming drift once A is live (keep the one pair of matching secrets). - Delete or ignore dead
dev/*_DEV/latest-testDocker Hub paths. - Remove
staging.b-side.msfromlib/common/url.tsvalidHostnamesif the host stays unused.
- Rate-limit or shared-secret on public
createforcontact-formsandnot-found-pages. - Revisit CMS
cors: '*'.
- Shared types package (only if
yarn sync:typesis still painful). - CMS image build in GitHub Actions (see G0). Hygiene leftovers are G6 in the CMS ROADMAP.
- Watchtower on
:latest: do not do this. - Local password login / Keycloak-free CMS: out of scope unless admin-onboarding becomes a real block.
Own PRs. Do not mix with A–F.
CMS image and CI (other repo). First Actions image build 6953cc5 (~4 min). Cache export 73.5s on a cold mode=max cache. Canonical checklist: bside_payload ROADMAP G6.
- Second CMS Actions run: confirm GHA layer cache. If upload stays huge,
cache-tomode=min. - CMS lint:
yarn install --immutable, minimumpermissions, Yarn cache, skip lint-during-next build. - Later: CMS runtime image without a full
node_modulescopy (standalone). Same class of work as this repo’s Dockerfile.
Website image and CI. First Actions image build took ~8 min; most of that is SSG of 1144 pages against the CMS.
- Stop prerendering every event slug in the website image build.
- Use Next
standaloneoutput in the website Dockerfile. - Website CI: minimum
permissions, skip lint-during-next build, confirm GHA layer cache, DockerfileFROM AS/ENV key=value. - Deps later (not now):
npm audit, React 19 / Next 14--force, browserslist.
CMS content that shows up in the website build log
- Clean RichText links that serialize as
incorrect link(null, empty text, hosts withouthttps://). - Fix invalid
hrefs that Next rejects:https//…, leading spaces, malformed ticket links. -
/kreise/hansawerkstattpage data is ~230 kB (limit 128 kB). Trim or split.
- A1 website revalidate API (done, live)
- A2 CMS hooks + website extra paths, then A3 server env
- B1 preview slugs (can ride with A2)
- B2 runtime redirects
- B3 + C + D1 (copy, skel, agent docs, CI)
- D2 editor handbook in the CMS (after A and B are live)
- E1 Payload bump
- F and G when someone has backup time
Each step should be mergeable alone. A without B already helps the /bside publish complaint.