Skip to content

Company services

Most services require you to have completed your Onboarding process.

Keycloak is our central single sign-on. We are gradually making it the company default and consolidating the separate logins we have used so far onto it. During this transition you will still meet two:

  • Keycloak (keycloak.d-centralize.nl) - the central SSO and the new default. Most services log in through it, including webmail at mail.d-centralize.nl and plan.d-centralize.nl.
  • GitLab SSO - still used by some tools such as Mattermost.

When a service offers “Log in with d-centralize SSO”, that is Keycloak.

Your username will be assigned by a d-centralize admin.

Company email account
{username}@d-centralize.nl
Receive email through
imap.d-centralize.nl (TLS/SSL, port 993)
Send email through
smtp.d-centralize.nl (TLS, port 587)
Webmail
https://mail.d-centralize.nl/SOGo/

Company emails and aliases are listed in the company data wiki.

Webmail signs in through SSO (Keycloak), but mobile mail apps and desktop email, calendar, and contacts apps connect over IMAP, SMTP, CalDAV, and CardDAV, which can’t use your SSO login. Create an app password for them:

  1. Go to mail.d-centralize.nl/user and open the App passwords tab.
  2. Click Create app password.
  3. App name: name the device or app, e.g. iPhone Mail.
  4. Click generate to fill in a strong password (it appears in both password fields).
  5. Allowed protocols for app password: select what the app needs - IMAP and SMTP for email, CardDAV/CalDAV for calendar and contacts.
  6. Save, and copy the generated password.

In the app, use your full email address as the username and this app password in place of your SSO password.

When communicating with clients, it could be useful to have extra email addresses on different domains. For example, username@itslanguage.nl.

Use the same IMAP and SMTP host names as for the company email mentioned above.

These extra email addresses are an alias of your primary email address, so you’ll receive everything in one inbox. This is the default: if you prefer you can have it separated.

Sometimes you might receive an email that looks like it originates from @d-centralize.nl, only for it to come from a malicious actor. If you suspect something is fishy, check the sender’s email address to make sure it actually comes from the person they claim to be. A general rule of thumb is that people from d-centralize will send emails using their @d-centralize.nl email address. If in doubt, contact the person in question to verify the email using a channel which is not email, like Mattermost.

Read more on how to recognize phishing emails: Phishing (Dutch Government)

Obviously you should not click any links in the email or reply to it. Just flag the email as junk/spam if your email client supports it, and delete it.

An example of a fake email. Note how it is vaguely worded and tries to
make you curious.

English transcription:

“Do you have some time now? I’d call you about it, but I’m in the middle of something important and can’t talk right now. I have an assignment that you must do for me right away. Let me know if you’re available”

If you’d also like to use the calendar and contacts. You can use them through the above webmail portal as well as using the following links for use in other applications:

These connect over CalDAV/CardDAV, so they need an app password too.

You can see and subscribe to the calendar of a co-worker from webmail.

Login, load your calendar and locate the + button next to Subscriptions. Click it and select a co-workers’ agenda.

image

plan.d-centralize.nl is our booking tool: share a personal link and the people you want to meet pick a free slot on your calendar, without the back-and-forth emails. See Booking meetings with plan.d-centralize.nl for the one-time calendar setup and how to share your link.

Our next server provides a personal folder to store files. It also provides access to shared project folders. Use it for persistent storage and shared project folders. Both can be accessed through:

If you access these files a lot, spend a bit of time making a permanent connection from your OS.

Use share.d-centralize.nl for temporary file sharing when the recipient only needs a download link and should not need an account. Sign in with your d-centralize account, upload the files, copy the generated link and send it to the recipient. Files expire automatically after about 60 days.

Use this instead of third-party transfer services like WeTransfer for normal company work.

Through the web interface of our next server, files and folders can be shared with people outside our organization for collaboration efforts. Use Nextcloud here when the other side needs ongoing access or collaborative editing instead of a temporary download link.

  1. Select the share properties of a file or folder.
  2. Click the three dots left of “Share link”.
  3. If you want the person you’re about to share with to edit, tick the “Allow editing” box.
  4. Fill in a note on top (the person and/or reason you’re sharing). That’s for ourselves to be able to get an overview of what content is shared out of the organization. Click the right arrow to finalize.
  5. Now click the copy icon right of “Share link (Sharing with Anna)”.
  6. Paste the link into an incognito browser window to test out if this is how you’d like the user to share with sees it.
  7. Send that link to the user to share with.

image

Many services allow individual login accounts with roles. However, there are still cases where shared credentials are the only way to distribute access within the team. When invited by an admin, you can join our Vaultwarden service.

