CMS-krønike·april 2026·

CMS Chronicle #13: Alt sammen – og hoveddøren

485 commits på 12 dage. Vi genopbyggede agents-motoren fra bunden (seks faser), sendte ét-klik Docker-deploys til Fly.io, lærte CMS’en at teleportere sig mellem maskiner, gav redaktører adgang uden adgangskode med passkeys og TOTP, og skrev endelig masterplanen for mobil-app’en. Dette er det mest intense forløb i projektets historie.

485 commits på 12 dage

Chronicle #12 afsluttede den AI-native editor den 27. marts. Tolv dage senere er vi oppe på 485 commits – cirka fyrre om dagen – og projektet er tydeligt vokset i mindst otte forskellige retninger. Denne kronik forsøger at gøre alle disse retninger retfærdighed. Den er lang. Hæld kaffe i.

De overordnede temaer:

  • Agents-motoren fik seks faser i et enkelt sprint og er nu et rigtigt autonomt indholdssystem – ikke blot en cool demo.
  • Ét-klik Docker-deploys til Fly.io – ingen mere kævl med Dockerfiles, CMS’en gør det for dig via deploy-guiden.
  • F122 Beam – sites kan teleportere sig mellem CMS-instanser. Fra localhost til sky, sky til localhost, sky til sky. Live streaming inkluderet.
  • F59 Passwordless – passkeys (WebAuthn) og TOTP via autentifikator-app. Hoveddøren er endelig blevet en førsteklasses funktion.
  • F94 Favorites, F92 Desktop PWA, F35 Webhooks, F124 Snippet Embeds – den lange hale af "selvfølgelig burde du have dette"-funktioner, som endelig er blevet bygget.
  • F119/F121/F31 – dokumentationssitet blev lanceret med 89 sider, og vi skrev Next.js-hjælpepakken, der gør det muligt for andre Next-sites at forbruge CMS’en med ét import.
  • F07 Masterplan – mobil-app’en Capacitor har endelig en skreven plan, som vi kan give videre til en ny session.

Lad os komme i gang.

Agents-motoren, genfødt

Vi havde agents i #12. De kunne skrive et udkast, kurationskøen håndterede det, en redaktør godkendte eller afviste. Det virkede, men det var groft. Efter seks faser med polering er det et helt andet produkt.

Fase 1 – hurtige gevinster. GEO-rolle dukkede op i agent-UI’en, MCP-legitimationslækage blev lukket, og der blev tilføjet et web-søgefaldback, når den primære udbyder rammer rategrænsen. Små ting, men de var splinterne, der gjorde ondt.

Fase 2 – lukket feedback-loop. Hver korrektion, en redaktør foretager i kurationskøen, og hver afvisning føder nu tilbage i agentens prompt som konkrete eksempler. Agenten lærer husstilen uden, at du nogensinde åbner dens prompt-skabelon. Efter tre eller fire korrektioner på den samme agent, behøver du ikke længere at korrigere den.

Fase 3 – multi-model-alternativer. Når en agent genererer et udkast, kan kurationskøen vise alternative versioner fra forskellige modeller side om side. Klik på den chip, du kan lide – Claude, Gemini, GPT – og det er den version, der bliver sendt videre. De tabende alternativer bliver træningssignal for feedback-loopen i fase 2.

Fase 4 – omkostningsbegrænsninger pr. agent. Daglige, ugentlige og månedlige USD-begrænsninger pr. agent. Præ-flight-tjek før LLM-opkaldet, ikke efter. En løbsk agent stopper nu ved $1,52 af sin $1,50-begrænsning i stedet for at brænde hele månedens budget klokken 3 om natten. Planlæggeren, manuelle kørsler og workflow-trin kontrollerer alle de samme begrænsninger.

Fase 5 – SEO-score på udkastet. Agenten genererer både indlægget og SEO-scoren for det i samme gennemløb. Når det lander i kurationskøen, kan du allerede se, om titlen er for lang, om meta-beskrivelsen mangler, eller om nøgleordstætheden er forkert. Ingen flere "godkend, deployér, og så opdager du, at SEO-panelet er tomt."

