CrtUP
TLS certificate and domain-expiry monitoring.
Ground-up rewrite of a legacy Ruby/Sinatra app: ~5,900 lines with 674 tests, a custom
Postgres-backed job queue instead of Redis, and a three-node HA deployment
(Patroni, etcd, HAProxy over Tailscale) shipped as a single static binary
Read more crtup.com
TLS certificate & domain expiry monitoring :: Crystal, PostgreSQL, Ansible
Designed and built a production monitoring service that tracks TLS certificate coverage for registered domains, polling Certificate Transparency logs (crt.sh) and whois on a daily schedule and alerting on DNS names about to lose valid certificates. Complete ground-up rewrite of a legacy Ruby/Sinatra application, redesigned around DNS names rather than certificates to eliminate false alerts on every renewal.
The rewrite
- ~5,900 lines of Crystal across 90 files, backed by 674 tests.
- A custom Postgres-backed job queue, rather than pulling in Redis.
- Ships as a single static binary.
The Project
- Project has zero package dependencies - so no ORM and all SQL in one layer, plus a CI pipeline enforcing format, lint and a full suite against real PostgreSQL.
- Custom PostgreSQL-backed job queue (FOR UPDATE SKIP LOCKED + LISTEN/NOTIFY) with retries, exponential backoff, dead-lettering and cron scheduling - deliberately avoiding Redis so the app has exactly one runtime dependency.
- Three-node HA deployment provisioned with Ansible: PostgreSQL under Patroni with etcd consensus, HAProxy routing writes to the elected leader, all inter-node traffic over Tailscale. Ships as a single static binary.
- Purpose-built health checks used both by Icinga and as a rolling-upgrade gate - designed to fail while the cluster is still serving but no longer at full strength, so an automated rollout halts instead of taking out quorum.
- Migrated 12,340 certificates from the legacy database, collapsing 34,849 duplicated rows into a 927-row DNS-name registry and re-deriving every fingerprint through the application's own X.509 parser so future polls wouldn't duplicate the import.
Flagjack
3D multiplayer capture-the-flag tank game in Crystal, with an authoritative UDP game server,
client-side prediction, and a hand-rolled binary network protocol.
Four teams battle over flags in a generated city across timed 8-minute rounds.
Cross-platform Windows/macOS builds.
Read more Flagjack
3D multiplayer capture-the-flag tank game :: Crystal, raylib
Built around an authoritative headless UDP server running a fixed 30 Hz simulation, with client-side
prediction and reconciliation so steering stays instant under latency. The pure simulation core is a shared library used unmodified by both client and server, giving one source of truth for game rules.
Designed a binary wire protocol with value quantization and Quake III-style delta compression against per-client acknowledged baselines - ~15-20× smaller than JSON and small enough to avoid IP fragmentation.
A pure, IO-free simulation core shared by client and server, making game rules unit-testable in isolation and guaranteeing prediction matches authority.
Automated specs and Windows/macOS builds in Gitlab CI.
Game Server Console
A framework and web admin console for self-hosted Linux game servers, shipped as a single
dependency-free static binary that installs itself as a systemd service and then serves the UI.
Two consoles built on it (Valheim, Conan Exiles); ~3,900 lines of Crystal covered by 357 specs and
a 10-job CI matrix that runs the real installer under real systemd across five Ubuntu and Debian releases.
Read more Game Server Console
A framework and web admin console for self-hosted Linux game servers :: Crystal, systemd, GitLab CI
Framework plus per-game web consoles for administering self-hosted Linux game servers, currently supporting: Valheim & Conan Exiles
- One static binary is both the installer and the server: setup creates the service user, installs packages, and writes systemd units, sudoers rules and env files - idempotent, with --dry-run.
- Browser console built on the standard library alone, no external dependencies: live-streamed start/stop/restart/upgrade, server settings, save-game downloads, and world switching.
- Designed so unsafe operations are unreachable rather than merely hidden - file downloads are constrained by an allow-list the listing itself produces, and locked settings can't be written even by a forged request.
- ~3,900 lines of Crystal, 357 specs, and a 10-job integration matrix that installs and exercises the real binary under real systemd on Ubuntu 22.04/24.04/26.04 and Debian 12/13
Amiga Workbench
A browser recreation of the Amiga Workbench, used as a front-end for viewing demoscene creations.
Open a drawer, click a floppy, watch a demo.
Read more Amiga Workbench
A recreation of the Commodore Amiga desktop, rebuilt in a browser and put to work as a demoscene viewer. The grey-and-black palette, the Topaz font, the drawer windows and Amiga mouse pointers, all looks the way it did in 1992. Icons and windows can be dragged around, windows resized and drawers opened and closed.
In the drawers are productions from some of the great demoscene groups. Each demo opens a player window, where the titles link back to the productions and groups on www.pouet.net. Every demo has its own address, so a single production can be linked directly. Type a wrong one and you get a Guru Meditation.
EVA
DNS telemetry ingestion pipeline in Crystal as a dependency-light, horizontally scalable service. Reimplemented the PCAP and DNS wire decoders from scratch - Ethernet/IPv4/IPv6, IPv4 fragment reassembly, TCP stream reassembly and handshake RTT, and a full DNS message decoder with EDNS0 - then request/response joining, a 50-column row schema, GeoIP/ASN/resolver/public-suffix enrichment, and ZSTD Parquet output written as day-partitioned files to S3.
Read more
DNS telemetry ingestion pipeline :: Crystal, S3, Parquet, RabbitMQ
Ported the ingestion core of ENTRADA (SIDN's Java/Spring DNS analytics platform) to Crystal as a dependency-light, horizontally scalable service.
- Reimplemented two external Java libraries in Crystal: a PCAP reader (IP defragmentation, TCP reassembly, handshake RTT) and a DNS wire decoder (all common RR types, EDNS0 / ECS / EDE), validated against the upstream project's JUnit fixtures.
- Built the full pipeline: S3 fetch → gzip/xz/bz2 decompress → packet parse → request/response join → 50-column row build → MaxMind GeoIP/ASN, public-suffix and resolver enrichment → Parquet write to day-partitioned S3.
- Wrote Parquet via Apache Arrow GLib FFI with ZSTD, dictionary encoding and deliberate row-group sizing; added a compactor that merges small per-file outputs into sorted ~128 MB–1 GB partition files for interactive query (DuckDB/Athena/Trino) over ~3.6 B rows/year.
- Hand-rolled an S3 client with AWS SigV4, a pure-Crystal MaxMind .mmdb reader, and liblzma/libbz2 FFI, keeping the runtime dependency surface minimal; verified against MinIO in CI.
- Designed the runtime as leaderless stateless workers on a RabbitMQ quorum queue, with hot-reloading enrichment data from S3 and separate compaction/refresh jobs - deployed as a single multi-call binary.