Mattermost is our primary team communication tool where you can also meet your remote working colleagues. Aside from the general team (dcentralize), a project specific team can be joined for project specific communication. Also, bots post relevant messages here. Join through Mattermost by signing in with your d-centralize Gitlab SSO account.

Mattermost is chosen because of its open source codebase and default (but optional) coupling with Gitlab.

Mobile and desktop apps can be downloaded here.

Ubuntu installation instructions:

Terminal window
curl -fsS -o- https://deb.packages.mattermost.com/setup-repo.sh | sudo bash
sudo apt install mattermost-desktop

Instructions on logging into a self-hosted Mattermost server here.

The External team in Mattermost is designated to invite guests outside our organization.

For new topics, create a new channel of type Private channel. Next click the button Add members to this private channel and invite them through email or send them the invitation link.

Please archive channels once they’re no longer in use.

The Appsemble team uses Discord which is convenient to join for external guests: (Appsemble).

It’s convenient to have a wormhole for messages to travel from Mattermost to Discord and vice versa. That’s the #bridge channel on Mattermost and #mattermost-bridge on Discord.

Besides having textual conversations with your coworkers, having video calls is also very convenient way to also see each other. We use Jitsi to have our video conferences.

To invite coworkers or clients for a conference call, you can create a new conference room and share it.

When in doubt if you have audio/video problems, just check your setup with the echo server

Expose a local or staging service on a public *.tunnel.d-centralize.nl subdomain (for callbacks, demos, temporary external access) with our Pangolin tunnel. The quickest way is the tunnel.sh helper; see the Pangolin tunnel guide.

We maintain a VPN subscription from a commercial VPN provider for several uses:

  • For employees working outside the Netherlands who need to appear as Dutch for security reasons while working on client projects.
  • For deliberately obtaining a remote presence to test out regional differences in our SaaS.

Installing this browser extension makes it easy to use.

Some of our internal services are locked to a set of IP addresses for access. In case you’re working remotely your traffic is rerouted through the office router.

To use the company VPN, you have to configure your device to create a WireGuard VPN tunnel.

Some services are reachable only over IPv6. Keep IPv6 enabled on your device. If IPv6 is slow on dual-stack websites, prefer IPv4 without disabling IPv6; see IPv6 and IPv4 preference.

  1. Open your Terminal and install WireGuard apt package:

    Terminal window
    sudo apt install wireguard
  2. Download the vpn.conf file your admin provides you.

Now choose either GUI or CLI config:

  1. Open NetworkManager

  2. Add VPN

  3. Import from file

  4. Select vpn.conf

  5. Add

  6. Check if the tunnel is working. Go to a website showing your current IP. e.g.,

If the site shows IPv4 address 185.184.111.56, or an IPv6 address in 2001:470:1f15:288::/64, and the country is The Netherlands, then you are connected to the VPN.

  1. Copy it to /etc/wireguard/.
Terminal window
sudo cp vpn.conf /etc/wireguard/
  1. Activate the tunnel and your internet traffic will be rerouted through the company router
Terminal window
sudo wg-quick up vpn
  1. If you want to deactivate the tunnel, you can use the following command:

    Terminal window
    sudo wg-quick down vpn
  2. Check if the tunnel is working. If the tunnel is working, you are going to see a new property in section peer called latest handshake. This property will have a value representing the time of the latest handshake.

Terminal window
sudo wg show
  1. Download the WireGuard app through the WireGuard website and install it

  2. Download the vpn.conf file your admin provides you and place it on your hard drive

  3. Open the WireGuard App, click on Add tunnel and import thevpn.conf file

  4. Type a name and the WireGuard tunnel will be created

  5. Activate the tunnel via the Activate button and your internet traffic will be rerouted through the company router

    To deactivate, please use the Deactivate button and your WireGuard tunnel connection will be turned off.

  6. Check if the tunnel is working

    • Option 1: If the tunnel is working, you are going to see a new property in section Peer called Latest handshake with value representing the time of the latest handshake.
    • Option 2: Go to a website showing your current IP. e.g., https://checkip.amazonaws.com/, https://whatismyipaddress.com/, https://ipleak.net/. If it shows IPv4 address 185.184.111.56, or an IPv6 address in 2001:470:1f15:288::/64, and the country is The Netherlands, then you are connected to the VPN.

