Sådan er VIDAR bygget

Hvordan et dokument i VIDAR bliver til en færdig HTML-side, og hvordan portalen kan køre på et lukket netværk uden en eneste server.

Denne side er et landkort over, hvordan VIDAR er bygget – fra de tekstfiler, indholdet skrives i, til den færdige hjemmeside, en besøgende åbner.

VIDAR er en statisk hjemmeside. Der findes ingen database og ingen server, der regner noget ud, mens du besøger siden. Alt er bygget på forhånd og lagt ud som færdige filer. Det er forudsætningen for, at portalen kan køre på et lukket netværk uden internetadgang.

Det store billede

Et program ved navn Hugo læser projektets tekstfiler, data og skabeloner og skriver en komplet hjemmeside ud: almindelige HTML-, CSS- og JavaScript-filer. De filer lægges et sted, hvor folk kan hente dem. Når en besøgende åbner en side, henter browseren de færdige filer – der er intet, der skal regnes ud undervejs.

flowchart LR
    R["✍️ Redaktør<br/>skriver tekst"] --> K["📁 Kildefiler<br/>tekst, data, skabeloner"]
    K --> H["⚙️ Hugo<br/>bygger siden"]
    H --> F["📦 Færdige filer<br/>HTML, CSS, JS"]
    F --> B["🌐 Besøgende<br/>åbner siden"]
    classDef ind fill:#f3f3f3,stroke:#212529,color:#212529
    classDef kerne fill:#212529,stroke:#212529,color:#ffffff
    classDef ud fill:#C5E0B4,stroke:#385723,color:#000000
    class R,K ind
    class H kerne
    class F,B ud
  • Kildefilerne er tekst, data og skabeloner – det, redaktører og udviklere arbejder i.
  • Hugo samler dem efter skabelonerne til færdige sider.
  • De færdige filer er selve hjemmesiden: HTML, CSS og JavaScript.
  • Besøgende henter de færdige filer, og browseren viser dem.

Fordi alt bygges på forhånd, er der ingen kørende server. De færdige filer kan ligge et hvilket som helst sted, hvor folk kan hente dem.

Hvorfor en statisk side?

Valget af en statisk side er ikke tilfældigt. Det løser flere ting på én gang:

  1. Lukket netværk. VIDAR skal kunne køre uden adgang til internettet. En statisk side har ingen ydre afhængigheder, når den først er bygget – derfor er alt tredjepartskode lagt ind i selve projektet (vi kalder det at vendore). Intet hentes fra et CDN ude på nettet.
  2. Sikkerhed. Der er ingen database og ingen serverkode at udnytte – angrebsfladen skrumper til et minimum. Det er vigtigt nok til at få sit eget afsnit længere nede.
  3. Enkel drift. Færdige filer kan ligge hvor som helst: en filserver, en simpel webserver, en USB-nøgle. Ingen specialopsætning.

Byggekæden: de fire slags kildefiler

Når Hugo bygger VIDAR, læser den fire slags input. Det er den vigtigste model at have i hovedet, for hver gang du redigerer noget i VIDAR, redigerer du i én af disse fire:

flowchart TB
    subgraph IND["Kildefiler i projektet"]
        C["📝 content/<br/><b>Indholdet</b><br/>Markdown-tekstfiler"]
        L["🧩 layouts/<br/><b>Skabelonerne</b><br/>HTML med huller"]
        D["🗃️ data/<br/><b>Strukturerede data</b><br/>YAML-filer"]
        A["🎨 assets/ + static/<br/><b>Udseende og filer</b><br/>SCSS, JS, billeder"]
    end
    H["⚙️ Hugo + Docsy"]
    P["📦 public/<br/>Den færdige hjemmeside"]
    C --> H
    L --> H
    D --> H
    A --> H
    H --> P
    classDef ind fill:#f3f3f3,stroke:#212529,color:#212529
    classDef kerne fill:#212529,stroke:#212529,color:#ffffff
    classDef ud fill:#C5E0B4,stroke:#385723,color:#000000
    class C,L,D,A ind
    class H kerne
    class P ud

1. content/ – selve indholdet

Hver side i VIDAR er en tekstfil skrevet i Markdown – et tekstformat, hvor man skriver ## Overskrift og **fed** direkte i teksten i stedet for at klikke på knapper. Filen, du læser lige nu, er en Markdown-fil.