Fase 6 – det store ét. Tre dele:

  • Lokalitet pr. agent + auto-oversættelse ved godkendelse. En dansk indholdsredigeringsagent og en engelsk indholdsredigeringsagent, der deler den samme prompt-skabelon, er nu én agent med to lokaliteter. Godkend det danske udkast, og den engelske version bliver auto-oversat og sat i kø bagefter.
  • Workflow-pipelines. Agenter kan nu kædes sammen i workflows. Trin 1: forskning i emnet. Trin 2: disposition. Trin 3: skriv udkastet. Trin 4: generér hero-billede med Nano Banana 2. Trin 5: SEO-tjek. Hvert trin er sin egen agent med sin egen model, prompt, budget og feedback-historik. Outputtet fra trin N er input til trin N+1.
  • Træk-og-slip trinredigering + JSON-tilstand. Omarranger trin ved at trække. Skift til rå JSON, hvis du vil redigere et workflow programmatisk. Den samme ModeToggle-komponent bruges nu på tværs af agent- og workflow-redigeringerne, så skifteren opfører sig identisk overalt.

Og en tredje hurtig gevinst oven på alt dette: cron-planer for workflows, tildeling pr. trin, og forfinelse af afvisningsprompter.

Billedgenerering blev også rigtig: værktøjet generate_image kalder nu Gemini Nano Banana 2 (den nye model-id) og skubber resultatet direkte ind i mediebiblioteket med auto-genereret alt-tekst. En kontrakt uden hallucinationer ved fejl: Hvis modellen ikke kan generere et billede, siger agenten det eksplicit i stedet for at opfinde en URL.

Agents-markedet blev lanceret på L2 (lokalt) og L3 (marked). Du kan gemme dine indstillede agenter som skabeloner og dele dem med andre CMS-instanser.

Hvis du prøvede agents i #12 og blev frustreret, så prøv dem igen. Det er et helt andet produkt.

F122 Beam – Sites, der teleporterer

Dette føles som science fiction, indtil du bruger det.

Fase 1. Et site kan pakkes ind i et enkelt .beam-arkiv – indhold, medier, skema, indstillinger, chat-historik, hukommelse, brugertilstand – alt sammen. Arkivet er en bærbar, komplet øjebliksbillede. Drop det på en anden CMS-instans, og sitet materialiserer sig der, som om det altid havde levet der.

Fase 2. Det samme, men med live streaming. Åbn Beam i kilde-CMS’en, peg på en mål-CMS, klik Start. De to instanser forhandler via et streaming-protokol, og indholdsændringer propagerer, mens de sker. Du kan redigere på localhost og se cloud-instansen opdatere i realtid, eller omvendt.

De primære brugsscenarier, vi byggede det til:

  • Localhost → cloud første deploy. Du bygger et site på din bærbare computer, Beam det til produktions-CMS’en med ét klik. Ingen git, ingen rsync, ingen manuel upload af _data/.
  • Katastrofegenoprettelse. Cloud’en blev ramt af en katastrofe? Beam det seneste øjebliksbillede tilbage fra din bærbare computer.
  • Kloning af site. Tag et fungerende site, Beam det under en ny slug, tilpas kopien. Vi bruger det internt til at spinne nye eksempel-sites op.
  • Cross-org migration. Flyt et site fra én organisation til en anden uden at røre filer.

F122 er tæt forbundet med F95 (sky-backups), som fik sin egen opmærksomhed i dette forløb: en S3-kompatibel adapter, der virker med R2, B2, Scaleway, Hetzner og AWS. pCloud blev omskrevet fra REST til WebDAV (REST-API’et har en 2 GB JSON-anmodningsgrænse, der bryder enhver backup med mere end et par hundrede filer; WebDAV har ikke det problem). Lagerkvoter med automatisk beskæring af de ældste øjebliksbilleder, når du rammer grænsen. Lokale/sky-mærker på hvert øjebliksbillede i UI’en, så du kan se, hvor hvert enkelt lever.

F119 – Ét-klik Docker-deploy