The WireGuard tunnel runs on Windows, not inside the WSL VM. Install and activate it with the Windows app (above) and check its status there; WSL traffic reaches the office through the Windows tunnel.

  1. Follow the Windows / Mac instructions above to install WireGuard and activate the tunnel.
  2. From a WSL shell, confirm internal services are reachable with the verification check below.
  1. Download the WireGuard app from the Google Play Store and install it

  2. Scan the VPN configuration through the QR code your admin provides you

  3. Type a name and the WireGuard tunnel will be created

  4. Activate the tunnel via the toggle button and your internet traffic will be rerouted through the company router

    To deactivate, please use the toggle button and your WireGuard tunnel connection will be turned off.

  5. Check if the tunnel is working

    • Option 1: If the tunnel is working, you are going to see a new property in section Peer called Latest handshake with value representing the time of the latest handshake.

    • Option 2: Go to a website showing your current IP. e.g., https://checkip.amazonaws.com/, https://whatismyipaddress.com/, https://ipleak.net/. If it shows IPv4 address 185.184.111.56, or an IPv6 address in 2001:470:1f15:288::/64, and the country is The Netherlands, then you are connected to the VPN.

By default the tunnel routes all the phone’s traffic through the office. If you only want certain apps to use the VPN — for example apps that reach IP-locked office services — you can restrict the tunnel to those apps and leave the rest of your traffic on your normal connection.

  1. In the WireGuard app, open the tunnel and tap the edit (pencil) icon.
  2. Under Applications (“Excluded/Included Applications”), choose “Include only these applications” and select the apps that should use the VPN — for example Bitwarden.
  3. Save and re-activate the tunnel.

Set a keepalive for per-app tunnels. When the tunnel only carries a few apps, set Persistent keepalive = 25 on the peer (edit the tunnel -> Peer -> Persistent keepalive). Without it the tunnel only establishes when an included app sends traffic, so an idle app (e.g. a locked Bitwarden) can leave it showing no handshake. Configs handed out by the onboarding tool already include this.

One config per device: never import the same config or QR on a second device, or both will silently stop working.

On macOS, Linux, or WSL, one command checks whether your tunnel can actually reach our internal services — and tells you what’s wrong if it can’t:

Terminal window
curl -fsSL https://handbook.d-centralize.nl/vpn-status.sh | sh

A healthy tunnel ends with “Company VPN is working”. If it fails, the output names the likely cause (most often DNS not going over the tunnel because another VPN is still active) and the fix. Want to read it before running? The script is at https://handbook.d-centralize.nl/vpn-status.sh.

[Interface]
PrivateKey = <Client-PrivateKey>
Address = <IPv4 address>,<IPv6 address>
DNS = <DNS address>
[Peer]
PublicKey = <Public key of WireGuard interface>
PresharedKey = <Client-PresharedKey>
Endpoint = <Public IP of the system>
AllowedIPs = <IPv4 address>, <IPv6 address>

By default, the company VPN routes all traffic through the office. If you want to keep the VPN always active but only route office traffic through it (split tunnel), you can use a NetworkManager dispatcher script.

How it works:

  1. Disables automatic peer routes (prevents default route from being added)
  2. When the VPN connects, the script fetches the route configuration
  3. It adds routes only for office resources (subnets and hostnames)

Installation:

Download and install the dispatcher script:

Terminal window
curl -o /tmp/99-wg0-split-tunnel https://handbook.d-centralize.nl/vpn/99-wg0-split-tunnel.sh
sudo mv /tmp/99-wg0-split-tunnel /etc/NetworkManager/dispatcher.d/
sudo chmod +x /etc/NetworkManager/dispatcher.d/99-wg0-split-tunnel

Disable automatic peer routes and enable autoconnect:

Terminal window
nmcli connection modify wg0 wireguard.peer-routes no
nmcli connection modify wg0 connection.autoconnect yes

Reconnect to apply:

Terminal window
nmcli connection down wg0 && nmcli connection up wg0

Verify:

Terminal window
# Should show your real IP (not VPN):
curl ifconfig.me
# Should work (routed via VPN):
ping 192.168.2.10

Check logs: journalctl -t wg0-split-tunnel

Project planning, issue tracking, code, CI is all kept in our gitlab. To join, create your personal GitLab login and ask a colleague to grant you access to the appropriate projects. Also see Git for further setup instructions.

We have a local LLM service on the office network for internal development, experiments, and day-to-day chat. It runs Ollama with Open WebUI on neverturnoff2.

PropertyValue
Chat UIhttps://chat.d-centralize.nl
API base URLhttps://litellm.d-centralize.nl/v1
Model namelocal
ReachabilityChat UI: office network or VPN

The chat UI works like chatgpt.com and signs you in with your d-centralize Keycloak account. The API is OpenAI-compatible. See the infra documentation for the installed models and server details.

