Skip to content

30 minutter til første innsikt

Denne tutorialen tar en teknisk evaluator fra en ren installasjon av publiserte artefakter til den første survey-responsen synlig i dashboardet — uten å gå veien om produksjonsherdning. Målet er å bevise at hele kjeden fungerer på din egen infrastruktur før du investerer i en full utrulling.

Lokal-eval, ikke produksjonsoppskrift

Denne tutorialen bruker en lokal Keycloak som OIDC-provider for å gjøre evalueringen selvbetjent. Det betyr ikke at Keycloak er den anbefalte eller forventede enterprise-provideren. Når din virksomhet kjøper Lumi, kobler du typisk mot Entra ID eller en annen sentral IdP — se Entra ID-sporet nederst og OIDC-oppsett for provider-detaljer.

Bruk dette løpet til å validere produktet, ikke som mal for prod-arkitektur.

Verifisert ende-til-ende

Løpet under er kjørt mot et disposable kind-cluster fra de publiserte artefaktene: OIDC-login, setup-wizard, innsendingskontrakten med origin-validering (samme V2-payload som widgeten sender via transport.submit, verifisert direkte mot API-et), og responsen synlig i dashboardet. Selve widget-rendringen i nettleser er ikke del av det beviset. Stegene beskriver observert oppførsel — der lokal-oppsettet krever en snarvei et produksjonscluster ikke trenger, er det merket kind-only.

Hva du trenger

KravDetalj
Kubernetes-clusterEt engangs-/disposable cluster holder (kind, minikube, k3s). 4 GB RAM ledig. Både linux/amd64 og linux/arm64 støttes — verifiser multi-arch-manifestene i Fase 0.
kubectl + helmHelm 3.14+.
GHCR-tilgangEt Personal Access Token (PAT) til ghcr.io — se Fase 0.
Docker med buildxFor multi-arch-preflight-sjekken i Fase 0 (docker buildx imagetools).
Realm-fixturelumi-realm.json — følger med evalueringstilgangen, brukes i Fase 1.
Node 20+For å bygge widget-vert-appen i Fase 4.
opensslFor å generere sessionSecret.

Tidsbudsjettet under forutsetter at clusteret allerede kjører. Selve clusteret (kind create cluster) er ikke regnet inn i de 30 minuttene.

OIDC-provideren må være på plass før Lumi installeres — chartet validerer at issuer-verdiene er ikke-tomme når authProvider=oidc, og rendringen feiler ellers. Derfor settes Keycloak opp i Fase 1, før Helm-installasjonen i Fase 2.

FaseHvaTid
0Tilgang til artefakter5 min
1Sett opp lokal Keycloak8 min
2Installer Lumi med Helm8 min
3Kjør setup-wizarden3 min
4Koble widgeten mot API-et4 min
5Se den første responsen2 min

Fase 0: Tilgang til artefakter (5 min)

Alle Lumi-imager og Helm-chartet er private GHCR-pakker. Før noe kan installeres må du logge inn mot ghcr.io med et token du har fått utlevert.

Credential-utlevering

For en pilot-evaluering utleverer vi GHCR-tilgangen (PAT med read:packages) og realm-fixturen lumi-realm.json direkte til deg — self-serve-evaluering uten manuell utlevering er ikke på plass. Hold tokenet utenfor shell-historikk og committede filer.

Logg inn mot OCI-registeret for å hente chartet (read:packages er nok):

sh
echo "$GHCR_TOKEN" | helm registry login ghcr.io -u <din-bruker> --password-stdin

Forventet utskrift: Login Succeeded.

Opprett namespace og en image-pull-secret slik at podene kan hente de private imagene:

sh
kubectl create namespace lumi
kubectl create secret docker-registry ghcr-pull \
  --namespace lumi \
  --docker-server=ghcr.io \
  --docker-username=<din-bruker> \
  --docker-password="$GHCR_TOKEN"

Pre-flight: verifiser at runtime-imagene dekker din plattform. Spesielt viktig på arm64-noder — et image som mangler plattformen din gir ImagePullBackOff med no match for platform in manifest først ved install:

sh
echo "$GHCR_TOKEN" | docker login ghcr.io -u <din-bruker> --password-stdin
docker buildx imagetools inspect ghcr.io/asorheim/lumi-analytics/lumi-api:1.0.0
docker buildx imagetools inspect ghcr.io/asorheim/lumi-analytics/lumi-dashboard:1.0.0