Deploy-guiden har nu en ny skabelon: "Kør på Fly.io". Du vælger regionen, giver det et navn, klikker Deploy. I baggrunden:

  1. CMS’en henter det forbyggede billede fra GitHub Container Registry (ghcr.io/webhousecode/cms-admin:latest)
  2. Kalder Fly.io Machines API med en genereret fly.toml
  3. Provisionerer et volumen, sætter hemmelighederne, starter maskinen
  4. Første opstart kører auto-oprettelsen: opretter admin-brugeren, frøer team-filen, installerer eksempel-CMS Demo-sitet
  5. Du bliver vist den live URL

Ingen Dockerfile at skrive. Ingen fly launch at køre. Ingen miljøvariabler at kopiere-indsætte. Hvis du selv vil bygge dit eget billede, udgiver vi stadig Dockerfilen, men de fleste brugere vil aldrig have brug for den.

Fremgangen vises i en deploy-modal med et trin-for-trin måler – forbindelse, byg, push, provisionering, frøning, helbredstjek, færdig. Hvert trin bliver grønt eller rødt, så du ved præcis, hvor det fejlede, hvis det gør det.

GitHub Pages-adapteren lærte at håndtere en 500-fejl fra Pages API’en, når Pages allerede er aktiveret – det viser sig, at GitHub returnerer en serverfejl i stedet for en 409 i det tilfælde. Nu behandler vi det som en succes.

F59 – Passkeys & TOTP

Hoveddøren har siden dag ét været baseret på email/adgangskode, med GitHub OAuth boltet på senere. Begge fungerer, men de er ikke 2026’s login-metode. I dette forløb tilføjede vi to adgangsløse veje.

Fase 1: passkeys (WebAuthn). FaceID, TouchID, Windows Hello, hardware-sikkerhedsnøgler. Tilføj en passkey via Konto → Sikkerhed, log ind med ét klik. CMS’en gemmer kun den offentlige nøgle, så et datalæk vil ikke kompromittere nogen. Cross-device login virker via browserens indbyggede QR-baserede hybridtransport – ingen separat app kræves. Vi lærte en masse om sikkerhedskontekst-krav (passkeys har brug for HTTPS eller localhost – almindelig HTTP via LAN-IP virker ikke) og endte med at integrere next dev --experimental-https med mkcert i PM2-økosystemet, så intern test fra en telefon er blot ét kommando væk.

Fase 4: TOTP. Microsoft Authenticator, Google Authenticator, Authy, 1Password – enhver RFC 6238-app. Konto → Sikkerhed → Tilføj ny app, scan QR-koden, indtast en kode, få ti backup-koder. Login-flowet gatter nu TOTP efter primær legitimationsverifikation: en kortlivet cms-totp-pending-cookie gemmer brugeridentiteten i fem minutter, mens de henter koden, hvorefter en separat verifikations-endpoint bytter den ventende cookie ud med en reel session. Backup-koderne er sha256-hashede (32 bit entropi betyder, at bcrypt er overkill og forårsagede test-tidsudløb) og engangsbrug – konstant-tids sammenligning, atomisk forbrug.

Vi byggede også Fase 2: QR-kode login – en Discord-lignende cross-device-flow, hvor skrivebordet viser en QR-kode, din mobil-app scanner den, og du logger ind. Den fulde implementering er i repoet, men UI’en er skjult bag NEXT_PUBLIC_CMS_ENABLE_QR_LOGIN=false, indtil den mobile app rent faktisk eksisterer til at scanne den. Vi vil flippe flaget den dag, vi lancerer F07.

Tal ikke om fase 3 endnu.

F121 – Dokumentationssitet blev lanceret, og andre sites kan nu tage deres egen medicin

F31 (dokumentationssitet) blev afsluttet i dette forløb med 89 sider på engelsk og dansk, fuld JSON API, GitHub Actions-deploy til Fly.io. Det er live på docs.webhouse.app. Hver link i CMS-administratorens HelpCards peger nu på en reel side der.

