Javis OS
Javis OS는 기업가를 돕는 에이전시 AI로, 업무를 지원하고 함께 성장합니다
스웨덴 제조용 오픈소스 ERP로 재고·구매·판매·제조·원가·회계가 하나에 포함, 무료 자체호스팅
Affärssystem med öppen källkod för svensk tillverkning: lager, inköp, försäljning, tillverkning, kalkyl och bokföring i ett system. Gratis och självhostat.
비즈니스TypeScript · CSSGitHub @ubbe76온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
Affärssystem (ERP) med öppen källkod för mindre och medelstora svenska tillverkande företag (alfa)
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗[!WARNING] Nav|ERP är inte redo för produktion. Systemet är under aktiv utveckling och har inte prövats i skarp drift. Datamodell, API och arbetsflöden kan ändras utan förvarning, och det finns ingen stabil version att uppgradera från. Använd det inte för riktig bokföring, lön, fakturering eller lagerhållning ännu — prova det med exempeldata.
Nav|ERP är ett affärssystem (ERP) med öppen källkod för mindre och medelstora svenska tillverkande företag. Byggt för att täcka kärnflödet artikel → lager → inköp/försäljning → tillverkningsorder → tidrapportering → kalkyl.
Webbplats, funktionsöversikt och användarmanual: naverp.se. Prova utan att installera: demon med ett besökskonto som bara kan läsa.
Se docs/architecture.md för full kravspecifikation,
datamodell och statusflöden, och CLAUDE.md för
utvecklingskonventioner.
Webben har tre inbyggda färgteman — Ljust, Mörkt och Dracula — som väljs uppe till höger och sparas på användaren. Under System → Teman kan administratören skapa egna teman i temaredigeraren eller ladda upp en temafil, välja förvalt tema och låsa valet. Bilderna visar påhittade exempeldata. Klicka på en bild för full storlek.
| Del | Teknik |
|---|---|
| Runtime | Node.js (LTS) + TypeScript (strict) |
| Databas | PostgreSQL 16 |
| ORM | Drizzle ORM |
| API | Fastify (apps/api) |
| Frontend | Next.js App Router (apps/web) |
| Validering | Zod (packages/shared) |
| Containerisering | Docker + Docker Compose |
| Tester | Vitest |
apps/
├── api/ # Fastify REST-API (/api/v1), moduler i routes/service/repository
└── web/ # Next.js-frontend, designtokens + UI-komponenter
packages/
├── db/ # Drizzle-schema, migrationer, delad DB-klient
└── shared/ # Zod-scheman & delade typer
db/
└── schema_phase1.sql # Referensschema (dokumentation, ej källa till sanning)
docs/
├── architecture.md # Full kravspec, datamodell, statusflöden, faser
└── manual/ # Användarmanualen (svenska, ett kapitel per fil) — renderas i appen på /hjalp
Källan till sanning för databasschemat är Drizzle-migrationerna i
packages/db/drizzle/, inte db/schema_phase1.sql.
Det finns två sätt att köra projektet på, beroende på om du utvecklar eller bara vill driftsätta/testa det:
api/web körs direkt med npm run dev:*
för snabbare omstart/felsökning). Det är detta avsnittet nedan beskriver.cp .env.example .env
npm install
Fyll sedan i de två obligatoriska värdena i .env — Docker Compose vägrar
starta något medan de är tomma:
POSTGRES_PASSWORD — för lokal utveckling erp_dev_password, som
matchar npm-skriptens inbyggda standard (porten är bunden till
127.0.0.1 och nås inte från nätverket).JWT_SECRET — openssl rand -hex 32..env läses automatiskt av Docker Compose (för POSTGRES_PASSWORD/
JWT_SECRET/WEB_ORIGIN/NEXT_PUBLIC_API_URL, se docker-compose.yml). De lokala npm-skripten
(dev:api, dev:web, db:migrate) läser inte .env filen själva —
varje modul har istället samma lokala default inbyggd (matchar
docker-compose:s Postgres-uppgifter), så npm install + kommandona nedan
fungerar rakt av utan att exportera något manuellt, så länge du kör mot
standard-Postgres:en nedan. Om du pekar mot en annan databas/hemlighet,
exportera motsvarande variabel i skalet innan du kör kommandot, t.ex.
DATABASE_URL=postgresql://... npm run db:migrate.
docker compose up -d postgres
npm run db:migrate
Det här steget behövs bara i det lokala utvecklingsläget, där api körs på
värdmaskinen med npm run dev:api. Kör du hela stacken i Docker migrerar
api-containern själv vid start (se nedan) och du kan hoppa över det.
npm run dev:api # Fastify på http://localhost:4000
npm run dev:web # Next.js på http://localhost:3000
Vill du köra hela stacken i Docker i stället, t.ex. för att prova en riktig
produktionsbuild lokalt innan du driftsätter den på en server — se
Driftsätta på en annan server
nedan. Den bygger produktionsimages (ingen hot-reload), så för snabb
utveckling används npm run dev:api/npm run dev:web ovan.
Systemet har inga förinstallerade konton. Gå till http://localhost:3000/register
och skapa det första kontot där (blir automatiskt admin) — sidan loggar in
dig direkt efter skapande. Registreringen kräver en engångskod som
API:t skriver i sin logg när det startar och inga användare finns:
docker compose logs api | grep Engångskod # eller titta i terminalen där npm run dev:api körs
Koden gör att den som råkar nå /register först inte kan ta
administratörskontot — bara den som kommer åt serverns logg. En ny kod
skrivs vid varje omstart; vill du bestämma den i förväg (t.ex. i en
skriptad installation), sätt SETUP_CODE i .env. /register stänger sig
själv så fort en användare finns, och fler användare skapas därefter av en
inloggad admin via POST /api/v1/auth/users.
Vill du prova systemet utan egna register: System → Exempeldata skapar ett påhittat företag med artiklar, struktur och operationer, kunder, leverantörer och lagersaldon. Det går bara på en tom installation.
Föredrar du curl framför webbgränssnittet fungerar samma engångsanrop direkt mot API:et:
curl -X POST http://localhost:4000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"ett-tillräckligt-långt-lösenord","name":"Admin","setupCode":"ABCD-EFGH-JKLM"}'
npm run test
Testerna i apps/api kör mot en riktig Postgres-instans (append-only
lagerlogik och BOM-cirkularitet är projektets känsligaste domänlogik och
mockas medvetet inte bort).
Varning: testerna TRUNCATE:ar tabellerna i databasen som
DATABASE_URL pekar på. npm run test kör som standard mot den separata
testcontainern postgres-test (databasen erp_test på port 5433, data i
RAM), aldrig mot utvecklingsdatabasen. Starta den först:
docker compose --profile test up -d postgres-test
Containern töms vid omstart och testkörningen migrerar den själv innan sviten startar, så en ny migration behöver aldrig köras för hand. Kör bara en testkörning åt gången mot den.
GitHub kör samma svit automatiskt vid varje push till main och på varje
pull request (.github/workflows/ci.yml), tillsammans med typkontroll och
bygge av API:t och webben.
Du behöver inte installera PostgreSQL, Node.js eller något annat i förväg
— Docker Compose sköter allt, databasen inkluderad (den körs som postgres-
tjänsten i docker-compose.yml och sparar data i en namngiven volym,
erp_postgres_data, som överlever omstarter). Det enda den nakna servern
faktiskt behöver är Docker själv.
På en färsk Ubuntu/Debian-VPS:
curl -fsSL https://get.docker.com | sh
sudo adduser --disabled-password --gecos "Nav|ERP" naverp
sudo usermod -aG docker naverp
(Andra distributioner: se Docker Engines officiella installationsguide.)
Vilken användare kör Nav|ERP? Ett eget servicekonto, här naverp —
inte root och inte ditt personliga konto. Kontot äger katalogen med koden,
.env och säkerhetskopiorna, och det är som naverp du kör docker compose,
uppdateringar och backupens cron. Administratörer arbetar som kontot med
sudo -iu naverp.
Medlemskap i gruppen docker är i praktiken root-behörighet på servern:
den som får styra Docker kan starta en container som når hela värdens
filsystem. Lägg därför bara servicekontot i gruppen, inga personliga konton.
Vill du isolera ännu mer finns rootless Docker,
där även Docker-tjänsten körs utan root — det är inte provat med Nav|ERP.
Som servicekontot:
sudo -iu naverp
git clone https://github.com/ubbe76/Nav-ERP.git
cd Nav-ERP
cp .env.example .env
chmod 600 .env
chmod 600 gör att bara naverp kan läsa .env — filen innehåller
databasens lösenord och JWT_SECRET, som räcker för att logga in som vem
som helst.
Ändra sedan i .env innan du startar något:
POSTGRES_PASSWORD — databasens lösenord, ett nytt slumpat värde
per installation (openssl rand -hex 24; det hamnar oescapat i en URL,
så håll dig till bokstäver och siffror). Postgres använder det bara när
databasvolymen skapas första gången — se
Uppdatera en befintlig installation.JWT_SECRET — ett nytt slumpat värde (openssl rand -hex 32). Det
signerar både inloggningssessioner och OAuth2-klienttokens, så den som
känner till det kan logga in som vilken användare som helst.Båda är tomma i .env.example, och så länge någon av dem är tom vägrar
docker compose up starta och avbryter direkt med t.ex.:
POSTGRES_PASSWORD måste sättas i .env — se .env.example
COOKIE_SECURE — lämna osatt som standard (följer NODE_ENV, dvs.
Secure i produktionscontainern). Driftsätter du på ett rent HTTP-LAN
utan TLS-terminering framför stacken, sätt COOKIE_SECURE=false —
annars skickar webbläsaren aldrig sessionscookien över okrypterad HTTP
(webbläsarregeln för Secure-cookies), och inloggningen ser ut att
misslyckas helt tyst, utan något felmeddelande.
WEB_ORIGIN — sätt till adressen användarna öppnar i webbläsaren
(t.ex. https://erp.example.se eller http://din-server-ip:3000), inte
localhost. API:et validerar CORS mot den, så fel värde ger inloggning
som blockeras av CORS. Ändringsanrop med en inloggad session som kommer
från en annan webbplats vägras (403), utom från den här adressen.
TRUST_PROXY — lämna tom om webben nås direkt. Inloggningen spärras
ändå i 15 minuter efter 10 misslyckade försök på samma konto (och
OAuth2-tokenanropet efter 10 på samma klient). Står en omvänd proxy som
själv skriver X-Forwarded-For framför webben (nginx, Caddy, Cloudflare
Tunnel) och all trafik går genom den, sätt TRUST_PROXY=1 — då spärras
även en IP-adress efter 30 misslyckade försök över alla konton. Sätt den
aldrig utan en sådan proxy: då kan klienten själv välja vilken adress den
ser ut att komma från, och anrop utan rubriken delar på en och samma
räknare.
NEXT_PUBLIC_API_URL — lämna tom. Webben skickar vidare /api/v1/*
till API-containern över Docker-nätverket, så webbläsaren och externa
integrationer (även OAuth2-tokenanropet) når API:et genom webbens adress.
Sätt den bara om du medvetet exponerar API:et på en egen värd bakom en
omvänd proxy.
INTERNAL_API_URL (redan hårdkodad till http://api:4000 i
docker-compose.yml, inget du behöver sätta) är en separat variabel för
anrop som web-containern gör åt sig själv (serversidan, t.ex. att läsa
inloggad användare) — den går över Docker-nätverket, inte den publika
adressen. Utan den separationen försöker containern nå sin egen värds
publika IP, vilket vanligtvis inte går och visar sig som Error: fetch failed i webbläsaren.
docker compose up -d --build
Det är allt som krävs för att få igång stacken — inget migreringssteg.
Första gången bygger Compose produktionsimages för api och web från
grunden (flerstegs-Dockerfiles, se apps/api/Dockerfile/
apps/web/Dockerfile) — räkna med några minuters byggtid. api-
containerns startkommando (CMD i apps/api/Dockerfile) kör
migrationerna innan servern startar, och depends_on-kedjan väntar på
hälsokontroller: web startar inte förrän api svarar friskt, som i sin
tur väntar på att Postgres är frisk. Drizzle håller reda på vad som redan
körts, så migreringen är en tom operation vid varje omstart efter den
första.
På en tom databas är migrationerna installationen: de skapar schemat och
seedar företagsinformationsraden, nummerserierna och grundenheterna.
Verifierat end-to-end mot en databas med noll tabeller. Misslyckas en
migrering startar containern inte — bättre än att servera mot fel schema.
Felet syns med docker compose logs api.
Gör det direkt efter docker compose up. Se
Skapa första användaren ovan — hämta
engångskoden med docker compose logs api | grep Engångskod, gå till
<din-server>:3000/register, eller kör samma engångs-POST /api/v1/auth/register-
anrop på servern själv mot localhost:4000.
Bara webbens port 3000 behöver nås utifrån — eller hellre en omvänd proxy med HTTPS framför den (då sköter proxyn port 443 och 3000 kan stängas för allt utom proxyn). Webbläsaren och externa integrationer når API:et genom webbens adress.
docker-compose.yml binder Postgres (5432), testdatabasen (5433) och
API:et (4000) till 127.0.0.1, så de nås bara från servern själv — av
npm run dev:*, migreringarna och backupskripten. Ändra inte det till
alla interface: Docker skriver egna iptables-regler som går förbi ufw,
så en port som compose-filen publicerar på 0.0.0.0 är nåbar från
internet även om brandväggen säger att den är stängd.
Stacken är Linux-containrar och går att köra på Windows med Docker Desktop och dess WSL 2-backend. Windows är inte testat i projektet, så se det som ett alternativ för utvärdering eller en mindre installation.
wsl --install).~/Nav-ERP) — inte under C:\. Filsystemet blir mycket snabbare och
skripten körs i rätt miljö..env, docker compose up -d --build, engångskoden och
http://localhost:3000. På en egen Windows-dator kör du som din
WSL-användare — servicekontot från steg 0 behövs inte där.Att känna till:
scripts/*.sh). Kör dem i
WSL-terminalen, inte i PowerShell. Schemalägg den nattliga dumpen med cron
inne i WSL, eller med Windows Schemaläggaren som anropar
wsl bash ~/Nav-ERP/scripts/backup-db.sh..gitattributes tvingar LF i .sh-filerna, så skripten
fungerar även om repot klonas med Git för Windows.127.0.0.1 på samma sätt som på Linux — se Brandvägg.git pull
docker compose up -d --build
Nya migrationer körs automatiskt när api startar — inget separat steg.
Säkerhetsuppdateringar av Node.js, Alpine och Postgres kommer inte med
apt upgrade på värden — de finns i basimagerna. Bygg därför om med
--pull med jämna mellanrum (t.ex. en gång i månaden), även när koden inte
har ändrats:
docker compose pull postgres
docker compose build --pull
docker compose up -d
api och web körs som den oprivilegierade användaren node i
containrarna, utan Linux-capabilities och med no-new-privileges; api
har dessutom ett skrivskyddat filsystem. Ge bara administratörer
medlemskap i gruppen docker på servern — det är i praktiken root-behörighet.
Uppgraderar du från en version före 2026-10-14 stod databasens lösenord
erp_dev_password fast i docker-compose.yml. Nu läses det från .env,
och Postgres använder POSTGRES_PASSWORD bara när volymen skapas — en
befintlig databas behåller sitt gamla lösenord. Gör därför ett av följande
innan docker compose up:
behåll lösenordet: lägg till POSTGRES_PASSWORD=erp_dev_password i .env
(porten är numera bunden till 127.0.0.1), eller
byt lösenordet i databasen och skriv samma värde i .env:
docker compose exec postgres psql -U erp -d erp -c "ALTER USER erp PASSWORD 'nytt-lösenord'"
Byt du lösenordet behöver npm run dev:api/db:migrate på servern
DATABASE_URL med det nya lösenordet.
Har din .env raden NODE_ENV=development (den stod i äldre versioner av
.env.example), ta bort den — annars körs containrarna i utvecklingsläge
och sessionskakan blir inte Secure ens bakom HTTPS.
Inget docker compose down behövs längre: stacken har varken
källkods-mountar eller anonyma node_modules-volymer kvar, så
--build ensamt bygger färska images med färsk kod och färska beroenden
och ersätter containrarna med dem.
Eftersom all data — även uppladdade ritningar, underlag, arkiverade fakturor och företagslogotypen — ligger i Postgres räcker en databasdump för att flytta hela ERP:en.
På den gamla servern — ta en färsk säkerhetskopia, antingen i appen
(System → Säkerhetskopiering → Ladda ner säkerhetskopia) eller på servern
(scripts/backup-db.sh, dumpen hamnar i ~/backups/nav-erp/). Kopiera
.sql.gz-filen till den nya servern.
På den nya servern — installera Docker och skapa servicekontot (se
steg 0), sätt upp stacken
som naverp och återställ dumpen:
git clone https://github.com/ubbe76/Nav-ERP.git && cd Nav-ERP
cp .env.example .env && chmod 600 .env # sätt POSTGRES_PASSWORD, JWT_SECRET, WEB_ORIGIN
docker compose up -d --build
scripts/restore-db.sh erp-2026-07-20_0230.sql.gz
restore-db.sh läser in både schema och data ur dumpen, så du behöver inte köra
migrate separat. Att api-containern redan migrerat den tomma databasen vid
start gör ingen skada — skriptet rensar både public- och drizzle-schemat
innan det läser in dumpen, så det den skapade ersätts i sin helhet. Observera att ett nytt
JWT_SECRET gör gamla inloggningssessioner och OAuth2-klienttokens ogiltiga —
användare loggar in igen och konfiguratorns klienttokens hämtas på nytt.
restart: unless-stopped i docker-compose.yml startar om containrar som
kraschar. Volymen erp_postgres_data säkerhetskopieras inte i sig — använd
scripts/backup-db.sh via cron för nattliga
pg_dump-dumpar (30 dagars historik som standard). Lägg raden i
servicekontots crontab (crontab -e som naverp), så hamnar dumparna i
/home/naverp/backups/nav-erp/:
30 2 * * * /home/naverp/Nav-ERP/scripts/backup-db.sh >> /home/naverp/backups/nav-erp/backup.log 2>&1
Återställ med
gunzip -c dump.sql.gz | docker compose exec -T postgres psql -U erp -d erp.
De nattliga dumparna ligger på samma disk som databasen. Bokföringslagen kräver att räkenskapsinformationen bevaras på ett betryggande sätt i sju år, så sätt också upp den krypterade externa säkerhetskopian (gpg + valfritt rclone-mål, med årsarkiv som innehåller räkenskapsårets SIE-fil):
scripts/offsite-backup-setup.sh
Se manualens kapitel Drift & utveckling för detaljer, återställning och krav på förvaringsort.
Version 0.6.1-alpha — inte redo för produktion, se varningen överst. En alfaversion är till för den som vill prova systemet och ge återkoppling. Funktionerna nedan är byggda och testade, men systemet har ännu inte använts i skarp drift.
Backend (Fas 1–5 enligt byggordningen) är klart, testat och kör: Article/BOM,
Stock, grundläggande auth (sessionscookie), Purchase, Sales, Manufacturing,
TimeReport (inkl. stämpling/närvaro), Payroll (frånvaro, övertid/OB, tidattest
och löneunderlag/PAXml-export), Costing, Economy (grundbokföring,
automatkontering, kund-/leverantörsreskontra, moms och ekonomirapporter),
Import (CSV-/Excel-import av register, med nedladdningsbara Excelmallar och
förklaring per kolumn), System, Mail (SMTP-utskick +
PDF-generering) samt ett publikt OAuth2 client-credentials-API för en extern
konfigurator. Frontend har in-/utloggning
(inkl. engångsregistrering av första admin-kontot på /register), en
Översikt-dashboard byggd kring materialflödet (inköp → lager → tillverkning →
försäljning) med bevakningslista, sidan Aviseringar (negativa saldon,
försenade ordrar, förfallna fakturor, offerter att följa upp m.fl., med
valfri daglig sammanställning per e-post), sidomeny med undermenyer per modul,
modulhubbar med grupperade listkort, och modul-UI för hela byggordningen.
Alla registerlistor har serversidig fritextsökning, filtrering och
paginering (?search/filter/limit/offset → { rows, total }), så
registren skalar till tusentals poster; artikelval i order- och
BOM-formulär görs med en sökbar artikelväljare i stället för dropdowns.
Alla register och strukturer är fullt redigerbara i efterhand:
Se
docs/architecture.md för
full byggordning och TODO.md för det som återstår.
Nav|ERP är en alfaversion, och återkoppling från dig som provar är det som hjälper mest:
Copyright © 2026 Urban Källefors
nav | ERP är fri programvara: du kan sprida den och/eller ändra den under
villkoren i GNU General Public License, publicerad av Free Software
Foundation, antingen version 3 av licensen eller (efter ditt val) valfri
senare version. Programmet sprids i hopp om att det ska vara användbart,
men UTAN NÅGON GARANTI; inte ens underförstådd garanti om SÄLJBARHET eller
LÄMPLIGHET FÖR ETT VISST ÄNDAMÅL. Se LICENSE för den
fullständiga licenstexten (SPDX: GPL-3.0-or-later).
licenses/Dracula-MIT.txt.Projektet startades som ett scaffolding-paket avsett att bootstrap:as med
Claude Code CLI — se
docs/claude-code-getting-started.md
för den ursprungliga onboarding-guiden.
*** (transaktioner utan vald plats bokförs
på artikelns lagerplats eller ***), transaktionslogg (inflödesrader
bär inköpskostnaden unit_cost i SEK), inventering samt
lagervärdering (/lager/vardering, administratörer): lagrets värde
per valfritt datum med tre metoder — standardpris, senaste inköp eller
vägt snitt — uppdelat per varugrupp eller artikelkod och nedladdningsbart som PDF/hjalp (renderad från
docs/manual/ och publicerad på
naverp.se/hjalp) med kapitel för alla rutiner
och inställningar och sökning i alla kapitel