CTO & Solution Architect building AI-powered products, SaaS platforms, and Web3 infrastructure.
Based in Hamburg, Germany 🇩🇪 — working with European startups and scale-ups to turn complex ideas into scalable products.
Desk hardware that works in both directions. Pressing runs something on your computer; an event from the internet — a failed build, a page going down, someone mentioning you — lights the device up. Two of them exist:
XENPAD One — a single mechanical switch with a full-colour indicator, on an RP2040. Three gestures, each mapped independently, and a resting colour that tells you the state of whatever it is watching.
XENPAD Omni — a round touchscreen on an ESP32-S3 with a rotary encoder and an RGB ring. Tiles, layers, and alerts that take over the whole glass with the actual message rather than a coloured dot. Press it and it opens the exact thing that raised the alert.
The whole desk side is mine: firmware for both chips, a signed and notarized macOS app, and the relay that carries events from the internet to a device sitting behind someone's router.
The firmware for both is open source:
xenpad-firmware— XENPAD One, RP2040, GPLv3xenpad-omni-firmware— XENPAD Omni, ESP32-S3, GPLv3xenpad-app-releases— the desktop app, macOS, universal build
The USB protocol is MIT in both, so anything can talk to the devices without inheriting copyleft. That split is deliberate: the shortcuts live in the device's own flash, so a button keeps working on a machine that has never seen my software — and if I stop, someone else can build the firmware and write a replacement app.
C · C++ · RP2040 · ESP32-S3 · TinyUSB · LVGL · Electron · NestJS
Open a browser tab and you are over the real Earth: real terrain, real photogrammetry where it exists, real weather for that city, real sunrise. The aircraft around you are not spawned — they are the ones actually in the air, decoded from ADS-B seconds ago. So are the ships below, from AIS.
Three things run at the same time, and each one feeds the next:
Fly it. Freestyle FPV drones and a gull, six-degree-of-freedom flight model, acro and angle modes, gamepad/RC transmitter support with a calibration wizard, and 19 hand-built race tracks with ghost replays and leaderboards. Also playable in VR on Quest through WebXR.
Collect it. Every real aircraft you catch overhead becomes a card — six rarity tiers, fifteen ranks, monthly seasons, achievements, an inbox that remembers why you got what you got. Types, airlines and manufacturers get their own canonical pages, so a catch turns into a permanent, indexable page about a real machine. Around 1,800 public URLs at the moment; the game itself speaks five languages.
Feed it. The aircraft data is ours: Skydex runs its own ADS-B aggregation network at feed.skydex.online — volunteers point a receiver at it, get a station page with uptime, range and the rarest aircraft they heard, and a badge in the game. Non-exclusive by design: keep feeding every other network at the same time. Getting a receiver onto it is a product of its own — see below.
Around all of that: multiplayer with voice on real airband frequencies over a
WebRTC mesh, user-run radio stations with their own studio, a flying-spots
map built from OpenStreetMap, and a self-hosted observability stack — the
whole thing lives on one small VPS and deploys itself from master.
Not the frozen one. There is an older Skydex — a React Native "Pokédex for planes" I started in 2025 and put on ice. This is the rebuild: same idea, but the sky is a place you fly through rather than a list you scroll, and the data comes from a network we operate instead of someone's API quota.
- 🌍
skydex.online— the game - 📡
feed.skydex.online— the feeder network · live coverage map · stations - 🖥
es-ua/skydex-feeder— macOS feeder app: releases, checksums and the GPL decoder sources we bundle · ⬇ download - 📻
radio.skydex.online— community radio stations · 🖼gallery· 📍spots— real-world FPV flying spots - 🏁 races ·
✈️ aircraft types · 📰 dev log
TypeScript · React · three.js · WebGL · WebXR · NestJS · MongoDB · Socket.IO · WebRTC · Next.js · Swift · Docker · nginx · Grafana
📡 Skydex Feeder for macOS — an SDR stick, two minutes, no Raspberry Pi
Feeding ADS-B has always assumed a spare Raspberry Pi, an install script and a terminal, which is why most people who own an SDR stick have never fed anything with it. This is a native menu bar app instead: plug the stick into the Mac you already use, run a two-minute wizard, and it is a receiving station. The decoder is bundled — no Docker, no terminal, no second computer.
Universal binary for Apple silicon and Intel, signed with a Developer ID and notarized, and it updates itself. Updates carry our signature on top of Apple's notarisation, because the two answer different questions: notarisation says the file came from us and is clean, the update signature says this particular update is the one we published — a swapped file is refused even when it is served from our own domain.
It is not a lock-in client. Skydex sits in the same list of toggles as adsb.lol, airplanes.live, adsb.fi and ADSBExchange, with a field for anything else that takes a Beast stream — and it can be switched off while the others keep feeding. "Check my feed" asks the network itself what it is receiving from your station, because a connected socket only proves the app is talking.
Closed app, open decoder — on purpose. The repository is the
corresponding-source offer: the app bundles
readsb (GPL-3.0-or-later) statically
linked with librtlsdr and libusb, and scripts/build-readsb.sh fetches the
exact upstream sources and reproduces the universal binary we ship, carrying no
patches of our own. Every release lists the version of every bundled component
and the SHA-256 of the DMG, so a download from the site can be verified
against what the repo declares.
- 🖥
es-ua/skydex-feeder— release notes, checksums, decoder sources, issues - ⬇ download — signed & notarized DMG, macOS 13+ · product page & FAQ
- 📡 what it feeds · your station's page
Swift · macOS · RTL-SDR · readsb · Sparkle · codesign · notarytool
A small business in Germany buys a chatbot and gets a widget. What it actually needed was the person behind the widget: someone who knows the opening hours, books the appointment, and writes down who called and what they wanted. Botkontor is that — one AI agent per business, and a customer database that is a by-product of the conversations instead of a second system nobody maintains.
The agent is a graph, not a prompt. Every bot runs a LangGraph
StateGraph — retrieval, routing, tool calls, verification, handover —
started from a preset (triage, RAG support, lead capture, booking) and
rewired per tenant. Its behaviour lives in skills: markdown modules in
a folder tree with @include cross-references, so "what to do when
someone cancels" is a file a human can read and diff, not a paragraph
buried in a system prompt. Answers are grounded in the tenant's own
documents through RAG over pgvector; tools that touch the real world can
be put behind human approval. Mistral by default, OpenAI / Anthropic /
Google on request.
The CRM writes itself. Contacts, timeline entries and bookings are
extracted from the conversation while it is still happening — calendar
sync (Google / Outlook / Apple), email or SMS OTP when identity actually
matters, and live handover that pushes the whole thread to a human on
Telegram the second someone asks for one. Each business gets
{biz}.botkontor.com — dashboard, a public chat page, and a widget that
drops into an existing site with one <script> tag.
Configure it from the chat you are already in. mcp.botkontor.com is
a hosted MCP server: 27 tools across bots, engine, knowledge, skills and
agent tools, so an admin can describe a bot to Claude Desktop or Cursor
and have it created, fed and published without ever opening the dashboard.
It is a stateless proxy that holds no secrets of its own — auth is a
per-tenant tnt_… key supplied by the client, in memory for that session
only.
Everything runs on EU infrastructure. For a Handwerksbetrieb in Hamburg, GDPR is not a feature on a pricing page — it is the precondition for the conversation happening at all.
Mapko is the other half of the platform. Same monorepo, same accounts
and businesses: a map where a place gets found, communities and chats
around it, public business pages at {biz}.mapko.net, and the CRM at
{biz}.mapko.app — web plus a React Native app. Botkontor is what answers
when someone taps message.
And the classification underneath is open. Both halves have to agree on what a business is, so that layer was split out, licensed MIT and published as its own standard — see below ↓.
- 🤖
botkontor.com·botkontor.de— product, pricing, branch templates · 🔐app.botkontor.com— dashboard - 🧩
mcp.botkontor.com— hosted MCP server (SSE), 27 agent-management tools for Claude Desktop / Cursor / any MCP host - 🗺️
mapko.net— community map & business pages ·mapko.app— the CRM side
TypeScript · NestJS · LangGraph · Next.js · React 19 · React Native · MongoDB · PostgreSQL + pgvector · Redis · Mistral · MCP · Docker · nginx
📚 NACE-OSM Taxonomy — a shared vocabulary for what a business is
Every discovery platform reinvents its own category list, and no two of them
agree — which is how Friseursalon, hair salon and shop=hairdresser end
up as three unrelated strings in three systems that are describing the same
shop. The classification layer under Mapko and Botkontor was pulled out into
its own repo, licensed MIT and published as a standard, instead of staying a
table in one company's database.
Anchored, not invented. NACE Rev.2 (Eurostat) as the statistical
backbone, OpenStreetMap tags as the community vocabulary, Schema.org
LocalBusiness subtypes for markup, Wikidata QIDs as cross-lingual anchors.
Nothing was copied from Google, Yelp or Foursquare — deliberately, because
the point is that anyone can use this without asking permission from anyone.
Three levels, four languages. 23 root categories → 353 subcategories → 1,333 service templates, each with names and synonyms in EN / UK / DE / RU. Templates carry typed attributes (price, duration, enums), an OSM tag at the most specific level that actually applies, and trigger/anti-trigger hints an LLM classifier can use to tell a neighbouring service apart from the one it is looking at.
The URIs resolve. It is real Linked Data: SKOS concepts in JSON-LD, one
file per node, 1,711 dereferenceable @ids under
taxonomy.mapko.net/v1/ served as application/ld+json, with a sitemap so
crawlers can find them. Follow an @id and you get the node, not a 404.
It is Layer 0 of something bigger — shared vocabulary on top, a public business graph in the middle, and per-tenant private memory underneath that never leaves its tenant. Mapko and Botkontor are the first consumers; the vocabulary is deliberately not theirs alone.
- 📚
taxonomy.mapko.net— human-browsable and machine-readable ·context.jsonld· example node - 📦
@mapko/nace-osm-taxonomy— npm, MIT, with TS helpers (findByOsmTag,findByNaceRef,findByWikidataId) - 🐙
es-ua/nace-osm-taxonomy— category sources, changelog, contributing
JSON-LD · SKOS · NACE Rev.2 · OpenStreetMap · Schema.org · Wikidata · TypeScript · Cloudflare Pages
|
Non-custodial crypto platform & MCP server — the AI interface layer for blockchain. Connect any wallet to AI agents without exposing private keys.
|
📊 CitensoAI Visibility Tracking — monitor how ChatGPT, Claude, Perplexity and other AI models recommend your brand vs competitors.
|
AI-powered SEO audit tool — comprehensive technical analysis, performance scoring, and actionable recommendations.
|
Building a Cyberpunk 2077-inspired robot from scratch — 3D printed chassis, stereo vision, ESP32 + Raspberry Pi 5, autonomous navigation with ROS 2, LED eye animations, and AI-powered human interaction.
Printing on Bambu Lab A1 Mini 🖨️ | Materials: PETG-CF, TPU
Languages & Frameworks
TypeScript · JavaScript · Python · Swift · SwiftUI · Rust
React · Next.js · Vue.js · React Native · Node.js · Express
Cloud & DevOps
AWS · Google Cloud · Docker · Jenkins · GitHub Actions · Vercel
Data & Backend
PostgreSQL · MongoDB · Redis · Supabase · Firebase · Strapi CMS · Prisma
Blockchain & Web3
Ethers.js · Wagmi · Reown · 1inch · 0x Protocol · LI.FI · MCP
Automation & AI
n8n · LangChain · OpenAI API · Anthropic API · Claude MCP
- Salesforce Certified Platform Developer I
- Salesforce Certified Administrator
💡 Ask me about CRM, iOS, project management, AI integrations, or non-custodial crypto infrastructure