Øverst i hver fil står der en lille blok med indstillinger mellem to streger af ---. Den hedder frontmatter og fortæller Hugo ting som titel, rækkefølge og hvilke roller siden er relevant for:

---
title: "Sådan er VIDAR bygget"
weight: 20
description: "Hvordan et dokument bliver til en HTML-side ..."
---

Mappen hedder content/da/, fordi hele VIDAR er på dansk. Mappestrukturen er sidernes adresser: filen content/da/om/teknisk-arkitektur.md bliver til siden /om/teknisk-arkitektur/.

2. layouts/ – skabelonerne

Indholdet siger hvad der skal stå. Skabelonerne siger hvordan det skal se ud. En skabelon er en HTML-fil med huller, som Hugo fylder ud med indhold. På den måde får alle teknologi-sider samme udseende, alle skabelon-sider samme udseende og så videre – uden at vi gentager os selv.

Her bor også VIDAR’s egne små byggeklodser, shortcodes, som fx tegner NAF-rammeværket.

3. data/ – strukturerede data

Nogle ting er ikke “en side”, men en liste af data. Fx forsidens bannere eller teknologiradarens trends. Dem lægger vi i YAML-filer under data/, og skabelonerne læser fra dem. Så kan samme data genbruges flere steder, og en redaktør kan rette i listen uden at røre ved layoutet.

4. assets/ og static/ – udseende og medfølgende filer

  • assets/scss/ indeholder stylesheets (farver, skrifttyper, afstande), som Hugo oversætter til CSS.
  • static/ indeholder filer, der bare skal med uændret: billeder, og ikke mindst VIDAR’s vendorede tredjepartskode (Mermaid, søgemotoren) under static/js/vendor/.

Hugo og Docsy

Hugo er motoren, der binder det hele sammen. Det er en static site generator: et program, der læser de fire slags kildefiler og skriver en færdig hjemmeside ud i mappen public/. Hugo bygger hele VIDAR på under et sekund.

Vi bruger ikke Hugo helt bart. Vi bygger oven på Docsy, et færdigt tema lavet til dokumentationssider, som giver os venstremenu, søgning, brødkrummesti og en gennemtænkt grundstruktur. Docsy er hentet ind som et Hugo-modul og ligger vendoret i mappen _vendor/ – igen, så intet skal hentes udefra.

Filosofien er “overstyr, erstat ikke”: Vi bruger Docsy som fundament og lægger kun vores egne filer oven på, dér hvor VIDAR skal afvige. En fil i vores eget layouts/ vinder over Docsys udgave af samme fil. Sådan kan Docsy opdateres, uden at vores tilpasninger går tabt.

Indholdsmodellen: sektioner, arv og taksonomier

VIDAR er ikke bare en bunke løse sider. Indholdet er organiseret efter tre principper, der arbejder sammen:

flowchart TB
    ROOT["content/da/"]
    ROOT --> S1["🏛️ arkitekturbibliotek/"]
    ROOT --> S2["📡 teknologiradar/"]
    ROOT --> S4["ℹ️ om/"]
    S1 --> P1["mange sider ..."]
    S2 --> P2["trends + teknologier"]

    T["🏷️ Taksonomier<br/>roller · domæner · statusser<br/>ringe · kvadranter"]
    P1 -.tagges med.-> T
    P2 -.tagges med.-> T

    classDef rod fill:#212529,stroke:#212529,color:#ffffff
    classDef sek fill:#f3f3f3,stroke:#212529,color:#212529
    classDef tax fill:#C5E0B4,stroke:#385723,color:#000000
    class ROOT rod
    class S1,S2,S4,P1,P2 sek
    class T tax

Sektioner. Hver hovedmappe under content/da/ er en sektion med sin egen forside (en _index.md-fil) og sine egne undersider. Det er det, du ser som hovedpunkterne i menuen.

Arv (cascade). En sektions forside kan sætte regler, der “drypper ned” på alle dens undersider. Fx siger om/_index.md, at alle sider herunder skal bruge dokumentations-layoutet – så hver ny side arver det automatisk, uden at man skal sætte noget op manuelt.