F121 er den pakke, der kom ud af at bygge dokumentationssitet: @webhouse/cms/next – drop-in SEO-hjælpere til ethvert Next.js-site, der forbruger indhold fra en webhouse.app CMS. Ét import giver dig metadata-generatoren, JSON-LD-generatoren, hjælperen til canonical-URL’er, sitemappet, robots.txt. Dokumentationssitet er beviset. Næste gang nogen bygger et Next-site mod CMS’en, burde de have en fungerende SEO-opstilling på omkring ti minutter.

Værktøjet /update-docs landede sammen med det – når CMS-funktioner lanceres, læser en enkelt kommando de seneste commits og sætter dokumentationsopdateringer i kø på dokumentationssitet, og post-commit-hook’en minder dig nu om at køre det.

Den lange hale af indlysende funktioner

Funktioner, der burde have været der fra dag ét og endelig er blevet bygget:

  • F94 Favorites. Hjerte-knap på samlinger, sidebar-favoritter, kommandopalette-pin, dedikeret /admin/favorites-side med liste- og gittervisninger. Du kan endelig pinne de tre ting, du rent faktisk redigerer.
  • F92 Desktop PWA. CMS-administratoren installeres som en desktop-app via browserens PWA-installationsprompt. Fuldskærm, ingen browser-krom, dock-ikon. Samme UX som en nativ app for redaktører, der lever i CMS’en.
  • F35 Webhooks. Discord- og email-integrationer på tværs af indholdshændelser, deploy-hændelser og agent-hændelser. Discord-embeds er linket til kurationskøen (ikke preview-URL’en – det brød, hvis preview-URL’en var skabelonbaseret), de udsender kun absolutte URL’er (relative URL’er brød i mobil-Discord), og det redundante felt "Genereret billede" blev droppet til fordel for en beskrivelses-link.
  • F124 Snippet Embeds. En TipTap-node til {{snippet:slug}} – henter et snippet fra snippets-samlingen ved render-tid. Rediger snippets én gang, og hvert indlæg, der embedder det, opdateres. Snippet-picker’en er en fuzzy-søge-drop-down.
  • F81 Hjemmesidebetegnelse. Eksplicit indstilling på et enkelt dokument: "dette er forsiden", i stedet for at gætte ud fra slug:'home' eller slug:'index'.
  • Klon site. Fuldt filsystem-copy med fjernelse af hemmeligheder. node_modules symlinkes fra kilden, så klonen ikke tager 30 sekunder at installere. Deploy-tilstand ryddes på klonen, så du ikke utilsigtet genudgiver kilden.
  • Reserveret samlingsnavnbeskyttelse. Site-valideren advarer nu, hvis et samlingsnavn kolliderer med indbyggede admin-paneler (media, interactives, settings, admin osv.). Den samme udgivelse tilføjede i18n-preview-omdirigeringer, så tosprogede sites har fungerende preview-URL’er for ikke-standard lokaliteter.
  • F116 HelpCards → HelpButton. Det kontekstuelle hjælpepanel flyttede fra et per-side sidebar-kort til en enkelt knap i handlingslinjen med en portal-popover. Mindre visuel støj, samme adgang.
  • F109 Inline korrekturretning færdiggjort – ProseMirror-dekorationer til inline-korrektioner med godkend/afvis, plus tab-isolation-fixet, der stopper en korrektur på ét dokument i at lække ind i et andet.
  • F117 MCP-paritet – 43 MCP-værktøjer, fuld paritet med chat-værktøjsfladen. Enhver handling, du kan foretage via chat, kan du også foretage via MCP, og omvendt.
  • Øjeblikkelig indholdsdeployering – når en indholdsændring kan serveres via on-demand-revalidering, springer vi den fulde deploy over. At redigere et indlæg og se ændringen live på 2 sekunder i stedet for 90 er et helt andet produkt.

F120 – Onboarding

Nye brugere får nu en guided produkt-tur første gang, de logger ind. Værktøjstips-baseret, peger på sidebaren, editoren, chat-knappen, deploy-knappen og AI-panelet. Vi skrev tour-leverandøren om én gang i midten af forløbet, fordi den oprindelige havde en genrenderingsloop, der lejlighedsvis sprang to trin over. Den nuværende implementering udvider sidebaren, før den peger på sidebarknapper, avancerer robust gennem dynamiske monteringer og har poleret design på værktøjstipsene. Der er en ONBOARDING=true-miljøvariabel til udvikling, så du kan iterere på turen uden at nulstille din brugertilstand.

