Overzicht & reikwijdteOverview & scope
JcoLabs is gevestigd in Nijverdal (Nederland). Wij bouwen KPI-dashboards, automatiseringen en AI-workflows voor klanten en werken met een eigen beheerportaal en een apart klantportaal per klant.
Onze uitgangspunten:
- Minimale toegang: iedere gebruiker, dienst en koppeling krijgt alleen de rechten die nodig zijn.
- Gescheiden per klant: de gegevens van klanten staan niet in één gedeelde omgeving (zie Scheiding van klantgegevens).
- Meerdere lagen: wij vertrouwen niet op één maatregel; inloggen, sessies, netwerk en database beschermen elk afzonderlijk.
- Eerlijk: op deze pagina staat alleen wat wij daadwerkelijk doen. Wat nog in ontwikkeling is, staat onder Verbeteringen in uitvoering.
Deze pagina beschrijft het JcoLabs-beheerportaal en de klantportalen. De Tools Store en de AI Arcade zijn aparte diensten met eigen databases. JcoLabs heeft op dit moment geen formele certificering (zoals ISO 27001 of SOC 2). Deze pagina wordt bijgehouden zodat zij als basis kan dienen voor een onafhankelijke beoordeling.
JcoLabs is based in Nijverdal, the Netherlands. We build KPI dashboards, automations and AI workflows for clients, using our own internal portal and a separate client portal per client.
Our principles:
- Least access: every user, service and integration only gets the rights it needs.
- Separated per client: client data does not live in one shared environment (see Client data separation).
- Several layers: we do not rely on a single measure; sign-in, sessions, network and database each protect independently.
- Honest: this page only states what we actually do. What is still being built is listed under Improvements in progress.
This page covers the JcoLabs internal portal and the client portals. The Tools Store and the AI Arcade are separate services with their own databases. JcoLabs currently holds no formal certification (such as ISO 27001 or SOC 2). This page is maintained so that it can serve as the basis for an independent assessment.
Toegang & authenticatieAccess & authentication
- Wachtwoorden worden nooit leesbaar opgeslagen. Alleen een gezouten, eenrichtings-hash (via Werkzeug) wordt bewaard.
- Tweestapsverificatie (TOTP) is verplicht voor medewerkers van JcoLabs. Zonder ingestelde tweede factor kan een medewerker niet inloggen. Klantportalen werken eveneens met wachtwoord plus TOTP.
- Kortdurende sessies: een sessie verloopt na 10 minuten inactiviteit. Sessiecookies zijn alleen via HTTPS verstuurbaar (
Secure), niet leesbaar voor scripts (HttpOnly) en beperkt tot dezelfde site (SameSite=Lax). Pagina's voor ingelogde gebruikers worden niet door de browser in de cache bewaard. - Bescherming tegen raden: herhaalde mislukte pogingen leiden tot tijdelijke, steeds langere blokkades. Foutmeldingen verraden niet of een gebruikersnaam bestaat.
- Apparaat onthouden is optioneel. Daarvoor bewaren wij alleen een hash van een willekeurige sleutel; apparaten kunnen worden ingetrokken. Een onthouden apparaat verloopt na 3 dagen; alleen vooraf vertrouwde netwerken kunnen daarvan afwijken.
- CSRF-bescherming: wijzigende acties vereisen een persoonlijk, willekeurig token dat bij elke sessie anders is.
- Beheerfuncties zijn beperkt: gevoelige beheerpagina's (accounts, Tools Store, AI Arcade) zijn alleen toegankelijk voor een kleine groep superbeheerders.
- Passwords are never stored in readable form. Only a salted, one-way hash (via Werkzeug) is kept.
- Two-factor authentication (TOTP) is mandatory for JcoLabs staff. Staff without a configured second factor cannot sign in. Client portals also use password plus TOTP.
- Short-lived sessions: a session expires after 10 minutes of inactivity. Session cookies are only sent over HTTPS (
Secure), cannot be read by scripts (HttpOnly) and are restricted to the same site (SameSite=Lax). Pages for signed-in users are not cached by the browser. - Protection against guessing: repeated failed attempts trigger temporary, increasingly long lockouts. Error messages do not reveal whether a username exists.
- Remembering a device is optional. We then only store a hash of a random key; devices can be revoked. A remembered device expires after 3 days; only pre-approved networks can differ from this.
- CSRF protection: state-changing actions require a personal, random token that is different for every session.
- Administrative functions are restricted: sensitive admin pages (accounts, Tools Store, AI Arcade) are only available to a small group of super administrators.
Scheiding van klantgegevensClient data separation
Iedere klant krijgt een eigen omgeving. Er is dus geen gedeelde klantendatabase waarin een fout in de ene omgeving de gegevens van een andere klant kan raken.
- Eigen subdomein, eigen serverproces, eigen database met eigen databasegebruiker, eigen map en eigen configuratiebestand per klant.
- Een klantportaal heeft geen toegangsgegevens tot onze centrale database.
- Rechten gelden per project (rol per gebruiker per project). Er is geen algemene beheerder die binnen een klantportaal automatisch overal bij kan.
- Onze centrale omgeving praat met een klantportaal via een aparte databaserol die alleen een vaste lijst tabellen mag lezen en wijzigen en geen databasestructuur mag aanpassen.
- Meldingen van een klantportaal naar het beheerportaal zijn ondertekend (HMAC) met een geheim dat per klant verschilt, worden met een constante-tijdvergelijking gecontroleerd en zijn begrensd in frequentie.
- Klantportalen bevatten nooit onze Stripe-sleutel; betalingen lopen via het beheerportaal.
Every client gets its own environment. There is no shared client database in which a flaw in one environment could touch another client's data.
- Own subdomain, own server process, own database with its own database user, own directory and own configuration file per client.
- A client portal has no credentials for our central database.
- Rights apply per project (a role per user per project). There is no global administrator who automatically has access everywhere inside a client portal.
- Our central environment talks to a client portal through a separate database role that may only read and change a fixed list of tables and cannot alter the database structure.
- Messages from a client portal to the internal portal are signed (HMAC) with a secret that differs per client, are verified with a constant-time comparison and are rate limited.
- Client portals never hold our Stripe key; payments go through the internal portal.
Wachtwoorden & sleutelsCredentials & secrets
- Geen geheimen in de broncode. Sleutels en wachtwoorden staan in configuratiebestanden buiten de repository, met bestandsrechten die alleen de betreffende dienst toelaten.
- Geheimen die de server zelf aanmaakt (zoals databasewachtwoorden en ondertekeningssleutels) zijn willekeurig gegenereerd en worden bij databasebeheer niet op de opdrachtregel meegegeven.
- API-sleutels van externe diensten worden uitsluitend op de server gebruikt en nooit naar de browser gestuurd.
- Wachtwoordbeheer voor het team: teamwachtwoorden staan in een zelf gehoste, Bitwarden-compatibele kluis (Vaultwarden). Zelf registreren is uitgeschakeld (alleen op uitnodiging), de kluis is alleen via HTTPS bereikbaar en de inhoud is end-to-end versleuteld.
- Bij het beëindigen van een klantrelatie worden de bijbehorende toegangsgegevens verwijderd (offboarding-procedure).
- No secrets in source code. Keys and passwords live in configuration files outside the repository, with file permissions that only allow the service concerned.
- Secrets that the server generates itself (such as database passwords and signing keys) are random and are not passed on the command line during database administration.
- API keys of external services are only used on the server and never sent to the browser.
- Team password management: team passwords are kept in a self-hosted, Bitwarden-compatible vault (Vaultwarden). Self-registration is disabled (invitation only), the vault is only reachable over HTTPS and its content is end-to-end encrypted.
- When a client relationship ends, the related credentials are removed (offboarding procedure).
Netwerk & versleuteld transportNetwork & transport
- Verkeer loopt via Cloudflare (DNS en bescherming tegen overbelasting) naar onze webserver (Nginx) en daarna naar de applicatie.
- Versleuteld via HTTPS. Niet-versleutelde verzoeken worden doorgestuurd naar HTTPS. Klantportalen accepteren alleen TLS 1.2 en 1.3.
- Klantportalen sturen beveiligingsheaders mee:
X-Frame-Options: DENY,X-Content-Type-Options: nosniff, een strikteReferrer-Policyen een Content-Security-Policy. - Foutpagina's tonen geen interne details.
- Traffic passes through Cloudflare (DNS and protection against overload) to our web server (Nginx) and then to the application.
- Encrypted over HTTPS. Unencrypted requests are redirected to HTTPS. Client portals only accept TLS 1.2 and 1.3.
- Client portals send security headers:
X-Frame-Options: DENY,X-Content-Type-Options: nosniff, a strictReferrer-Policyand a Content-Security-Policy. - Error pages do not reveal internal details.
DatabasesDatabases
- Wij gebruiken PostgreSQL. De applicatie verbindt met een beperkte databasegebruiker die geen databasestructuur mag wijzigen; structuurwijzigingen doet een beheerder bewust, apart.
- Queries gebruiken parameters (geen samengestelde SQL met gebruikersinvoer), als bescherming tegen SQL-injectie.
- Per klant een eigen database (zie Scheiding van klantgegevens).
- Voor de Tools Store, de AI Arcade en het accountsysteem gebruikt het beheerportaal aparte, beperkte rollen. Voor accounts zijn daarin geen wachtwoord-hashes of tokens leesbaar.
- We use PostgreSQL. The application connects with a limited database user that may not change the database structure; structural changes are made deliberately and separately by an administrator.
- Queries use parameters (no SQL built from user input), as protection against SQL injection.
- One database per client (see Client data separation).
- For the Tools Store, the AI Arcade and the accounts system, the internal portal uses separate, limited roles. For accounts, password hashes and tokens are not readable through them.
Documenten & uploadsDocuments & uploads
- Uploads zijn alleen mogelijk voor ingelogde gebruikers en vereisen een CSRF-token. Bij bonnetjes is het aantal uploads per minuut begrensd.
- Het bestandstype wordt gecontroleerd op de inhoud van het bestand, niet op de bestandsnaam. Bonnetjes: PNG, JPEG, WebP, HEIC en PDF, maximaal 10 MB. Klantportalen: een vaste lijst toegestane bestandstypen met een maximum van 50 MB.
- Bestanden krijgen een door ons gegenereerde bestandsnaam; de naam die de gebruiker opgeeft wordt niet als opslagnaam gebruikt.
- Bonnetjes blijven niet op onze server staan: ze worden naar de Google Drive van JcoLabs gestuurd en direct uit het geheugen verwijderd. Uploads in klantportalen worden buiten de publiek bereikbare map opgeslagen.
- Projectmappen in Google Drive worden alleen gedeeld met het e-mailadres van de betreffende klant.
- Uploads are only possible for signed-in users and require a CSRF token. For receipts the number of uploads per minute is limited.
- File types are checked on the file content, not on the file name. Receipts: PNG, JPEG, WebP, HEIC and PDF, up to 10 MB. Client portals: a fixed list of allowed file types with a 50 MB maximum.
- Files get a name generated by us; the name supplied by the user is not used as storage name.
- Receipts are not kept on our server: they are sent to JcoLabs' Google Drive and removed from memory straight away. Uploads in client portals are stored outside the publicly reachable directory.
- Project folders in Google Drive are only shared with the email address of the client concerned.
ContactaanvragenContact enquiries
- Het formulier op jcolabs.com vraagt naam, bedrijf, e-mailadres en een korte omschrijving van uw vraag. Verzenden kan alleen na uw toestemming.
- De aanvraag gaat per e-mail (via Gmail) naar de JcoLabs-mailbox en wordt daar als gewone e-mail bewaard. Wij slaan de aanvraag niet op in een database van ons portaal.
- Onze applicatie logt niet wat u invult: ons applicatielogboek bevat alleen dat een aanvraag is verstuurd of geweigerd, met de reden.
- Tegen misbruik gelden een begrenzing per IP-adres en een algemene begrenzing op het totaal aantal aanvragen (beide alleen in het geheugen van de server, niet opgeslagen), een verborgen controleveld en een minimale invultijd.
- The form on jcolabs.com asks for your name, company, email address and a short description of your question. It can only be sent with your consent.
- The enquiry is sent by email (via Gmail) to the JcoLabs mailbox, where it is kept like any other email. We do not store the enquiry in a database of our portal.
- Our application does not log what you enter: our application log only records that an enquiry was sent or refused, with the reason.
- Against abuse there is a limit per IP address and an overall limit on the number of enquiries (both kept only in the server's memory, not stored), a hidden check field and a minimum fill time.
Externe partijenThird parties
Wij gebruiken de volgende externe diensten. De koppelingen verlopen server-naar-server met API-sleutels die alleen op de server staan.
We use the following external services. Connections run server-to-server using API keys that exist only on the server.
| Partij | Doel | Gegevens |
|---|---|---|
| Hetzner | Hosting van de servers | Alle gegevens op onze servers |
| Cloudflare | DNS, bescherming tegen overbelasting, caching | Websiteverkeer |
| Google (Drive, Gmail, Agenda) | Documenten, e-mail en agenda van het team | Projectdocumenten, bonnetjes, e-mail en afspraken van medewerkers (per medewerker gekoppeld via OAuth); en aanvragen via het contactformulier op de website |
| Google (Gemini API) | AI-functies: bonnetjes uitlezen, AI-assistent | Het bonnetje of de tekst die een medewerker aanlevert |
| Stripe | Online betalingen | Betaalgegevens worden rechtstreeks bij Stripe ingevoerd (zie Betalingen) |
| MoneyBird | Facturatie en boekhouding | Contact- en factuurgegevens, gewerkte uren |
| HubSpot | CRM | Deals, contactpersonen, afspraken |
| ClickUp | Taakbeheer | Taken en projectstatus |
| GitHub | Broncode en uitrol van software | Broncode (geen klantgegevens of geheimen) |
| Party | Purpose | Data |
|---|---|---|
| Hetzner | Server hosting | All data on our servers |
| Cloudflare | DNS, protection against overload, caching | Website traffic |
| Google (Drive, Gmail, Calendar) | Team documents, email and calendar | Project documents, receipts, staff email and appointments (linked per staff member via OAuth); and enquiries from the website contact form |
| Google (Gemini API) | AI features: reading receipts, AI assistant | The receipt or text that a staff member supplies |
| Stripe | Online payments | Payment details are entered directly at Stripe (see Payments) |
| MoneyBird | Invoicing and bookkeeping | Contact and invoice data, hours worked |
| HubSpot | CRM | Deals, contacts, appointments |
| ClickUp | Task management | Tasks and project status |
| GitHub | Source code and software deployment | Source code (no client data or secrets) |
BetalingenPayments
- Online betalingen verlopen via de gehoste betaalpagina van Stripe. Kaart- en bankgegevens komen nooit op onze servers en worden niet door ons opgeslagen.
- Berichten van Stripe naar ons (webhooks) worden alleen geaccepteerd met een geldige handtekening en een recent tijdstempel (maximaal 5 minuten oud).
- Betaalopdrachten gebruiken een idempotentiesleutel, zodat een herhaald verzoek niet tot dubbele afschrijving leidt.
- Facturen worden aangemaakt en verstuurd via MoneyBird. Btw wordt toegepast op basis van het factuurland en, voor EU-zakelijke klanten, een gecontroleerd btw-nummer (VIES).
- Online payments run through Stripe's hosted payment page. Card and bank details never reach our servers and are not stored by us.
- Messages from Stripe to us (webhooks) are only accepted with a valid signature and a recent timestamp (at most 5 minutes old).
- Payment requests carry an idempotency key, so a repeated request does not result in a double charge.
- Invoices are created and sent through MoneyBird. VAT is applied based on the billing country and, for EU business clients, a verified VAT number (VIES).
Logging & controlespoorLogging & audit trail
- Alle inlogpogingen (gelukt en mislukt), tweestapsverificatie en uitloggen worden vastgelegd met tijdstip, IP-adres en browsergegevens.
- Uitrolacties van software worden gelogd in het systeemlogboek van de server.
- Foutmeldingen voor gebruikers bevatten geen technische details; die blijven in de serverlogs.
- Onze applicatie logt de inhoud van contactaanvragen (naam, e-mailadres, bericht) niet; zie Contactaanvragen.
Een breder controlespoor voor wijzigingen in gegevens en geautomatiseerde meldingen staat onder Verbeteringen in uitvoering.
- All sign-in attempts (successful and failed), two-factor verification and sign-outs are recorded with time, IP address and browser details.
- Software deployment actions are logged in the server's system log.
- Error messages shown to users contain no technical details; those remain in the server logs.
- Our application does not log the content of contact enquiries (name, email address, message); see Contact enquiries.
A broader audit trail for data changes and automated alerting is listed under Improvements in progress.
Hosting & back-upsHosting & backups
- Onze servers draaien bij Hetzner Cloud in een datacenter in Duitsland (Europese Unie).
- Van de servers worden automatische back-ups gemaakt via Hetzner.
- Wanneer een klantportaal wordt opgeheven, volgen wij een vaste procedure: gegevens archiveren, toegangsgegevens verwijderen en de omgeving afbreken.
- Our servers run at Hetzner Cloud in a data centre in Germany (European Union).
- Automatic server backups are made through Hetzner.
- When a client portal is retired, we follow a fixed procedure: archive the data, remove credentials and tear down the environment.
Software & wijzigingenSoftware & changes
- De broncode staat in privé-repositories op GitHub.
- Software wordt uitgerold doordat een wijziging op de hoofdbranch een webhook activeert. Die webhook wordt alleen geaccepteerd met een geldige HMAC-SHA256-handtekening; zonder geheim of met een onjuiste handtekening wordt het verzoek geweigerd. De webhook voert zelf geen code uit en geeft alleen het signaal om het eigen uitrolscript te starten.
- Klantportalen draaien onder een eigen, niet-bevoorrechte servicegebruiker en hebben elk een eigen deploy-sleutel en webhook-geheim.
- Wijzigingen aan de databasestructuur worden afzonderlijk door een beheerder uitgevoerd.
- Source code is kept in private repositories on GitHub.
- Software is deployed when a change to the main branch triggers a webhook. That webhook is only accepted with a valid HMAC-SHA256 signature; without a secret or with a wrong signature the request is rejected. The webhook itself runs no code; it only signals our own deployment script to start.
- Client portals run under their own unprivileged service user and each have their own deploy key and webhook secret.
- Changes to the database structure are carried out separately by an administrator.
Verbeteringen in uitvoeringImprovements in progress
Beveiliging is nooit af. Dit staat op onze lijst:
- Strengere Content-Security-Policy door inline scripts te verwijderen, en de webserver alleen bereikbaar maken vanaf Cloudflare.
- Versleuteling op applicatieniveau van opgeslagen tokens van gekoppelde diensten.
- Extra verharding van de verwerking van geüploade bonnetjes en bijlagen.
- Een breder controlespoor voor gegevenswijzigingen, plus bewaking en alarmering van de servers.
- Vastgelegde back-upfrequentie en bewaartermijn, regelmatige databaseback-ups en periodieke herstelproef.
- Definitieve privacyverklaring, algemene voorwaarden en verwerkersovereenkomsten.
- Een onafhankelijke beveiligingsbeoordeling (audit) van onze omgeving.
Security is never finished. This is on our list:
- A stricter Content-Security-Policy by removing inline scripts, and making the web server reachable from Cloudflare only.
- Application-level encryption of stored tokens for connected services.
- Additional hardening of the handling of uploaded receipts and attachments.
- A broader audit trail for data changes, plus server monitoring and alerting.
- Documented backup frequency and retention, regular database backups and periodic restore tests.
- Final privacy statement, terms and conditions and data processing agreements.
- An independent security assessment (audit) of our environment.
Een zorg meldenReport a concern
Heeft u een vraag over deze pagina, een vragenlijst voor uw eigen leveranciersbeoordeling, of vermoedt u een beveiligingsprobleem? Mail ons op [email protected]. Wij nemen elke melding serieus en gaan er vertrouwelijk mee om.
Do you have a question about this page, a questionnaire for your own supplier assessment, or do you suspect a security problem? Email us at [email protected]. We take every report seriously and handle it confidentially.