Taksonomier. Ud over mappestrukturen kan en side mærkes på tværs med etiketter. VIDAR har fem egne taksonomier i stedet for de sædvanlige “tags”: roller, domæner, statusser, ringe og kvadranter. En skabelon kan fx mærkes med rollen Arkitekt, og så dukker den automatisk op på arkitektens rolleside. Hugo bygger selv oversigtssiderne for hver etikette.

“Dynamik” uden en server

Hvis alt er forudbygget, hvordan kan VIDAR så have ting, der ligner programmer – søgning, eksport, diagrammer? Dynamikken sker enten under bygningen eller i din egen browser – aldrig på en server:

Funktion Hvordan den virker uden server
Søgning En søgemotor (Lunr) bygges ind som en lille JavaScript-fil. Søgningen kører i din browser.
Eksport af skabeloner Hugo bygger en ekstra, ren Markdown-udgave af hver skabelon (output-formatet fillin), klar til download.
Diagrammer Mermaid-diagrammer (som dem på denne side) tegnes af en vendoret JavaScript-fil i browseren.
CMS Et redigeringsværktøj (Sveltia) som rene filer; det taler direkte med vores Git-server.

Pointen er, at alt det “levende” enten er lagt ind i de færdige filer på forhånd eller udføres lokalt af den besøgendes egen browser.

Sådan kommer indhold online

Det sidste stykke er, hvordan ny tekst kommer fra en redaktørs hoved og helt ud på portalen. Her er VIDAR’s redigerings- og udgivelsesvej:

sequenceDiagram
    autonumber
    actor Red as ✍️ Redaktør
    participant CMS as 🖊️ Sveltia CMS<br/>(/admin)
    participant Git as 🗄️ Git-server<br/>(Forgejo)
    participant Host as 🏗️ Hosting<br/>(statichost.eu)
    actor Bes as 🌐 Besøgende

    Red->>CMS: Skriver/retter en side i browseren
    CMS->>Git: Gemmer ændringen som en commit
    Note over Git: Al historik bevares i Git
    Red->>Host: Trykker "Rebuild" manuelt
    Host->>Git: Henter nyeste indhold
    Host->>Host: Kører Hugo → bygger public/
    Host-->>Bes: Serverer de færdige filer
  1. En redaktør åbner CMS’et/admin og redigerer i en formular – ingen kode, ingen Markdown nødvendig.
  2. Når der gemmes, lægger CMS’et ændringen ind i Git (vores versionsstyring på Forgejo). Dermed har vi fuld historik: hvem ændrede hvad, hvornår, og vi kan altid rulle tilbage.
  3. Hostingen (statichost.eu) henter det nyeste, kører Hugo og lægger de færdige filer ud. Dette trin udløses manuelt med en Rebuild – bevidst, så vi har kontrol over, hvornår noget faktisk går i luften.

Bemærk: Genopbygningen sker med vilje manuelt. Når du har redigeret indhold, er det først synligt udadtil, når nogen trykker Rebuild.

Sikkerhed

Arkitekturen fjerner hele kategorier af angreb, fordi der ikke er noget at angribe på serversiden. Det, der serveres, er færdige filer – ikke en kørende applikation.

  • Ingen serverkode, ingen database. Der er ingen forespørgsler at manipulere, ingen database at trække data ud af og ingen serverkode at udnytte. Klassiske webangreb som SQL-injection og fjernkørsel af kode på serveren har ingen flade at ramme.
  • Ingen udgående forbindelser. En færdig VIDAR-side henter intet udefra, mens den vises. Browseren taler kun med den server, filerne ligger på. Der er hverken sporings- eller tredjepartsindhold, der kan lække data ud.
  • Integritetstjek på medfølgende kode. De vendorede scripts indlæses med en Subresource Integrity-kontrolsum. Ændres en fil bare én byte, nægter browseren at køre den – en manipuleret kopi bliver opdaget.
  • Adgang styres ét sted. Kun den, der er logget ind mod Git-serveren (Forgejo), kan ændre indhold gennem CMS’et. Repositoriet er privat, så kildeteksten er ikke offentlig.
  • Fuldt revisionsspor. Alt indhold ligger i Git. Hver ændring er knyttet til en person og et tidspunkt, og man kan altid se – og rulle tilbage til – en tidligere version.
  • Kontrolleret udgivelse. Genopbygningen udløses manuelt. En ændring bliver først synlig udadtil, når nogen bevidst trykker Rebuild, så intet går utilsigtet i luften.