Forventet: begge manifestene lister både linux/amd64 og linux/arm64. Mangler en plattform: stopp og ta kontakt før du installerer.

Hvis det feiler:

  • denied / 403helm registry login → PAT-en mangler read:packages, eller du er ikke gitt tilgang til pakkene. Ta kontakt for ny utlevering.
  • unauthorized senere på helm pull → du logget inn mot feil bruker, eller tokenet er utløpt.

Fase 1: Sett opp lokal Keycloak (8 min)

For en selvbetjent evaluering kjører vi en lokal Keycloak i clusteret og bruker den som IdP. Lumi-API-et trenger issuer-URL-en herfra allerede i Fase 2, så Keycloak settes opp først.

Prinsippet som styrer hele OIDC-oppsettet: den robuste konfigurasjonen er én issuer-URL som når Keycloak likt fra både podene og nettleseren, slik at iss-claimet er identisk i nettleserens login-redirect og i den server-side kodevekslingen. Lokalt løses det med verts-navnet keycloak.127.0.0.1.nip.io — nip.io slår opp til 127.0.0.1, så nettleseren treffer en port-forward, mens podene rutes til Keycloak-Service-en via en hostAliases-snarvei (kind-only, Fase 2). I produksjon er dette automatisk: én offentlig IdP-URL som både cluster og brukere når.

Realm, client, mappere, grupper og test-brukere importeres fra fixturen lumi-realm.json — ingen manuelle admin-konsoll-klikk. Opprett en ConfigMap fra fixturen, og deploy en utviklings-Keycloak som importerer den ved oppstart:

sh
kubectl -n lumi create configmap lumi-realm --from-file=lumi-realm.json
kubectl -n lumi apply -f keycloak.yaml

der keycloak.yaml er:

yaml
# Disposable Keycloak for lokal evaluering. IKKE for produksjon.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
  namespace: lumi
  labels: { app: keycloak }
spec:
  replicas: 1
  selector:
    matchLabels: { app: keycloak }
  template:
    metadata:
      labels: { app: keycloak }
    spec:
      containers:
        - name: keycloak
          image: quay.io/keycloak/keycloak:26.2
          args: ["start-dev", "--import-realm"]
          env:
            - { name: KC_BOOTSTRAP_ADMIN_USERNAME, value: "admin" }
            - { name: KC_BOOTSTRAP_ADMIN_PASSWORD, value: "admin" }
            - { name: KC_HEALTH_ENABLED, value: "true" }
          ports:
            - { name: http, containerPort: 8080 }
            - { name: mgmt, containerPort: 9000 }
          readinessProbe:
            httpGet: { path: /health/ready, port: 9000 }
            initialDelaySeconds: 20
            periodSeconds: 5
            timeoutSeconds: 3
            failureThreshold: 48
          volumeMounts:
            - { name: realm-import, mountPath: /opt/keycloak/data/import, readOnly: true }
      volumes:
        - name: realm-import
          configMap:
            name: lumi-realm
---
apiVersion: v1
kind: Service
metadata:
  name: keycloak
  namespace: lumi
  labels: { app: keycloak }
spec:
  selector: { app: keycloak }
  ports:
    - { name: http, port: 8080, targetPort: 8080 }

Vent til Keycloak er klar (boot + realm-import tar et par minutter), og start port-forwarden som nettleseren skal bruke. La den stå i en egen terminal gjennom hele evalueringen — issuer-URL-en avhenger av den:

sh
kubectl -n lumi rollout status deploy/keycloak
kubectl -n lumi port-forward svc/keycloak 8080:8080

Issuer-URL-en for resten av tutorialen er dermed http://keycloak.127.0.0.1.nip.io:8080/realms/lumi.

Fixturen definerer alt resten av tutorialen trenger:

  • realm lumi med public client lumi-dashboard (standard flow + PKCE S256)
  • Audience-mapperen (aud: lumi-dashboard) og Group Membership-mapperen (claim groups, full path av) — de to claimene API-et validerer, og som en default OIDC-client ikke sender
  • gruppene lumi-admins (matcher adminGroup i Fase 2) og team-alpha
  • tre disposable test-brukere, inkludert [email protected] (medlem av lumi-admins) som du logger inn med i Fase 3

Fixturen er insecure-by-design — kun for lokal evaluering