F07 – Mobil COCpit masterplan

Vi bygger den endnu ikke, men vi skrev masterplanen: docs/features/F07-mobile-cocpit-master-plan.md. Et enkelt dokument, som en frisk Claude-session kan hente op og begynde at udføre. Det er baseret på Capacitor + Fastlane-opskriften fra fysiodk-aalborg-sport-projektet (to apps, begge allerede godkendt på App Store og Google Play).

Princippet er usædvanligt: Claude gør alt. Kode, bygninger, simulator-automatisering, App Store Connect API-opkald, Apple Developer Portal-formularer via Playwright (hvor API’et ikke rækker), Google Play Console-uploads via Fastlane, Firebase-projekt-provisionering, skærmbillede-generering, TestFlight-indsendelse. Christian giver én gang legitimationsoplysninger ind i .env.secrets, hvorefter han orkestrerer og kuraterer. Manuel portal-klikning behandles som en fejl – lav en issue og skriv et script. Pipeline’en er designet til at være genanvendelig for den næste mobil-app efter denne.

Otte faser, 12-16 dage fra ende til ende til en feature-komplet app indsendt til begge butikker. Fase 1 leverer en MVP-skal med biometrisk autentifikation fra dag ét.

Mindre ting, der betød noget

  • PM2 styrer cms-admin på port 3010 nu, med en hård regel i CLAUDE.md om, at Claude ikke kan dræbe eller genstarte dev-serveren uden eksplicit tilladelse. Det kostede os én session. Løst.
  • HTTPS-udviklingsopsætning ende-til-ende: mkcert + next dev --experimental-https integreret i PM2, plus et one-shot-scriptet setup-https-dev.sh, der installerer certifikatet i systemets nøglering og serverer rootCA’en til din telefon via en midlertidig HTTP-server med en QR-kode.
  • F127 Samlingsformålsmetadata – samlinger erklærer nu en kind og description, så chat-assistenten og onboarding-turen kan forstå, hvad hver samling er til, ikke blot hvilke felter den har.
  • Kurations-forhåndsvisningsmodal – se præcis, hvordan et agents-udkast vil se ud som et offentliggjort indlæg, ESC til lukning, ingen flere gæt.
  • Indstillingsanker-navigation med glødende markering af det målrettede afsnit. Småt, men du lægger mærke til det.
  • PM2-ports-script (pm2-ports.sh) – pm2 list med en PORT-kolonne, så du rent faktisk kan se, hvilken dev-server der kører på hvilken port.
  • F102 Skema-drift fik flere tests efter arbejdet med inline-korrekturretning afslørede en kanttilfælde.

Retningen

Hvis #12 var kronikken om "den AI-native editor", så er #13 kronikken om "og nu også alt det andet". Autentifikation, deployment, observabilitet, mobilgrundlag, indholdsautomatisering, indholdsstreaming, dokumentationssitet, feedback-loopen, hoveddøren.

Vi forsøger at bygge et CMS, hvor hvert eneste manuelle trin er blevet automatiseret væk. For seks måneder siden betød det at deploye et site til Fly.io at skrive en Dockerfile, lære Machines API’en og fejlsøge volumer. Nu er det ét klik. For seks måneder siden betød det at logge ind at skrive en adgangskode. Nu gør dit ansigt det. For seks måneder siden betød det at fortælle en agent, hvordan du ville have den til at skrive, at redigere en prompt-skabelon i hånden. Nu korrigerer du den tre gange i kurationskøen, og den lærer.

Den næste kronik vil sandsynligvis begynde med Mobil COCpit. Derefter? Hvem ved. Roadmapet er fuldt, og hastigheden er ikke faldet.

Vi bygger stadig det CMS, vi havde brug for for tredive år siden. Tolv flere dage, og det kommer tættere på.