Angrebsfladen er dermed begrænset til de filer, der ligger på hostingen – ikke et program, der kører og kan narres.

Sådan virker integritetstjekket (Subresource Integrity)

VIDAR læner sig op ad nogle færdige JavaScript-biblioteker – fx Lunr til søgning og Mermaid til de diagrammer, du ser her på siden. De ligger som kopier i projektet. Men hvordan ved browseren, at sådan en fil ikke er blevet byttet ud eller ændret, inden den køres? Det er præcis det, Subresource Integrity (SRI) løser.

Når siden bygges, regnes der en kontrolsum ud af filens indhold: en kort tegnstreng, der er entydig for netop den fil. Ændrer man bare ét tegn i filen, bliver kontrolsummen helt anderledes. Den kontrolsum skrives ind i den <script>-linje, der indlæser filen. Det script, der tegner diagrammerne på denne side, ser fx sådan ud:

<script
  src="/js/vendor/mermaid-11.15.0.min.js"
  integrity="sha384-yQ4mmBBT+vhTAwjFH0toJXNYJ6O4usWnt6EPIdWwrRvx2V/n5lXuDZQwQFeSFydF"
  crossorigin="anonymous"></script>

sha384 er navnet på den metode, kontrolsummen er regnet ud med; resten er selve kontrolsummen.

Når browseren har hentet filen, regner den selv en kontrolsum ud af det, den modtog, og sammenligner med den, der står i integrity:

  • Stemmer de overens, er filen uændret, og browseren kører den.
  • Stemmer de ikke, afviser browseren filen helt. Den bliver ikke kørt, og i stedet kommer der en fejl i browserens konsol.

Konkret betyder det, at hvis en af de medfølgende filer ændres på serveren – ved et uheld eller med vilje – holder siden op med at indlæse netop den fil i stedet for at køre noget uventet. Browseren udfører tjekket helt af sig selv; der skal ikke sættes noget op ud over at have kontrolsummen stående i integrity.

Offline-princippet – den røde tråd

Hvis man kun skal huske én ting om VIDAR’s arkitektur, er det denne: intet hentes fra internettet, hverken når siden bygges, eller når den vises.

Hver gang man normalt ville hente et bibliotek fra et CDN – temaet, diagram-motoren, søgningen, skrifttyper – har vi i stedet lagt en kopi ind i selve projektet. Det koster lidt ekstra plads, men det er prisen for, at VIDAR kan køre på et fuldstændig lukket netværk. Det er en hård regel, ikke en præference.

Mappestruktur – lynoversigt

For den nysgerrige er her de vigtigste mapper samlet:

VIDAR/
└── site/
    ├── content/da/      # Indholdet (Markdown) – det redaktører skriver
    ├── layouts/         # VIDAR's egne skabeloner og shortcodes
    ├── data/            # Strukturerede data (YAML)
    ├── assets/scss/     # Styling (bliver til CSS)
    ├── static/          # Filer der følger med uændret
    │   ├── admin/       #   Sveltia CMS
    │   └── js/vendor/   #   Vendoret tredjepartskode (offline)
    ├── _vendor/         # Docsy-temaet (Hugo-modul, vendoret)
    ├── hugo.toml        # Hovedopsætningen
    └── public/          # Den færdige hjemmeside (bygges af Hugo)

Kort ordliste

  • Statisk side – en hjemmeside af færdige filer uden en kørende server bagved.
  • Hugo – programmet (en static site generator), der bygger VIDAR.
  • Docsy – det færdige dokumentationstema, VIDAR bygger ovenpå.
  • Markdown – tekstformatet, indholdet skrives i.
  • Frontmatter – indstillingsblokken øverst i hver indholdsfil.
  • Shortcode – en lille genbrugelig byggeklods, man kan indsætte i en side.
  • Taksonomi – et system af etiketter (roller, domæner …), der binder sider sammen på tværs.
  • Vendore – at lægge en kopi af tredjepartskode ind i projektet, så intet hentes udefra.
  • Subresource Integrity (SRI) – en kontrolsum på en medfølgende fil, så browseren afviser den, hvis filen er blevet ændret.
  • Git / Forgejo – versionsstyring, der gemmer al historik over ændringer.
  • CMS (Sveltia) – redigeringsværktøjet på /admin, hvor indhold rettes i en formular.