lumi-realm.json er selv-merket «LUMI INSTALL PROOF — NOT FOR PRODUCTION», og skal aldri importeres i en tenant som møter ekte brukere:

  • Test-brukerne ([email protected], [email protected], [email protected]) har hardkodet passord test.
  • Den konfidensielle lumi-service-clienten har hardkodet secret. Den finnes kun for API-ets integrasjonstester; dashboard-login trenger den ikke.
  • Realmet bruker sslRequired: "external" slik at localhost-HTTP fungerer.

Clienten lumi-dashboard er dessuten pinnet til redirect-URI http://localhost:3000/* — det passer denne tutorialen (dashboardet port-forwardes på 3000 i Fase 3), men en produksjons-IdP må settes opp med ditt reelle dashboard-host. For produksjon: opprett et tomt realm i din egen IdP og repliser de to mapperne — se OIDC-oppsett → Keycloak-oppsett.

Trenger du å inspisere realmet, er admin-konsollet tilgjengelig på http://localhost:8080 (admin / admin) mens port-forwarden kjører.


Fase 2: Installer Lumi med Helm (8 min)

Chartet publiseres som OCI-artefakt. Versjon 1.0.1 er den verifiserte chart-versjonen for launch; API- og dashboard-imagene peker fortsatt på 1.0.0 via chartets appVersion.

Lag en eval-values.yaml med samme issuer-URL for API og dashboard — single-issuer er den verifiserte konfigurasjonen:

yaml
# eval-values.yaml
global:
  imagePullSecrets:
    - name: ghcr-pull

api:
  image:
    tag: "1.0.0"
    # Sett digest fra release-audit for produksjonspinning:
    # digest: sha256:API_IMAGE_DIGEST_FROM_RELEASE_AUDIT
  env:
    lumiEnv: production
    authProvider: oidc
    oidcIssuerUrl: http://keycloak.127.0.0.1.nip.io:8080/realms/lumi
    oidcAudience: lumi-dashboard
    dashboardClientId: lumi-dashboard
    adminGroup: lumi-admins

dashboard:
  image:
    tag: "1.0.0"
    # Sett digest fra release-audit for produksjonspinning:
    # digest: sha256:DASHBOARD_IMAGE_DIGEST_FROM_RELEASE_AUDIT
  env:
    authProvider: oidc
    oidcIssuerUrl: http://keycloak.127.0.0.1.nip.io:8080/realms/lumi   # SAMME url som API-et
    # oidcBrowserIssuerUrl: bevisst usatt — single-issuer
    oidcClientId: lumi-dashboard
    oidcRedirectUri: http://localhost:3000/auth/callback
  secret:
    sessionSecret: SESSION_SECRET_PLACEHOLDER

postgresql:
  auth:
    password: POSTGRES_PASSWORD_PLACEHOLDER

valkey:
  auth:
    password: VALKEY_PASSWORD_PLACEHOLDER

ingress:
  enabled: false

Split-issuer er ikke wet-run-verifisert — bruk single-issuer

Chartet eksponerer dashboard.env.oidcBrowserIssuerUrl for split-horizon-oppsett (én issuer-URL for nettleseren, en annen for podene). Konfigurasjonen er støttet i koden (discovery hentes fra server-issueren; browser-issueren brukes kun i login-redirecten), men den er ikke verifisert ende-til-ende slik single-issuer-løpet i denne tutorialen er. Hold deg til single-issuer for evalueringen; split-horizon er kun aktuelt når IdP-en genuint har to adresser. Merk at API-ets oidcIssuerUrl uansett må matche iss-claimet IdP-en stempler — se OIDC-oppsett → split-networking for detaljene.

Generer hemmelighetene og installer:

sh
sed -i.bak \
  -e "s|SESSION_SECRET_PLACEHOLDER|$(openssl rand -base64 32)|" \
  -e "s|POSTGRES_PASSWORD_PLACEHOLDER|$(openssl rand -hex 24)|" \
  -e "s|VALKEY_PASSWORD_PLACEHOLDER|$(openssl rand -hex 24)|" \
  eval-values.yaml && rm eval-values.yaml.bak

helm install lumi oci://ghcr.io/asorheim/lumi-analytics/charts/lumi \
  --version 1.0.1 \
  --namespace lumi \
  -f eval-values.yaml

Forventet utskrift: STATUS: deployed.

Kind-only: pek podene på Keycloak-hostnavnet

keycloak.127.0.0.1.nip.io slår opp til 127.0.0.1 — riktig for nettleseren (port-forwarden fra Fase 1), men feil inne i podene. Legg inn en hostAliases-oppføring som ruter hostnavnet til Keycloak-Service-en, rett etter helm install:

