Lalabase
Tickets, Doku, Zeiterfassung und Kundengespräche an einem Ort, durchsuchbar per KI. Für Softwarefirmen mit Kundenprojekten. Open Source: auf deinem eigenen Server oder in unserer Cloud in Deutschland.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Multi-tenant SaaS-platform voor Digital Product Passports (DPP) in de EU. Bedrijven beheren hun producten, documenten en QR-codes in een eigen, volledig geïsoleerde omgeving; consumenten scannen een QR-code en zien het publieke productpaspoort zonder account. Systemen kunnen hetzelfde paspoort op dezelfde URL machineleesbaar opvragen (JSON, JSON-LD, XML).
Laatst bijgewerkt: 2026-10-21 (avond). Status: live. Applicatie en database op een eigen Linux-server (STRATO, Duitsland), bestanden en QR-rand bij Cloudflare (EU), derde back-up bij Hetzner (Finland). Bewaking met pushmeldingen en een geteste noodherstelprocedure: zie §7.
Domeinen (DNS bij Cloudflare):
| Adres | Rol |
|---|---|
| https://www.veripasso.com (en veripasso.com) | landingspagina |
| https://app.veripasso.com | de SaaS-applicatie (inloggen, dashboards, beheer) |
| https://qr.veripasso.com | QR-scans en publieke productpaspoorten (permanent: staat op elke gedrukte code) |
Documentatie:
| Onderwerp | Document |
|---|---|
| Alle functies, in gewone taal | docs/functies.md |
| Wat de eigenaar nog moet doen | docs/checklist-eigenaar.md |
| Hoe alles in elkaar zit | docs/infrastructuur.md |
| Bij storingen | docs/noodplan.md |
| Serverbeheer (STRATO) | docs/hosting-strato.md |
| Cloudflare vóór qr.veripasso.com | docs/cloudflare.md |
| DPP-datamodel en B2B-API | docs/dpp-datamodel.md, docs/api-v1.md |
| Back-up-dienstverlener en EU-register | docs/eu-register-en-backup.md |
| Rollen, accounts en QR-scans | docs/accounts.md, docs/scans.md |
| Wet en normen | docs/compliance/wettelijke-matrix.md, normmatrix |
| Juridische documenten (concepten) | docs/juridisch/ |
| Tests en hersteltest | docs/verification/ |
VeriPasso wordt op twee manieren verkocht:
| Rol | UI-label | Kort |
|---|---|---|
platform_owner | Platform Owner | Precies één account (geseed). Ziet en beheert alles. Voor alle anderen onzichtbaar (API geeft 404, geen 403). |
partner_admin | Partner Admin | Alleen door de owner toekenbaar, alleen op een bedrijf met kind='partner'. Werkt in /partner. |
company_admin | Bedrijfsbeheerder | Beheert het eigen bedrijf: medewerkers, producten, documenten, export. |
company_user | Medewerker | Werkt aan producten/documenten/QR van het eigen bedrijf. |
Aanvullende regels (in code én database): soft delete van gebruikers
(active/blocked/suspended/archived/deleted); per bedrijf altijd ≥ 1 actieve
Bedrijfsbeheerder; company-rollen niet op een partnerbedrijf en andersom;
impersonatie ("Inloggen als") max. 1 uur, nooit genest, volledig in de auditlog.
Licenties: src/services/license.service.js is de enige bron van waarheid. Een
verlopen licentie blokkeert alleen aanmaken; bestaande data en publieke paspoorten
blijven werken (ook verplicht vanuit DPP-persistentie, zie §3).
dbo.users; een geslaagde login geeft een eigen sessie (httpOnly-cookie,
SameSite=Lax, alleen de sha256-hash van het token in dbo.sessions).MFA_ENFORCE_ADMINS=false): zonder MFA kan
een beheerder na inloggen alleen MFA instellen (403 MFA_SETUP_REQUIRED) en niet uitzetten.
Optioneel voor medewerkers. "Onthoud deze computer 30 dagen": eigen cookie (vp_trusted,
httpOnly, SameSite=Strict, alleen /api/auth, alleen SHA-256 in dbo.trusteddevices) slaat de
code over, nooit het wachtwoord; vervalt bij wachtwoordwijziging/-reset, MFA-reset en via
"Alle computers vergeten" in het profiel. Instellen via het eigen profiel (QR-code + eerste code + 10 eenmalige
herstelcodes). Na het wachtwoord geeft de API alleen een kortlevend ondertekend
ticket; pas met een geldige code ontstaat een sessie. Codes zijn eenmalig
(replaybescherming), geheimen staan AES-256-GCM-versleuteld in de database.
Beheerders kunnen MFA van een gebruiker resetten (POST /api/users/:id/mfa/reset).POST /api/products/:id/{photo|documents}/upload-url: rol, tenant, type en grootte
worden gecontroleerd; de server geeft een presigned PUT voor precies één object
(products/{productId}/{uuid}.{ext}); type en exacte grootte zitten in de handtekening,
dus de opslag dwingt ze af (werkt ook met Cloudflare R2, dat geen presigned POST kent);POST .../complete: de server controleert het object (HeadObject) en koppelt het.{QR_BASE_URL}/p/{PUBLIC_ID} met QR_BASE_URL=https://qr.veripasso.com.
De GUID staat in hoofdletters (zo staat hij op de gedrukte codes); opzoeken werkt
hoofdletterongevoelig. In de app heeft een product één QR-code (het model). Batch- en
exemplaarpaspoorten (eigen permanente id per productieronde of serienummer) bestaan technisch
nog, maar het tabblad is op 2026-10-25 uit de app gehaald (te ingewikkeld) en de testdata is
verwijderd; aanmaken kan alleen nog via de API. Vrije ruimte 4 modules (ISO/IEC 18004); ook Data Matrix (ISO/IEC 16022) en
GS1 Digital Link (qr.veripasso.com/01/{GTIN}/10/{lot}/21/{serie}, met resolver).
qr.veripasso.com nooit weghalen of laten verlopen.PASSPORT_PERSISTENCE); een gearchiveerd concept met
gereserveerde QR wordt nooit openbaar.dbo.passportversions (SHA-256 van de
inhoud + hash-keten; UPDATE/DELETE/TRUNCATE geweigerd door een trigger). Opvragen:
GET /api/dpp/:publicId/versions,
, ;Menu's verbergen is UI-comfort; élk endpoint dwingt de rol ook server-side af.
/admin/systeemstatus: request-telemetrie in-process (alleen
tellers en duur, routes genormaliseerd), elke ~5 minuten per serverproces weggeschreven naar
SystemRequestMetricsHourly; dagelijkse snapshot (DB-grootte, tabellen, opslag per
bucket/extensie) en opschonen van oude telemetrie.instrumentation.js →
src/monitoring/scheduler.js). Het dagelijkse onderhoud gebruikt een lease-lock in
dbo.joblocks, zodat er altijd precies één proces het doet. Het onderhoud vult ook
ontbrekende paspoortversies aan.POST /api/scan/:id), zodat scans ook kloppen als Cloudflare de pagina uit de cache
serveert; de Worker ontdubbelt en bundelt (docs/scans.md, docs/cloudflare.md).GET /api/health (liveness, alleen OK) en GET /api/health/ready
(database bereikbaar). Gedetailleerd: owner-only /api/admin/system/health.src/utils/logger.js); gevoelige velden worden altijd
gemaskeerd.npm ci.npm run db:local (echte PostgreSQL 17,
zonder installatie; data in .localdb/, poort 5433). Nooit een productiedatabase..env in de projectroot (wordt nooit gecommit; zie .env.example):
NODE_ENV=development
DATABASE_URL=postgresql://postgres:[email protected]:5433/veripasso
COOKIE_SECRET=<lange random string>
APP_BASE_URL=http://localhost:3000
QR_BASE_URL=http://localhost:3000
# Bestanden (optioneel): S3-compatibele opslag, bijv. MinIO met S3_ENDPOINT,
# S3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY, de twee buckets en S3_FORCE_PATH_STYLE=true.
# Zonder deze waarden werkt alles behalve foto-/documentuploads.
npm run test:db → npm run migrate → npm run seed:owner (met SYSTEM_OWNER_*,
daarna weer weghalen) → npm run dev (http://localhost:3000).Lokaal is er geen domeinverdeling: alle pagina's (landingspagina, app, paspoorten) staan
op localhost:3000.
Volledig overzicht: docs/infrastructuur.md · bij storingen: docs/noodplan.md · serverbeheer: docs/hosting-strato.md.
Scanner / extern systeem ─► Cloudflare (DNS · QR-rand: cache + noodkopie · R2-bestanden, EU)
Gebruiker (browser) ──────► STRATO-server (Docker: Caddy HTTPS → app → PostgreSQL 17)
│ elke nacht, versleuteld
▼
server · Cloudflare R2 · Hetzner Storage Box (+ snapshots)
| Onderdeel | Hoe het werkt |
|---|---|
| App en database | Eén Linux-server met Docker (app, PostgreSQL 17, Caddy voor HTTPS). Alleen poort 22/80/443 open, inloggen alleen met sleutel. Uitrollen met deploy/update.sh (maakt eerst een back-up); migraties draaien automatisch. |
| Bestanden | Foto's en documenten in Cloudflare R2 (EU), privé; downloads via de app met toegangscontrole. |
| QR-codes | qr.veripasso.com loopt via een Cloudflare Worker: paspoortpagina in de cache, en bij serveruitval een noodkopie (ook voor batch- en itemcodes en machineleesbare JSON). Getest: QR-codes blijven werken als de server plat ligt. |
| Beveiliging | TLS 1.2+ op alle domeinen, HSTS, rollen en bedrijfsscheiding server-side (getest), MFA beschikbaar, rate limits, auditlog. |
| Back-ups | Elke nacht: database versleuteld naar de server, R2 en een Hetzner Storage Box; daar ook de configuratie en alle bestanden, versleuteld, plus dagelijkse snapshots die vanaf de server niet te wissen zijn. De back-up gebruikt een beperkte sleutel. Een server die na een noodherstel niet meer de actieve is, uploadt geen back-ups meer. |
| Bewaking | Cloudflare controleert elke 5 minuten app, database, website, noodkopieën en capaciteit (los van de server); het back-upscript meldt elke fout. Pushmeldingen naar de telefoon; Systeemstatus in de app. |
| Noodherstel | In stappen, niets automatisch: diagnose → STRATO herstarten → (alleen als het echt nodig is) nieuwe server bij Hetzner Cloud met alle gegevens uit de back-up → DNS omzetten. Via scripts/noodherstel.mjs of het noodpakket (ZIP met klikbestanden, buiten git bewaard). Hersteltest 2026-10-19: alles terug in ca. 3 minuten, alle gegevens gelijk aan live (verslag). |
| Code en CI | GitHub; elke push: tests op PostgreSQL 17, build, beveiligingscontrole, container-rooktest. Reservekopie van de code op de Storage Box. |
Geheimen (wachtwoorden, sleutels, tokens) staan nooit in de repository, alleen op de server
(deploy/.env, rechten 600), in het beveiligde gegevensdocument en het noodpakket.
Valkuilen: QR_BASE_URL en de DNS van qr.veripasso.com nooit wijzigen of verwijderen (staan op
elke gedrukte code); migraties altijd additief.
app/ Next.js App Router (admin, partner, company, login, p/[id] = publiek paspoort)
components/, lib/ UI-componenten en frontend-helpers (lib/api.js incl. presigned upload)
pages/api/[[...path]].js Eén API-route die alle /api/*-verzoeken aan Express geeft
instrumentation.js Start achtergrondtaken; nette afsluiting bij SIGTERM
proxy.js Domeinverdeling (www / app / qr)
src/
app.js Express: health/readiness, telemetrie, routes
config/ db (pg + TLS), storage (S3-compatibel), monitoring, zod-NL
middleware/ auth, rateLimit, validate, errorHandler
monitoring/ requestMetrics, collectors, health, scheduler
repositories/ SQL per entiteit
routes/ o.a. products (incl. versies, export), dpp (machineleesbaar + versies),
publicProducts, auth (incl. MFA), users, partner, admin, systemMonitoring
services/ passport (inhoud + JSON/JSON-LD/XML), passportArchive (versies, hash-keten),
mfa (TOTP), blobStorage (S3-compatibel), edgeCache (Cloudflare),
license, onboarding, scanPolicy, qrCode
utils/ logger, roles, tenant, auditLog, password, baseUrl
db/migrations/ PostgreSQL-migraties (oplopend; bijgehouden in dbo.schemamigrations)
deploy/ Serveropzet: compose.yaml (app + PostgreSQL + Caddy), Caddyfile, .env.example,
backup.sh (nachtelijke back-up), update.sh (uitrollen), recover.sh (herstel);
Dockerfile in de root
edge/cloudflare/ Cloudflare Worker voor qr.veripasso.com (cache, noodkopie, scans, bewaking)
scripts/ migrate, seed-system-owner, backfill-passport-versions, local-db,
check-db-connection, noodherstel (noodherstel in stappen),
maak-noodpakket (ZIP met klikbestanden)
tests/ node --test (zie §9)
docs/ zie de tabel bovenaan
npm test: volledige suite tegen de database uit DATABASE_URL, altijd een
testdatabase (de fixtures ruimen hun eigen data op; paspoortversies worden in tests
als superuser verwijderd omdat de tabel append-only is). In CI draait de suite tegen een
tijdelijke PostgreSQL 17-service.TEST_S3_LIVE=true en test-buckets;
de S3-logica zelf is altijd getest met een nep-client (tests/s3-storage.test.js).tests/passport-compliance.test.js (ID's verwijzen naar de matrix).Afvinklijst voor de eigenaar (wat jij nog moet doen of beslissen): docs/checklist-eigenaar.md. Hieronder het volledige overzicht.
aws-laatste-versie)./api/v1 met API-sleutels./api/dpp/:publicId/versions/:n/api/dpp/:publicId?at=<ISO-tijdstip>GET /api/products/:id/versions,
/versions/:n, /versions/verify (ketencontrole).GET /api/dpp/:publicId met
content negotiation (application/json, application/ld+json, application/xml) of
?format=json|jsonld|xml; een browser wordt naar de HTML-pagina gestuurd. Ook de
gedrukte URL /p/:publicId levert JSON/JSON-LD/XML als het systeem daarom vraagt.GET /api/products/passports/export (Company Admin; owner met ?companyId=) levert
NDJSON met alle paspoorten, de actuele stand en alle versies incl. hashes.digitalProductPassportId, uniqueProductIdentifier,
granularity, dppSchemaVersion, dppStatus, lastUpdated, economicOperatorId (LEI/GLN/
KvK/btw, per bedrijf), facilityId, contentSpecificationIds, meertalige naam/omschrijving,
dataElements met dictionaryReference, relatedResources. Zie docs/dpp-datamodel.md./api/v1 met API-sleutels per bedrijf (ReadDPPById, ReadDPPByProductId,
ReadDPPIdsByProductIds, CreateDPP, UpdateDPPById, DeleteDPPById, Read/UpdateDataElement,
RegisterProductDPP); OpenAPI op /api/v1/openapi.json. Zie docs/api-v1.md.uniqueProductIdentifier is altijd een persistente https-URL die naar het paspoort
leidt (GS1 Digital Link op qr.veripasso.com met de GTIN erin, anders de paspoort-URL).vpa_…) die het bedrijf uitgeeft; per document Zichtbaar voor. Antwoorden
met een token worden nooit gedeeld gecachet. Zie docs/dpp-datamodel.md.