Technology stack
HansaChat is an EU company chat with an operations layer for field teams, designed, built and run by one person. This page shows what each part is built with and why it was chosen.
Versions and counts reviewed in October 2026.
Counted in the repositories
- ConnectRPC services with 179 RPCs, all defined in Protobuf
- 18
- Go binaries from one codebase: API, worker, outbox relay, admin
- 4
- platforms from one client: web, macOS, Windows, Linux, iOS, Android
- 6
- languages on the public website
- 22
- interface languages in the app
- 23
- Playwright end-to-end specs on an isolated stack
- 70
How the pieces fit together
Three applications share one edge and one cluster. The website owns billing and the workspace registry, the Go backend owns everything inside a workspace, and the web app talks to the backend through generated ConnectRPC clients.
Clients
Browser
Website and web app
Tauri
Desktop and mobile shells
hansa CLI
Scripts and agents
Edge
Bunny.net CDN
TLS, caching, DDoS protection
IONOS Managed Kubernetes, Germany
Traefik
Ingress for every application
Web app Vue + Vite
Static Vue build
Chat API
Go with ConnectRPC
- Worker
- Outbox relay
Website Laravel + FrankenPHP
Billing, registry, provisioning
NATS JetStream
Durable commands and events
Centrifugo
WebSocket fan-out
Valkey Sentinel + HAProxy
Cache, presence, sessions
LiveKit
Call media
Managed data on IONOS
MariaDB
One database per workspace plus a central registry
Object Storage S3
Files and avatars
External services
ElevenLabs · LLM · Clef
Transcription, translation, triage
Creem · EmailLabs
Payments and email
Outside the cluster
Cloudflare Workers D1
External status checks
Bunny Storage restic
Encrypted backups in Frankfurt and Stockholm
The path of one message
- The client calls the message service over ConnectRPC.
- The API accepts the request only after NATS JetStream has durably stored the command.
- A worker writes the message and an outbox event to the workspace database in one transaction.
- The outbox relay publishes the committed event to NATS and Centrifugo.
- Every open client receives it over WebSocket; after a gap, clients reload the authoritative state from the API.
Chat backend
Everything that happens inside a workspace runs in Go: messages, channels, files, calls, realtime, imports and the cleaning operations pack. The website never touches workspace data.
- Go 1.26
-
Four binaries from one modular codebase: the API, the message worker, the outbox relay and a guarded admin command. They ship as static binaries in one small image.
Why
A strong standard library, predictable performance and simple concurrency suit a backend made of long-running workers. One language for the whole workspace core keeps it maintainable by one person.
- ConnectRPC + Protobuf connect-go 1.20 · buf 1.71
-
18 services and 179 RPCs are defined in Protobuf. buf generates the Go server and the TypeScript clients with local plugins, so no schema leaves the machine.
Why
One contract serves the web, desktop, mobile and command-line clients. There are no hand-written HTTP paths or response parsing, and a contract change shows up as a compile error rather than a runtime failure.
- One database per workspace MariaDB · sqlc
-
Each workspace gets its own physical MariaDB database. The API resolves the workspace from its domain and opens the matching pool; workers resolve it again themselves and never trust a database name from a queue message. Queries are plain SQL compiled to typed Go with sqlc.
Why
Isolation is structural instead of a filter on every query. Export, import, restore and deletion work per company.
- NATS JetStream 2.10
-
Durable streams for commands, jobs and events, plus a dead-letter stream. Message writes run in 64 ordered partitions.
Why
A message is accepted only after a durable acknowledgement. Messages in one channel stay in order while independent channels are processed in parallel.
- Transactional outbox
-
Domain rows and the outgoing event are committed in the same transaction. A relay publishes committed events to NATS and Centrifugo, deduplicated by event ID, and records failures for retry.
Why
No lost or phantom events when a process stops between the database write and the publish.
- Centrifugo v5
-
WebSocket delivery for messages, presence, typing and call state. Clients subscribe with short-lived per-channel tokens that the API issues only after checking workspace membership.
Why
A dedicated realtime server keeps long-lived connections out of the API process, and each token names both workspace and user, so identities from separate databases cannot collide.
- S3-compatible object storage aws-sdk-go-v2
-
Files and avatars are stored in IONOS Object Storage in the EU and handed out through short-lived signed links.
Why
File storage scales independently of the databases and is encrypted at rest on the server side.
- LiveKit 1.9
-
A self-hosted WebRTC media server for direct-message calls and audio-first channel calls with camera and screen sharing.
Why
Open source and self-hostable, so call media runs on infrastructure HansaChat operates.
- ElevenLabs Scribe
-
Speech-to-text for voice messages. The transcript is attached to the message, searchable and translatable like any text.
Why
Field crews talk more than they type.
- LLM translation
-
Per-reader translation through an OpenAI-compatible API. Each message is translated on demand, cached once per target language and skipped when the reader already shares the language.
Why
A mixed-language crew works in one conversation while every member reads in their own language. The provider stays replaceable behind one interface.
- Cloudflare Clef
-
A decision model classifies operational messages by category, urgency and confidence. It returns probabilities for fixed questions, not generated text. Currently in pilot.
Why
There is no free-form output to parse. Supervisors see what needs attention; acting on it always stays a human decision.
- go-pdf 0.9
-
A4 working-time sheets and service reports are generated on the server.
Why
A pure Go library keeps the backend image small and free of a headless browser.
- Sentry + slog
-
Errors and traces go to Sentry, logs are structured JSON from the standard library, and workers expose metrics in the Prometheus text format.
Why
Few moving parts to operate, and every failure arrives with its context.
Apps and CLI
One Vue client serves the web and is packaged by Tauri for desktop and mobile. The command-line client uses the same public API as the apps.
- Vue 3 + TypeScript Vue 3.5 · TS 5.9 · Pinia 3
-
A single client with 118 single-file components, Pinia stores, vue-router and vue-i18n in 23 interface languages, tested with Vitest.
Why
A mature, readable component model and strict TypeScript make a large client manageable for a very small team.
- Vite + Tailwind CSS Vite 8 · Tailwind 4
-
The build tool and the styling layer: Tailwind CSS 4 with a light and a dark theme.
Why
Fast builds, and design tokens that follow the same documented palette as the website.
- Tauri 2.10
-
Packages the same web client for macOS, Windows, Linux, iOS and Android, with native notifications, deep links and signed desktop updates.
Why
One interface everywhere, using the system webview instead of bundling a browser engine into every app.
- Generated Connect clients connect-web 2.1 · protobuf-es 2
-
TypeScript clients generated from the backend’s Protobuf and copied into the web app. They are never edited by hand.
Why
A contract change becomes a type error in the client instead of a surprise in production.
- centrifuge-js + livekit-client 5.7 · 2.20
-
The client libraries for Centrifugo realtime and LiveKit calls.
Why
Reconnection, recovery and media handling come from the projects that build the servers.
- TipTap 3.22
-
The message composer, with mentions, links and syntax-highlighted code blocks.
Why
A structured editor instead of fragile string handling.
- hansa CLI Go 1.26 · Cobra
-
A stateless command-line client for people, scripts and LLM agents. JSON output by default, credentials in the operating system keychain, and release checksums signed with Minisign.
Why
It consumes the same public API as the apps. Its Protobuf contracts are copied from the backend and locked with SHA-256 hashes, so drift is detected in CI.
Website, billing and registry
Laravel runs the public site, customer documentation, checkout, subscriptions and the central registry of workspaces. It creates a workspace by calling the Go backend over authenticated HTTP, with no shared code and no shared queues.
- Laravel + PHP Laravel 12 · PHP 8.5
-
The public website, customer documentation, checkout, subscriptions, coupons, lifecycle notices and the central registry of workspaces and domains.
Why
Billing, mail, scheduling, queues and forms are where Laravel is strongest. The chat stays in Go, and the boundary between them is one authenticated HTTP interface.
- FrankenPHP 1.13
-
The PHP application server inside the container image, built on a shared base image that pins PHP, its extensions and tooling by digest.
Why
One process serves the application, and rebuilding the same commit produces the same runtime.
- Blade + Alpine.js + Tailwind CSS Alpine 3.15 · Tailwind 3.4 · Vite 7
-
Server-rendered pages with small Alpine.js components, styled with Tailwind CSS and bundled by Vite.
Why
Content pages load fast without shipping a single-page-app framework.
- 22 languages
-
Every public page exists in 22 EU languages with locale-prefixed URLs, hreflang links and a localized sitemap. A test fails if any language misses a key or ships English copy.
Why
A European product should be readable in the buyer’s language from the first page.
- Creem
-
Checkout, tax, invoices and subscriptions, kept in sync through signed webhooks.
Why
Payment, tax and invoicing stay with a specialised provider, while the website keeps a reconciled copy of every subscription.
- EmailLabs
-
Transactional and service email such as invitations, password links and notices.
Why
An EU email provider for an EU product.
- Pest 4.4
-
More than 130 test files cover routes, billing, provisioning, translations and SEO, including browser tests.
Why
Fast, readable tests that run on every change.
Platform and operations
Production and preview run on IONOS in Germany. Every application ships as a container image with its own Helm chart and is deployed by a manually triggered GitLab job.
- IONOS Managed Kubernetes
-
Production and preview clusters, managed MariaDB and S3-compatible Object Storage, all run by a German provider in Germany.
Why
A European provider under EU jurisdiction, not the EU region of a non-European cloud.
- Helm + Traefik
-
Each application ships its own chart. Traefik routes traffic inside the cluster, and workspace database migrations run as a hook before every backend rollout.
Why
Every deployment is versioned, repeatable and easy to roll back.
- Bunny.net
-
CDN, DNS and DDoS protection in front of the cluster, and the storage for encrypted backups.
Why
A European company runs the edge and holds the backup copies, so neither depends on a non-European cloud.
- Valkey + Sentinel + HAProxy Valkey 9.1
-
Three replicas with Sentinel failover and an HAProxy proxy that always points to the current primary. It backs caching, sessions, presence and the realtime broker.
Why
A failover needs no application change, because every service keeps talking to the same address.
- restic 0.18
-
Encrypted database backups every 6 hours to two EU locations, with weekly integrity checks and an automated monthly restore drill.
Why
A backup only counts once a restore has been proven.
- GitLab CI
-
Tests, image builds and Helm chart checks run in CI. Deployments to preview and production are separate, manually triggered jobs.
Why
Automation does the repetitive work; a person decides when something reaches customers.
Around the product
The tools that keep a one-person company observable, reachable and explainable.
- Cloudflare Workers + D1
-
An external status page that checks the app and the website every five minutes and records incidents.
Why
Availability is measured from outside the infrastructure it describes.
- Umami 3.2
-
Self-hosted web analytics that only runs after a visitor has made a consent choice.
Why
Visitor data stays on HansaChat infrastructure instead of feeding an advertising network.
- Remotion 4.0
-
The product videos are recorded from the real application by the end-to-end test runner and composed in code with an AI voice-over.
Why
When the interface changes, the video is re-rendered instead of re-filmed.
- TwentyCRM + WireGuard
-
A self-hosted CRM that is reachable only through a private VPN.
Why
Sales data is not exposed to the public internet at all.
Engineering practices
The habits that let one person ship a multi-tenant product without a separate QA or operations team.
Contracts are generated, never hand-written
Protobuf is the single source for the Go server, the TypeScript clients and the CLI. Generators are pinned to exact versions, and the CLI records the hash of every contract file it uses.
Migrations keep a ledger
Every workspace database records which migrations it has applied. A deployment hook applies only the missing ones and checks again after the rollout. The website keeps its original baseline migrations so existing databases never run them twice.
End-to-end tests on an isolated stack
One command starts the databases, NATS, Valkey, Centrifugo and object storage on free ports, builds the Go binaries, starts the web app and runs 70 Playwright specs with a fresh workspace for every test.
Tests at every layer
478 Go test functions, including two-workspace isolation tests, 109 Vitest files in the client and more than 130 Pest files on the website.
Guardrails in CI
Formatting checks, a 1,000-line limit per Go file and Helm chart tests run in CI next to the test suites.
Manual deployment gates
CI builds and tests automatically, but preview and production deployments are separate manual jobs. Production deploys a release tag that is created only after the change is merged.
Backups proven by restores
Every backup run is monitored, and an automated drill restores the databases every month and validates tables and row counts.
One design system
The website and the app share one documented palette, type scale and component rules, with a light and a dark theme in the app.
AI-assisted, human-gated
AI coding agents work from an instruction file in every repository that points into one central engineering documentation repository. Repeatable workflows such as the end-to-end run are packaged as agent skills. Every change still passes the same tests, and deployments stay manual.
Security management in preparation
HansaChat is preparing for ISO/IEC 27001 certification with a risk register, an evidence register and a decision log. It is not certified today.
What this page claims and what it does not
Everything above describes what is implemented today and was counted from the source repositories. HansaChat does not claim a security certification. Message triage is in pilot, and push notifications for closed apps are not live yet.
Questions about the architecture?
Engineers evaluating HansaChat get direct answers from the person who built it.