sh
KC_IP=$(kubectl -n lumi get svc keycloak -o jsonpath='{.spec.clusterIP}')
kubectl -n lumi patch deploy lumi-api --type=strategic -p "{\"spec\":{\"template\":{\"spec\":{\"hostAliases\":[{\"ip\":\"$KC_IP\",\"hostnames\":[\"keycloak.127.0.0.1.nip.io\"]}]}}}}"
kubectl -n lumi patch deploy lumi-dashboard --type=strategic -p "{\"spec\":{\"template\":{\"spec\":{\"hostAliases\":[{\"ip\":\"$KC_IP\",\"hostnames\":[\"keycloak.127.0.0.1.nip.io\"]}]}}}}"

Et produksjonscluster trenger ikke dette — en reell, offentlig IdP-URL løses likt av DNS overalt.

Etter ~1–2 minutter skal podene være oppe:

sh
kubectl -n lumi get pods

Forvent Runninglumi-api, lumi-dashboard, lumi-postgresql-0 og lumi-valkey-*. API-et kjører Flyway-migrasjonene automatisk ved oppstart.

Verifiser at API-et er friskt via det interne Prometheus-endepunktet (merk: stien er /internal/prometheus, ikke /metrics). Service-en eksponerer port 80, så port-forwarden mapper dit:

sh
kubectl -n lumi port-forward svc/lumi-api 8081:80 &
curl -s http://localhost:8081/internal/prometheus | head -5
kill %1   # stopp forwarden; Fase 4 starter den på nytt

Produksjon: ikke inline secrets i values

Denne tutorialen skriver genererte hemmeligheter rett inn i values-filen — greit for et disposable eval-cluster, ikke for produksjon. Chartet støtter existingSecret-referanser for API, dashboard, PostgreSQL og Valkey (api.existingSecret, dashboard.existingSecret, postgresql.auth.existingSecret, valkey.auth.existingSecret), slik at SOPS, sealed-secrets eller plattformens secret-manager eier runtime-Secretene. Se charts/lumi/values.yaml for nøkkel-navnene og oppgraderingsguiden for rotasjonsregelen.

Hvis det feiler:

  • helm install aborterer med f.eks. api.env.oidcIssuerUrl (required when authProvider=oidc) must be set to a non-empty value (template-feil ved render) → en påkrevd OIDC-verdi i eval-values.yaml er tom. Dette skjer før noen pod startes. Sjekk at alle de OIDC-påkrevde verdiene (oidcIssuerUrl, oidcAudience, oidcClientId, oidcRedirectUri, adminGroup, sessionSecret) har ikke-tomme verdier.
  • ImagePullBackOff → pull-secreten fra Fase 0 mangler eller er i feil namespace. Sjekk kubectl -n lumi get secret ghcr-pull.
  • CrashLoopBackOff eller Pending-readiness på API/dashboard etter oppstart → podene når ikke Keycloak på issuer-URL-en. Sjekk at hostAliases-patchen over er lagt inn (kind-only), at Keycloak kjører (kubectl -n lumi get pods), og kubectl -n lumi logs deploy/lumi-api.
  • Postgres-poden henger på Pending → clusteret mangler en default StorageClass. kind og minikube har en; bare-metal kan trenge én.

Fase 3: Kjør setup-wizarden (3 min)

Port-forward dashboardet på port 3000 — det er porten både oidcRedirectUri i values og fixturens redirect-URI er pinnet til:

sh
kubectl -n lumi port-forward svc/lumi-dashboard 3000:80

Åpne http://localhost:3000 og logg inn med test-brukeren fra fixturen: [email protected] / test (medlem av lumi-admins). Første gang havner du i /setup-wizarden, som tar deg gjennom fire steg:

  1. Organisasjon — oppretter organisasjonen og det første teamet; team-slugen blir default — den brukes i Fase 5.
  2. API-nøkler — opprett en Publishable (pk)-nøkkel her. Nye pk-nøkler krever en tillatt origin: sett Applikasjonens origin til http://localhost:5173 (det er der widget-vert-appen kjører i Fase 4). Kopier lumi_pk_live_...-verdien som vises; den vises bare én gang.
  3. Embed widget — viser et kodesnutt-utgangspunkt. Fase 4 under bruker en herdet variant av samme snutt, så du kan gå videre.
  4. Ferdig — du lander i dashboardet.

