Ezra Hulsman

Projects

Detailed write-ups for selected work.

Drag, or use these, to turn the model
  1. −1 IdentityEntra ID, profile, history, memories. Everything is per user.
  2. −2 ClaudeTwo backends behind one interface: Claude Code streaming as a subprocess, or the Anthropic API.
  3. −3 MCP connectorsLoket for leave, ClockWise for hours, plus ANNA's own tools for memory, reminders and documents.
  4. −4 OperationsHealth checks, a log analyser, alerts before tokens expire, an admin portal.

ANNA

Anamata's AI assistant in Microsoft Teams: per-user memory, documents and MCP connectors, in production

In development since Dec 2025

Running

Henk drawn as a floor plan: alerts enter an intake room with a cadence gate, pass into the agent's room, and every tool call has to go through one door, the PreToolUse choke point, into a room of read-only tools. A call aimed anywhere else stops at the wall. INTAKE cadence gate debounce cooldown daily cap ntfy AGENT Claude Agent SDK TOOLSET read-only read-only own memory the one door: PreToolUse Signal, owner only out-of-registry call: stops at the wall

Henk, homelab agent

A security-first agent on the Claude Agent SDK that turns infrastructure alerts into Signal conversations. Its default-deny toolset refused a live prompt-injection payload, and it replays events exactly once across hard container kills.

4
egress ports, zero inbound
3,660
tests, SDK mocked out
The Finance Bot drawn as a sorting line: a bank export goes through about 40 rules, the rest goes to Claude in batches of 40, and a gate at 0.75 confidence sends each row into the month's sheet, the unsure ones written and marked in red. /upload bank export RULES ~40 patterns matched, 100% rest CLAUDE batches of 40 0.75 MONTH SHEET one per month ! below it: written, flagged, never guessed

Finance Bot

A Discord bot that turns our bank exports into a monthly household budget. About 40 rules handle the predictable transactions, Claude handles the rest, and anything it isn't sure of is written flagged instead of guessed.

0.75
confidence; below it, a row is flagged
~40
rules before Claude is asked
The homelab drawn as a site plan: two plots, home with a Raspberry Pi 5, a Raspberry Pi 2 and the workstation, and a Hetzner VPS, joined by a Tailscale mesh, with nightly backups crossing both ways and Cloudflare tunnels to the road. HOME HETZNER rp5 Raspberry Pi 5 DNS, Docker, exit node rp2 1 GB RAM w11 workstation, WSL2 vps Prometheus, Grafana, Taiga nightly backups Tailscale mesh Cloudflare tunnel Cloudflare tunnel N

Homelab infrastructure

Three servers and my workstation on a Tailscale mesh, fronted by Cloudflare tunnels, so no home device has a public port. Prometheus watches the servers, DNS resolves recursively with DNSSEC on each of them, and backups cross devices every night.

4
machines, one mesh
7
scrape targets
  1. This site

    Static-first Astro portfolio on Cloudflare Workers

    Online since Aug 2025

Paused

bible-tui drawn as one core with two wings: the same reader code drives a terminal through crossterm and the browser through ratzilla in 102 lines, with the three fixes the web side needed redlined beside it. the same UI code, two backends BIBLE-CORE 4,215 lines of Rust widgets, search, bookmarks, :goto 3 translations, built in native crossterm 1,191 lines web ratzilla 102 lines $ bible a terminal bible.hulsman.dev 3 fixed on a fork: measured the window, rebuilt every resize, lost its listeners

bible-tui

A terminal Bible reader in Rust with three translations built in. The same UI code runs in the browser: the web port is 102 lines, and the three bugs it surfaced are fixed on a fork of its web backend.

102
lines to put it in the browser
3
translations, nothing leaves the device
  1. Encrypted Chat TUI

    Self-hosted terminal chat in Rust: Tokio, a typed ndjson protocol, checked SQL

    Started Aug 2024

Done

The pytaiga-mcp pull request drawn as a redlined sheet of server.py: repeated error handling struck through and replaced by one helper, inside a revision cloud, with a revision triangle marking 207 lines removed. server.py pytaiga-mcp 1 one helper +11 tests, the first suite merged

pytaiga-mcp

A merged pull request to an open-source MCP server for Taiga: credentials masked everywhere with SecretStr, one helper in place of duplicated error handling, response filtering that takes a story list from about 50 fields to 5, and the project's first test suite.

11
tests, the first
3
Python versions in CI
Claude Sandbox drawn in section: Claude Code inside the thick walls of an empty network namespace, with three sensitive paths capped off at the roof. The only way out is one sleeve through the wall, a socat bridge, into an egress proxy that checks every connection against an allowlist. NETWORK NAMESPACE no interfaces, DNS or routes ~/.ssh ~/.aws ~/.gnupg capped: /dev/null CLAUDE CODE socat bridge EGRESS PROXY allowlist allowed blocked, logged

Claude Sandbox

An OS-level sandbox around Claude Code, independent of its built-in protections: kernel isolation, a filtering egress proxy, and an analysis of every session afterwards. Pure stdlib Python and awk, packaged with Nix for Linux and macOS.

3,700
lines of shell and Python
0–100
risk score per session

Other sheets, drawn for fun