Arkitektur

Slik fungerer det

Pipelinen oppdag, filtrer, hent, lever og registrer, leveringsloggen og ID-porten + BankID nivå 4-autentiseringsflyten.

Pipelinen: oppdag, filtrer, hent, lever, registrer

Den headless workeren behandler alle tenantens organisasjonsnumre i én enkelt seriell løkke, med én pipeline-kjøring per org-nr per syklus (det kjører én worker per tenant). Altinn (Dialogporten) er den autoritative arbeidskøen. Hver kjøring gjenoppdager fra Altinn; workeren holder ingen lokal innbokstilstandsmaskin.

Som standard kjøres pipelinen på et fast intervall, med henting hver fjerde time; hyppigere henting kan avtales særskilt. I tillegg finnes en hendelsesdrevet modus (eksperimentell) der workeren poller Altinn Events-feeden for de overvåkede tjenesteressursene og bare kjører full oppdagelse når det faktisk har skjedd noe. Feeden filtreres på hendelsestyper som betyr nytt eller endret innhold, slik at for eksempel lest-kvitteringer ikke utløser kjøringer. Posisjonen i feeden lagres i leveringsloggen, så en omstart fortsetter der den slapp. Et planlagt sikkerhetsnett kjører uansett pipelinen med jevne mellomrom, slik at ingenting går tapt om en hendelse skulle utebli. Modusen krever scopet altinn:events.subscribe på tenantens innloggingsklient.

Hendelsesmodusen kan i tillegg settes opp med push-vekking: innloggings-appen registrerer da abonnementer i Altinn Events som peker på dens eget webhook-endepunkt, og en levering derfra vekker workeren fra null via en intern vekkekø. Selve varselet inneholder bare metadata (hendelses-id, ressurs og part), aldri innholdet i posten; workeren henter uansett alt gjennom den autentiserte pull-flyten fra sist lagrede posisjon. Slik kan workeren skaleres helt ned når det ikke er aktivitet, uten at noe går tapt om et varsel skulle utebli.

  1. Oppdag: spør Dialogporten search API for tenantens org, filtrert til nye/uleste elementer og relevante avsendere. Oppdagingen kjører på en fast tidsplan (tidsplanlagt polling).
  2. Filtrer: bruk avsender-, vedleggsutvidelse- og hent-etter-dato-filtrene. Filtrerte elementer får ingen logg-skriving og re-evalueres billig hver kjøring.
  3. Hent: for hver ny dialog, hent korrespondansedetaljer for å liste opp vedlegg, og strøm deretter hvert vedlegg fra dets endepunkt med et gyldig access-token og korrekt scope.
  4. Lever: skriv vedlegget til Blob-droppsonen. Se Droppsone for navnekonvensjon og format.
  5. Registrer: skriv utfallet til leveringsloggen (idempotens + dead-letter), re-assert MarkedAsUnopened på dialogen slik at tenantens innbokstilstand bevares (eller arkiver dialogen, hvis dere har valgt arkivering etter levering, se nedenfor), og emit telemetri til driftspartnerens sentrale overvåkingsmiljø. Telemetrien er avgrenset til tekniske driftsdata, uten innhold og uten filnavn, se Personvern og datahåndtering.

Hvorfor vi re-asserterer MarkedAsUnopened

Dialogporten regner en dialog som «sett» hvis den er hentet etter siste innholds-oppdatering og ikke har systemetiketten MarkedAsUnopened. Vår listing flytter «sist hentet»-tidspunktet, så uten et mottiltak ville pipelinen indirekte flippe dialogen fra ulest til lest i sluttbrukerens Altinn-innboks. Etter hver behandlet dialog re-setter tjenesten derfor MarkedAsUnopened, og en mislykket re-assertion logges men avbryter ikke leveringen.

Effekt: sluttbrukeren ser dialogen som ulest i Altinn-innboksen etter at vi har behandlet den, og kan selv markere den som lest når de faktisk åpner den.

Valgfri arkivering etter levering

Som tilvalg per tenant kan tjenesten i stedet arkivere dialogen i Altinn etter fullført levering, slik at Altinn-innboksen viser hva mailroomen allerede har håndtert. Når alle vedlegg i en dialog har fått et endelig utfall i leveringsloggen og minst ett av dem faktisk ble levert til droppsonen, settes systemetiketten Archive og dialogen flyttes til arkivmappen. Den markeres da ikke som ulest, en arkivert dialog er per definisjon håndtert.

Merk presiseringene:

  • Dialoger der ingenting ble levert (alt filtrert ut, eller levering ga opp etter gjentatte feil) blir stående urørt i innboksen som ulest, akkurat som i standardoppførselen. Arkivet skal aldri skjule post dere ikke har fått i droppsonen.
  • Arkiveringen skjer først etter at leveringsloggen har registrert leveransen. En feil under arkiveringen logges, men feiler aldri selve leveringen; dialogen blir da liggende ulest i innboksen og filene er uansett trygt levert.
  • Angring trenger ingen egen funksjon: dere kan når som helst flytte dialogen tilbake fra arkivmappen i Altinn-innboksen.

Standard er av, altså oppførselen beskrevet ovenfor der innbokstilstanden bevares. Tilvalget aktiveres av driftspartneren for deres utrulling.

Manuelle systemetikett-operasjoner

