Denne side beskriver, hvordan invoia er bygget, og hvilke sikkerhedsforanstaltninger der faktisk er implementeret i produktet i dag. Den beskriver ikke hensigter, planer eller attester. Hvor en foranstaltning ikke findes, står det direkte.
Platform og hosting
invoia er én applikation. Den er bygget i Next.js og hostes hos Vercel. Alle data lagres i én Postgres-database hos Supabase i AWS-regionen eu-west-1 (Irland).
Planlagte og hændelsesudløste baggrundsopgaver — den daglige synkronisering med Dinero, udstedelse af fakturaer og månedens periodisering — orkestreres gennem Inngest og skriver til den samme database.
Der findes ingen lokal installation, ingen selvhostet udgave og ingen anden driftsplatform.
Vi udtaler os ikke om TLS-versioner, brandmure, netværkssegmentering eller angrebsbeskyttelse på netværkslaget. Det er egenskaber ved de hostede platforme, ikke noget invoia selv konfigurerer, og vi vil ikke tilskrive os foranstaltninger, vi hverken sætter eller kontrollerer.
Adgang til kontoen
Autentificering håndteres af Supabase Auth. Der er tre måder at logge ind på: e-mail og adgangskode, Google-konto eller Microsoft-konto. Der findes ikke andre loginmetoder. Log ind-siden viser en knap til adgangsnøgle (passkey); den vej er ikke sat i drift, og den giver ikke adgang til kontoen i dag.
To-faktor-godkendelse er obligatorisk. Hver eneste konto opsætter TOTP under oprettelsen — der er ikke et trin, der springer det over, og ingen konto kommer forbi uden. Den delte TOTP-hemmelighed krypteres på applikationsniveau med AES-256-GCM, med en tilfældigt genereret initialiseringsvektor pr. kryptering og et autentificeringsmærke, før den overhovedet skrives til databasen.
Engangskoder på e-mail — til bekræftelse af e-mailadressen og til nulstilling af adgangskode — opbevares aldrig i klartekst. Vi gemmer alene et SHA-256-aftryk af koden. Koden udløber efter 10 minutter, der er højst 5 forsøg pr. kode, og en ny kode ugyldiggør enhver tidligere.
Adgangskoder skal være mindst 12 tegn. Ved oprettelse og ved nulstilling kontrolleres den valgte adgangskode mod Have I Been Pwneds register over adgangskoder fra kendte databrud. Kontrollen sker efter k-anonymitetsmodellen: adgangskoden hashes med SHA-1 på vores egen server, og kun de første fem tegn af aftrykket sendes videre. Hele adgangskoden forlader aldrig serveren, og din IP-adresse gør det heller ikke. Ved nulstilling afvises en adgangskode, der optræder i registret; ved oprettelse vises resultatet som en advarsel. Svarer registret ikke, blokerer vi ikke — kontrollen er en hjælp, ikke en garanti.
En anmodning om nulstilling af adgangskode besvares ens, uanset om adressen findes hos os. Forløbet røber ikke, om der er en konto.
Alle serverhandlinger i login- og oprettelsesforløbet validerer deres input mod et skema, før der røres ved databasen.
Adskillelse og kryptering
Adskillelsen mellem kunder er forankret i databasen. Row Level Security — sikkerhed på rækkeniveau — er slået til på samtlige tabeller. Politikkerne kalder fire hjælpefunktioner i databasen, når de prøves af: hvilken konto brugeren tilhører, om brugeren er administrator, og hvilke integrationer brugeren har fået adgang til. Kontodata er afgrænset af kontoen. Fakturagrundlaget — kunder, fakturaer, fakturalinjer, timeregistreringer, periodiseringsbilag og serier — er afgrænset af integrationsadgangen. På en almindelig forbindelse fejler en forespørgsel efter en anden kundes data i databasen, ikke først i applikationslaget.
Administratorrollen håndhæves begge steder — både i databasens politikker og i applikationskoden — for invitationer, brugeradministration, integrationer, API-nøgler, webhook-endepunkter og abonnement.
Tabellerne bag OAuth-serveren og bag MCP-serverens skriveforslag har sikkerhed på rækkeniveau slået til uden en eneste politik. De er dermed lukkede for enhver almindelig forbindelse. Applikationen bruger tre adskilte databaseklienter. Den ene af dem omgår sikkerheden på rækkeniveau og bruges af baggrundsopgaverne og af en række handlinger inde i appen; på de stier er det applikationskoden, der afgrænser forespørgslen til kontoen og til integrationen.
Præcis hvad der krypteres. Der er tre lag at holde adskilt:
- Transport. Trafik mellem browseren og invoia, og mellem invoia og hver enkelt underdatabehandler, går over krypterede forbindelser. Vi angiver ikke en protokolversion, fordi den er en egenskab ved værtsplatformen og ikke noget, invoia sætter.
- Lagringsmedie. Databasen driftes af Supabase på AWS. Om og hvordan selve lagringsmediet er krypteret, er en egenskab ved den platform; vi opgiver det ikke, fordi vi hverken sætter eller kontrollerer det.
- Applikationsniveau. Præcis ét felt krypterer invoia selv, før det skrives: TOTP-hemmeligheden, med AES-256-GCM.
Kortoplysninger opbevares ikke — hverken krypteret eller i klartekst. Kortet indtastes direkte i Stripes eget indlejrede felt, og invoias servere ser aldrig kortnummeret.
Invoias egne hemmeligheder — API-nøglerne til de tjenester, vi kalder — findes udelukkende som miljøvariabler i driftsmiljøet og er ikke en del af kildekoden. De adgangsbeviser, der hører til din egen konto, ligger derimod i databasen: dine Dinero- og e-conomic-tokens og signeringsnøglen til et webhook-endepunkt gemmes som almindelig tekst, fordi de skal kunne bruges igen. De er beskyttet af databasens adgangskontrol, ikke af kryptering på applikationsniveau.
Underdatabehandlere
Listen er fuldstændig for de tjenester, applikationen kalder eller indlæser i dag.
| Leverandør | Anvendelse | Placering |
|---|---|---|
| Supabase | Database og autentificering. Modtager alle data i produktet. | AWS eu-west-1, Irland |
| Vercel | Hosting, indholdslevering og webanalyse. Analysen kører på alle sider, også i den indloggede del. | Globalt distribueret netværk |
| Stripe | Abonnement og betaling. Modtager virksomhedsnavn, faktureringsadresse, momsnummer og faktureringsmail. | Globalt |
| Resend | Transaktionsmails til dine egne brugere: bekræftelse af e-mail, nulstilling af adgangskode og invitationer. | Fastlægges af udbyderen |
| Dinero (Visma) | Fakturering og bogføring. Modtager dine debitorers stamdata, fakturaer, kreditnotaer, varer og periodiseringsbilag. Dinero udsender selv fakturadokumentet til din kunde. | Fastlægges af udbyderen |
| e-conomic (Visma) | Alene selve tilslutningen. Ved tilslutning hentes virksomhedsnavn, aftalenummer, CVR-nummer og basisvaluta. Der kaldes ikke videre. | Fastlægges af udbyderen |
| Inngest | Afvikling af planlagte og hændelsesudløste baggrundsopgaver. Hændelser kan indeholde bruger-id, navn, e-mailadresse og IP-adresse. | Fastlægges af udbyderen |
| Google og Microsoft | Login med Google- eller Microsoft-konto, formidlet gennem Supabase Auth. | Globalt |
| Have I Been Pwned | Kontrol af adgangskoder mod kendte databrud. Modtager de første fem tegn af et SHA-1-aftryk og vores servers IP-adresse — ikke adgangskoden og ikke din IP-adresse. | Globalt, leveret via Cloudflare |
| Frankfurter | Daglige valutakurser fra Den Europæiske Centralbank. Modtager valutakoder og datoer, ingen personoplysninger. | Fastlægges af udbyderen |
| Upstash | Hastighedsbegrænsning på det offentlige API, når tjenesten er slået til i driftsmiljøet. Modtager alene identifikatorer for API-nøgler. Begrænsningen er ingen garanti: svarer opslaget ikke, slipper kaldet igennem, og MCP-serveren er ikke omfattet. | Fastlægges af udbyderen |
| Twilio | Sms-udsendelse. Opsat som leverandør, men ikke i brug: der sendes i dag ingen data til Twilio. | Fastlægges af udbyderen |
Hvor der står fastlægges af udbyderen, er placeringen en egenskab ved leverandørens egen platform og ikke noget, invoia konfigurerer. Vi opgiver den ikke, fordi vi ikke kan indestå for den.
invoia sender ikke data til en AI-leverandør. Giver du selv en AI-klient adgang gennem MCP-serveren eller en API-nøgle, er det din egen beslutning og din egen adgang — se næste afsnit.
Maskinadgang
invoia har tre maskinelle grænseflader: et REST-API, en MCP-server og webhooks.
API-nøgler. En nøgle dannes af en kryptografisk tilfældighedskilde og vises én gang, i det øjeblik den oprettes. Vi opbevarer alene et SHA-256-aftryk af den. Den fulde nøgle findes ikke i vores database og kan ikke vises igen. Nøgler oprettes og tilbagekaldes udelukkende af en administrator inde i appen. En nøgle udløber ikke af sig selv — den er gyldig, indtil nogen tilbagekalder den.
MCP-serveren er en OAuth 2.1-ressourceserver med sin egen autorisationsserver. PKCE med S256 er et krav, håndhævet af en betingelse i databasen. Autorisationskoder kan bruges én gang og lever omkring et minut. Adgangstokens er signeret med EdDSA, lever ti minutter og er bundet til denne tjeneste som modtager. Fornyelsestokens opbevares alene som aftryk, roteres ved hver brug, og forsøg på at genbruge et allerede brugt token tilbagekalder hele kæden.
Et token er bundet til én integration. Når du godkender en klient, vælger du præcis én af dine tilsluttede regnskabsorganisationer, og adgangen rækker ikke ud over den. Til gengæld er godkendelsen enten-eller: du kan ikke skære i de rettigheder, klienten beder om, og det kræver ikke administratorrollen at give dem — enhver bruger med adgang til den pågældende integration kan godkende. Klienter kan registrere sig selv uden forudgående godkendelse fra os, og MCP-serveren har ingen hastighedsbegrænsning.
Webhooks. Du kan registrere et endepunkt. Hver udsendelse signeres med HMAC-SHA256 over et tidsstemplet indhold, så du kan verificere afsenderen, og verifikationen sker med en sammenligning, der tager samme tid uanset udfald. Selve udsendelsen er ikke sat i drift i dag.
Det, der er værd at forstå: en API-nøgle eller et OAuth-token bærer din egen myndighed. Systemet kan ikke se forskel på dig og den, der holder nøglen. En nøgle med skriveadgang kan bogføre fakturaer i din Dinero-organisation og få dem sendt til dine egne kunder — handlinger, der er uigenkaldelige i dit eget regnskab. Behandl en nøgle som en fuldmagt.
Din kontrol er tilbagekaldelsen. En administrator kan tilbagekalde en API-nøgle i appen, og den virker med det samme. For OAuth-adgange givet til en MCP-klient findes der i dag ingen oversigt i appen og ingen knap, der trækker adgangen tilbage. Skriv til support@invoia.io, så trækker vi den for dig.
Sletning
Sletning af en konto sker manuelt. Skriv til support@invoia.io fra en adresse, der er knyttet til kontoen, så sletter vi kontoen og de data, der ligger i vores egen database. Det er en håndholdt proces, ikke en automatiseret. Den rækker ikke ud over vores egen database: din kunderelation hos Stripe — virksomhedsnavn, faktureringsadresse, momsnummer, faktureringsmail og Stripes egne fakturaer — bliver stående hos Stripe, og sikkerhedskopier og logfiler hos vores leverandører udløber efter leverandørernes egne vilkår og ikke efter vores. Det, der er skrevet ind i din egen Dinero-organisation, bliver også stående — det står længere nede.
Der findes ikke selvbetjent sletning i appen, og der findes ikke en eksportfunktion. Vil du have data ud, inden vi sletter, skal det aftales i samme ombæring.
Vi angiver ingen frist for sletning. Vi har ikke et system, der måler den, og vi skriver hellere ingenting end et tal, vi ikke kan stå inde for.
Det, invoia har skrevet ind i din egen Dinero-organisation, bliver stående. Bogførte fakturaer, kreditnotaer og periodiseringsbilag er dit eget bogføringsmateriale i dit eget regnskabssystem. invoia kan ikke slette dem — Dinero afviser sletning af bogført materiale — og det er heller ikke meningen: opbevaringen af det materiale følger de regler, du som bogføringspligtig er underlagt, og din egen aftale med Dinero.
Afbryder du forbindelsen til Dinero, slettes hele fakturagrundlaget på invoias side. Kunder, fakturaer, fakturalinjer, timeregistreringer, periodiseringsbilag, serier og kreditnotaer forsvinder sammen med forbindelsen. Det kan ikke fortrydes, og der ligger ingen eksport forud. Bogføringen i Dinero er upåvirket.
Rapportering af sårbarheder
Har du fundet en sårbarhed, så skriv til support@invoia.io med ordet Sårbarhed i emnefeltet. Beskriv hvad du fandt, hvordan det gengives trin for trin, og hvad du vurderer konsekvensen er. Vedhæft gerne en forespørgsel, et log-uddrag eller et skærmbillede.
Vi beder om koordineret offentliggørelse: giv os rimelig tid til at udbedre, før du offentliggør noget. Til gengæld giver vi ikke tilsagn om en svartid eller en udbedringsfrist. Vi har intet, der måler den, og et tal uden måling er ingenting værd.
Omfattet: invoia.io og selve applikationen, det offentlige API på /api/v1, MCP-serveren på /api/mcp og de tilhørende OAuth-endepunkter.
Ikke omfattet: vores leverandørers egne systemer, som skal rapporteres til leverandøren selv; overbelastningsangreb og belastningstest; social manipulation af medarbejdere eller kunder; fysisk adgang; rapporter, der alene består af output fra en scanner uden påvist konsekvens; og fund, der forudsætter adgang til offerets egen enhed eller mailkonto.
Test kun mod din egen konto og dine egne data. Hent ikke data ud, der tilhører andre, og ændr eller slet ikke noget, du ikke selv har oprettet.
Det, vi ikke har
invoia har ingen SOC 2-rapport og ingen ISO 27001-certificering. Vi bestiller ikke årlige penetrationstest. Vi driver ikke et bug bounty-program og udbetaler ikke dusører. Vi driver ingen døgnovervågning og ingen statusside.
Det står her, fordi alternativet ville være opdigtet: et badge, en revisionsdato, en certificering "på vej". Denne side beskriver, hvad der er bygget — ikke hvad der er attesteret. Beskrivelserne af, hvordan produktet er bygget, svarer til kode, der findes i det i dag. Sletning, tilbagekaldelse af en OAuth-adgang og behandlingen af en sårbarhedsrapport er manuelle processer uden kode bag sig — det står, hvor det gælder. Ændrer koden sig, ændrer siden sig.
invoia drives af Creo Digital, CVR DK45246132. Spørgsmål til denne side: support@invoia.io.
Sprog
Denne side er skrevet på dansk. Den engelske udgave er en oversættelse til orientering. Er der uoverensstemmelse mellem de to udgaver, er den danske udgave gældende.