Hoppa till innehållet

Devportalen /portal/dev

Startsideportalens ekosystem för människa och agent

Agentvy

Teknisk stack

Valen, versionerna och skälen – plus container, deploy och miljövariabler.

page manifest · /dev/stack
id: stack
path: /dev/stack
titel: Teknisk stack
summering: Valen, versionerna och skälen – plus container, deploy och miljövariabler.
nyckelord: stack, next, react, typescript, tailwind, yarn, docker, dokploy, env

## Valen
Kommunens stack, utan deras byggblock. Avvikelserna är medvetna och dokumenterade som ADR:er.

Lager | Val | Version | Varför
Ramverk | Next.js, App Router | 16.3.1 | Kommunens starter kör Pages Router; App Router är Next:s huvudspår och passar modulkompositionen (ADR-001).
UI | React | 19.2.8 | Server components som default.
Språk | TypeScript strict | ~6.0.3 | Inga any. Varianter som unionstyper.
Stil | Tailwind v4 med egna tokens | 4.3.3 | Tokens som @theme i packages/tokens, porterade 1:1 ur prototypen.
Komponentgrund | shadcn/ui i repot | new-york | Radix bär tillgängligheten, färgen är vår (ADR-008).
State | Zustand | 5.0.15 | Som kommunens starter. Roll och dockningsläge.
Validering | Zod | 4.4.3 | Inkommande data och schemat bakom structured outputs.
Dra och släpp | @dnd-kit (core, sortable) | 6.3.1 / 10.0.0 | Användarens ordning av korten (ADR-018). Tangentbord och skärmläsare ingår; ett eget dragsystem skulle inte göra det.
AI | @anthropic-ai/sdk | 0.120.0 | Provider-agnostisk adapter bakom AI_PROVIDER.
Monorepo | yarn workspaces | yarn 4.9.1 | Kommunen kör yarn. Turborepo först om byggtiderna kräver.
Test | Vitest | 4.1.11 | Kommunens grindar i lättviktig prototypversion.
Runtime | Node | >= 24 | Som kommunens starter.

## Container och deploy

- next.config.ts har output: "standalone" och basePath: "/portal" – containern kör node apps/portal/server.js.
- outputFileTracingRoot pekar två nivåer upp, annars gissar Next fel monorepo-rot.
- Varje nytt modulpaket måste läggas i transpilePackages.
- Dokploy pekar på docker-compose.yml. Git-push ger deploy.
- En Compose-tjänst får inga Traefik-etiketter automatiskt – bara Application-typen gör det. Etiketterna och dokploy-network står därför i compose-filen. Utan dem svarar Traefik 404 med sitt standardcertifikat, och Let's Encrypt får inget att utfärda för.
- Healthchecken använder 127.0.0.1, inte localhost: busybox wget provar ::1 först och Next lyssnar bara på IPv4.
- Tjänsterna heter start-web, och framåt start-bff, start-redis, start-db. Prefixet skiljer stacken från andra på samma värd.
- Vid isolerad deploy ligger start-web i två nätverk: stackens eget och dokploy-network. traefik.docker.network avgör vilket Traefik ska använda – utan den kan den välja fel IP. Framtida tjänster ska bara ligga i det egna nätverket.
- Hemligheter läggs som Environment i Dokploy, aldrig i repot. Men de måste också listas under environment i docker-compose.yml – Dokploys Environment fyller bara compose-interpolationen, och en variabel som saknas i listan når aldrig containern.
- /api/health svarar { ok: true } och används både som healthcheck och rökkontroll efter deploy.

Verifiera som Dokploy gör:
```
docker network create dokploy-network   # en gång; på servern gör Dokploy det
docker compose up -d --build
docker inspect $(docker compose ps -q start-web) \
  --format '{{.State.Health.Status}}'          # ska bli healthy
```

## Miljövariabler

Variabel | Värden | Betydelse
DOMAIN | värdnamn | Domänen Traefik ska svara på. Standard start.sundsvall.dev; sätts i Dokploy om den ändras.
ACCESS_CODE | valfri sträng | Låser demon bakom en kod (ADR-012). Projektets yta på roten (utom ritningen och ändringsloggen), prototypens ingång /portal, /vision och /dev är alltid öppna. Osatt eller tom = ingen spärr alls, vilket betyder att en deploy utan variabeln är helt öppen.
AI_PROVIDER | mock | anthropic | mock ger regelmotorn för offline-demo. Standard är mock.
AI_MODELL | modell-id | Standard claude-sonnet-5. Bytbar utan kodändring.
ANTHROPIC_API_KEY | hemlighet | Bara på servern. Saknas den faller adaptern tillbaka på mock – och varnar en gång i loggen, så att tystnaden inte döljer det.
MIN_AI_MCP | JSON-lista | MCP-tjänster som Min AI får ansluta till: [{ id, namn, beskrivning, url, token? }], bara https (ADR-024). Tom = inga. Listas i docker-compose.yml.
ROSTNING_ADMIN_KOD | hemlighet | Slår på workshoprösten och låser /rosta/admin (ADR-028). Tom = röstningen är avstängd.
STATUS_SKRIVNYCKEL | hemlighet | Krävs för att skriva statusrapporter, som Bearer-token mot /api/status eller i formuläret på /status/ny (ADR-032). Skild från ACCESS_CODE. Tom = ingen kan skriva; läsningen är öppen.

## Kvalitetsgrindar

- yarn lint och yarn test gröna före varje commit. Båda körs från roten – eslint.config.mjs och vitest.config.mts – och täcker alla paket, så ett nytt modulpaket omfattas utan att något läggs till.
- yarn typecheck kör tsc i varje paket. Modulpaketen har noUncheckedIndexedAccess, så en uppslagning ger T | undefined.
- Drifttesterna i dev/portal/portal.test.ts fäller bygget när portalen beskriver något som inte finns: en modul utan katalogpost eller dok, ett dok som saknar ett obligatoriskt fält, eller en manifestkopia som glidit från module-sdk.
- docker compose up --build före push.
- Katalogpost och dok skrivna innan funktionen lämnas över för test. Täckningen syns på /dev/docs och kontrolleras av yarn test.
- Conventional Commits, ämnesraden på svenska.
- Ett arkitekturbeslut skrivs som en kort ADR i docs/beslut/ i samma commit som ändringen.

Hela portalen i samma form: /dev/llms.txt · /dev/spec.json