For operatør-initierte korrigeringer kan tjenesten utføre fire systemetikett-operasjoner mot en dialog via Dialogportens systemlabels-endepunkt (PUT /api/v1/enduser/dialogs/{id}/context/systemlabels): markere som lest/ulest og arkivere/av-arkivere.

OperasjonEffekt
Marker som lestFjerner MarkedAsUnopened.
Marker som ulestSetter MarkedAsUnopened.
ArkiverFlytter dialogen til Archive (fjerner Default/Bin).
Av-arkiverFlytter dialogen tilbake til Default (fjerner Archive/Bin).

Default/Bin/Archive er gjensidig utelukkende på Dialogporten-siden; arkiv-operasjonene sender med begge andre i removeLabels slik at kallet er idempotent uavhengig av dialogens nåværende mappe. Sent settes kun av Dialogporten selv og kan ikke endres av oss.

Av disse er arkivering den eneste pipelinen selv utfører, og bare når tilvalget ovenfor er aktivert. Ellers er den eneste automatiske systemetikett-handlingen re-assertion av MarkedAsUnopened.

Leveringsloggen

Loggen er en liten, holdbar oversikt over vedlegg vi har allerede fullført: en idempotens- og dead-letter-vakt, ikke en arbeidskø. Én post per (correspondenceId, attachmentId), skrevet etter at et utfall er kjent.

TilstandBetydning
DeliveredTerminal. Hoppes over på alle fremtidige kjøringer, selv etter at bloben er slettet.
RetryingEt leveringsforsøk mislyktes; attempts < MaxDeliveryAttempts.
AbandonedTerminal poison-melding: attempts == MaxDeliveryAttempts. Varslet én gang; aldri prøvd igjen.

Delivered-posten overlever vedlegget: droppsonen har kort GDPR-oppbevaring og vedlegg slettes når de er samlet inn. "Blob eksisterer" kan ikke være idempotens-nøkkel, for da ville et slettet element lastes ned på nytt og hente persondata vi bevisst slettet.

Autentisering: ID-porten + BankID nivå 4

Et rent maskin-til-maskin-token (Maskinporten med en systembruker) kan fastslå hvilken organisasjon som representeres, men det kan ikke bære et personlig høyt sikkerhetsnivå. Sensitiv korrespondanse (NAV, Namsmannen) er sperret ved sikkerhetsnivå 4 (BankID / Buypass / Commfides), som kun en personlig ID-porten-innlogging kan oppfylle. Kjøretiden er derfor menneskebakket, ikke M2M.

  • acr_values=idporten-loa-high låser nivå 4, og nivået kontrolleres i id_token-et etterpå. Tokenet valideres kryptografisk mot ID-portens egne signeringsnøkler, så kravene i det er bevist, ikke bare lest.
  • Scope inkluderer offline_access for å få et refresh-token: den langvarige legitimasjonen innloggings-appen bruker til å prege kortvarige access-tokens som workeren leser.
  • Klienten autentiserer med private_key_jwt; ID-porten krever klientautentisering på token- og refresh-grants for en konfidensiell klient.

Token-livssyklus og re-autentiseringsflyt

  1. En autorisert ansatt besøker innloggings-siden og logger inn via ID-porten + BankID nivå 4.
  2. Det resulterende tokensettet (id-, access- og refresh-token) skrives til tenantens Key Vault og passerer aldri gjennom Managed Service Providers infrastruktur. Id-tokenet bærer identiteten til medarbeideren som logget inn; se Personvern og datahåndtering.
  3. Innloggings-appen eier fornyelsen: en bakgrunnstjeneste fornyer access-tokenet før det utløper og skriver det tilbake til Key Vault. Den er den eneste skriveren mot det roterende refresh-tokenet — flere uavhengige fornyere ville ugyldiggjort hverandre — så fornyelsen er sentralisert her, og innloggings-appen holdes alltid varm (skalerer ikke til null).
  4. Ved hver kjøring leser workeren det gjeldende access-tokenet fra Key Vault (kun lesetilgang), bytter det mot et Altinn-plattform-token og kaller Dialogporten og attachment-endepunktene. Workeren fornyer aldri selv.
  5. Refresh-tokenet utløper etter ~90 dager (eller en fornyelse feiler); et Azure Monitor-varsel utløses og en ansatt re-autentiserer.

Autorisasjon: én innlogging dekker tenantens org-numre

Et ID-porten-token virker bare for organisasjonene der den innloggede personen har Altinn-roller (samme virkelighet som CORR-04001: en ikke-rollehaver får ikke hente korrespondansen). Workeren bruker ett delt token for alle organisasjonsnumrene den behandler, så den som logger inn må holde de nødvendige Altinn-rollene for hvert organisasjonsnummer tenanten behandler. I praksis er dette én utpekt person (typisk DAGL) som dekker alle org-numrene.

Lageret holder ett token om gangen — logger en annen person inn, overskrives det forrige (siste innlogging vinner). Modellen er altså én aktiv innlogging: flere personer kan bytte på å re-autentisere, men ikke bære hvert sitt token samtidig. Genuint samtidige per-person-tokens (ulike personer for ulike org-numre på én gang) ville krevd per-org nøkkeldelte hemmeligheter i stedet for det delte tokenet — bevisst utenfor scope her, der ett delt token gir lavest kompleksitet og angrepsflate.

Copyright © 2026