The model has no internet access by default. For questions that need live information (news, weather, current versions), enable Web Search for the message: click the sliders icon next to the + in the message box and switch “Web Search” on — the globe icon in the input bar turns blue. The toggle is per chat; new chats start with it off.

Use it from backend services, scripts, and local development tools. Do not call it directly from browser code unless the browser itself runs on the office network or through the company VPN.

Terminal window
export OPENAI_BASE_URL=https://litellm.d-centralize.nl/v1
export OPENAI_API_KEY=<your app's LiteLLM key>

Applications connect through the central LiteLLM gateway, the same endpoint that serves our other model aliases: one virtual key per application, minted by an admin and stored in Bitwarden. The stable local alias routes to the local model server; the concrete model behind it may change. Ask in Mattermost or see the infra documentation for how keys are created.

Mind privacy when picking an alias: expect none on free (free-tier cloud providers may log or train on prompts), reasonable on default/claude (paid, but data leaves our infrastructure), and the best on local (inference stays on office hardware).

Python example:

import os
from openai import OpenAI
client = OpenAI(
base_url=os.environ["OPENAI_BASE_URL"],
api_key=os.environ["OPENAI_API_KEY"],
)
response = client.chat.completions.create(
model="local",
messages=[
{"role": "user", "content": "Summarize the latest deployment notes."}
],
)
print(response.choices[0].message.content)

If you need cloud-hosted models instead of the office/VPN-only service, use the shared credentials from Bitwarden item Azure AI - Shared API Keys and follow AI assisted software development.

Sentry is the tool for “Application Performance Monitoring & Error Tracking”. Basically, if something crashes in production, the stack trace to investigate can be obtained there.

Weblate is the tool for translating strings within a project. It can be configured to automatically provide translations (using APIs to services like DeepL) but also allows manual translations and general linting of language specific oddities. An initial setup per project is needed. Depending on the configuration, it usually monitors changes in the main branch. It can then automatically translate source strings into several preconfigured target languages and provide a new MR using the translations branch Weblate maintains.

Plausible is our self-hosted “easy to use and privacy-friendly Google Analytics alternative.”

Information on shared company infrastructure (GitLab runners, router and such) is kept in the infra repo and is also available infra rendered.

Grafana hosts our infrastructure monitoring dashboards (server health, GitLab runners, and service metrics). Log in with your d-centralize Keycloak account.

We’ve got several (6 at the end of 2025) Gitlab runners available for running Gitlab jobs in Docker. You can define tags in a job to direct the job to any or a specific machine.

  • No tags: Gitlab will prioritize a Gitlab hosted runner over our private runners.
  • saas-linux-small-amd64: Gitlab will only run the job on a hosted runner.
  • dcentralize: Gitlab will only run the job on any of our private runners.
  • sole-occupant: Gitlab will only run the job on any of our private runners that have a job limit per server of to 1. (3 runners at the end of 2025 are capped like this).

There are 2 machines available for these situations:

  • When you laptop doesn’t have a GPU and you need that.

  • You have a long running task.

  • You need more memory or storage than your laptop supplies.

  • To access, ask an admin for a shell account. You get sudo (root) rights. You can only access the server from the office or from within the company VPN.

ssh 192.168.2.25 (cuda-dev, 40GB mem) or ssh 192.168.2.26 (cuda-dev2 64GB mem).

We have several S3 buckets in Hetzner. To be used for backups, storing application data. To browse the contents of it:

Terminal window
S3_REMOTE_STORAGE_URL=fsn1.your-objectstorage.com
S3_ACCESS_KEY_ID=<key>
S3_SECRET_ACCESS_KEY=<secret>
S3_BUCKET="<bucket name>"
S3_CONFIG_NAME="hetzner-s3"
rclone config create $S3_CONFIG_NAME s3 url $S3_REMOTE_STORAGE_URL vendor other access_key_id $S3_ACCESS_KEY_ID endpoint $S3_REMOTE_STORAGE_URL secret_access_key $S3_SECRET_ACCESS_KEY provider Other

Then to list all files in bucket:

Terminal window
rclone ls $S3_CONFIG_NAME:$S3_BUCKET

Or serve a webserver so you can browse buckets and files in browser:

Terminal window
rclone serve http $S3_CONFIG_NAME: --addr :8080 --read-only
sensible-browser http://localhost:8080/

Don’t expect the server to be behaving well. There are several users on the machine, they all have root access so things can be messed up easily. Make sure you treat it as an experimental system and keep valuable data backed up elsewhere.