En bruker i lumi-admins-gruppen (matcher adminGroup fra Fase 2) får org-admin-UI-et, og dashboardet er tomt til den første innsendingen kommer — det er Fase 4–5. Nøkler kan administreres senere under Innstillinger → API-nøkler.

For å koble Keycloak-grupper til Lumi-team (f.eks. team-alpha fra fixturen), følg OIDC-oppsett → Mappe Keycloak-grupper til Lumi-team.


Fase 4: Koble widgeten mot API-et (4 min)

Widgeten er publisert på npm som @lumianalytics/survey. Lag en minimal React-app som vert (f.eks. med Vite, som kjører dev-serveren på http://localhost:5173), og installer pakken pinnet til den verifiserte launch-versjonen:

sh
npm install @lumianalytics/[email protected]

Widget-verten poster til API-et på et eget localhost-opphav. Port-forward API-tjenesten (service-port 80) i en egen terminal:

sh
kubectl -n lumi port-forward svc/lumi-api 8081:80

Rendre LumiSurveyDock med publishable-nøkkelen fra Fase 3:

tsx
import {
  LumiSurveyDock,
  DEFAULT_SURVEY_RATING,
  type LumiSurveyTransport,
} from "@lumianalytics/survey";
import "@lumianalytics/survey/styles.css";

const transport: LumiSurveyTransport = {
  async submit(submission) {
    const res = await fetch("http://localhost:8081/api/v1/submission", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Api-Key": "lumi_pk_live_...",
      },
      body: JSON.stringify(submission.transportPayload),
    });
    // fetch resolverer også på 403/500 — kast slik at widgeten viser
    // feiltilstand i stedet for falsk suksess.
    if (!res.ok) {
      throw new Error(`Innsending avvist: ${res.status}`);
    }
  },
};

export function App() {
  return (
    <LumiSurveyDock
      surveyId="eval-rating"
      survey={DEFAULT_SURVEY_RATING}
      transport={transport}
    />
  );
}

Origin-en http://localhost:5173 være registrert som tillatt opprinnelse på pk-nøkkelen (det gjorde du i Fase 3) — API-et validerer Origin-headeren og avviser ukjente opphav med 403 Forbidden. Se Produksjonsherdning for hvordan origin-validering fungerer.

Produksjon: samme host, ingen egen port-forward

Port-forwarden over er eval-stien. I produksjon ruter chartets ingress både dashboard og API på samme host/ til dashboardet og /api til API-tjenesten — så widgeten poster til https://dashboard.example.com/api/v1/submission uten et eget opphav å sette opp. Dashboardet proxyer ikke innsendinger.

Start vert-appen (npm run dev), åpne http://localhost:5173, og fyll ut surveyen som dukker opp nederst.

Hvis det feiler:

  • 403 Forbidden med Origin not allowed for this keyhttp://localhost:5173 er ikke registrert som tillatt opprinnelse på pk-nøkkelen. Legg den til under Innstillinger → API-nøkler og prøv igjen.

Fase 5: Se den første responsen (2 min)

Gå tilbake til dashboardet på http://localhost:3000, åpne Tilbakemeldinger for teamet ditt (slug default fra Fase 3), og bekreft at responsen du nettopp sendte vises.

Suksesskriterium — du er ferdig når: svaret fra Fase 4 er synlig i dashboardet, med riktig spørsmål og tidsstempel, filtrerbart på tidsperiode.


Entra ID og andre providere

Denne tutorialen brukte lokal Keycloak for å gjøre evalueringen selvbetjent. For en faktisk enterprise-utrulling kobler du mot din sentrale IdP:

  • Entra ID — det vanligste enterprise-sporet. Et live-verifisert Entra-avsnitt (audience, groups-claim, admin-gruppe, team-mapping mot en ekte tenant) legges til her som et eget spor når det er verifisert. Behandl ikke Keycloak-løpet over som Entra-oppskrift.
  • Auth0 / Okta — kildeverifiserte konfigurasjonsnotater finnes i OIDC-oppsett, men er ennå ikke E2E-verifisert.

Neste steg

  • Produksjonsherdning — origin-validering, nøkkelrotasjon og sikkerhetskontekster før du går videre fra eval.
  • Distribuer Lumi — full referanse for Helm- og Compose-oppsett, miljøvariabler og image-mirroring.
  • OIDC-oppsett — provider-spesifikk client- og mapper-konfigurasjon.
  • Releaseverifisering — hvordan artefaktene du installerte er bygget og signert.

Lumi Analytics — bygget på navikt/lumi (MIT-lisens)