Na'aman Hirschfeld

@nhirschfeld.bsky.social

Co-Founder & CTO @ xberg.io. Document AI and extraction, polyglot Rust cores. Building basemind. Creator of Litestar & Polyfactory. Open source. Opinionated and strongly typed.

LLMs are genuinely good at writing SQL. It's declarative, the syntax has barely moved in 50 years, and the training corpus is enormous. Where agents fall apart with databases isn't the query. It's the ORM layer wrapped around it.

starmetal: one self-hosted binary that proxies your package registries. pip, npm, cargo, maven and more through a single cache with integrity and policy. Go/Zig/Swift have no index protocol to proxy, so it mirrors their git repos instead. Rust, MIT, early (v0.4).

Sceptre: EasyOCR reimplemented in Rust. EasyOCR is accurate but Python-only (a PyTorch stack). Sceptre matches its output using the same OCR approach (CRAFT detection, gen2 CRNN, CTC over ONNX) and ships as one static binary with no Python.

Xberg v1 is out. It's the successor to Kreuzberg: a content-intelligence framework with a Rust core that extracts text, tables, layout and metadata from 101 document formats, plus code, audio/video, and URLs. Local-first, CPU-only, MIT.

Every open-weight model drop splits the room: benchmark hype vs how-was-it-trained. Both skip the point. A frontier-quality open model drops that capability's price to near zero. No lab gets to rent it back as a moat. I build local-first tooling. That pressure on closed pricing is what sticks.

Long-lived JS/TS projects rot the same way: unused deps in package.json, dead exports, orphaned files. None break the build, so they linger. knip finds all three in one command. I run it on every serious TS repo I maintain. I just wish something this good existed for Python.

A spec is four documents in a trench coat: product reqs, architecture, the impl plan, the contract. Each rots at a different speed, so the whole file decays at the pace of its fastest-rotting part. Layer docs by lifetime instead of topic. Write-up below.

A few years ago I was genuinely scared AI would end development as a profession. Now I recognize I was a useful idiot. Big AI has been hyping the doom and gloom as free publicity and a way to set the narrative. Anthropic and OpenAI are basically a single entity in this regard. #ai #agi #skynet

Building xberg we bet on local-first, CPU-only doc extraction: no GPU, no per-page API call, docs never leave your infra. A Rust and SIMD core does the heavy lifting; the LLM only sees already-clean structured text as the last step. Predictable cost, no rate limits. v1 Jul 21.

Debugging basemind with Claude Code. 488% CPU, five cores pinned, looks like a runaway loop. But disk plateauing while CPU falls to one core is rayon finishing a rebuild. A leak keeps allocating, a scan converges. The derivative is the diagnosis, not the number.

Hand-maintaining 16 language bindings is where polyglot projects rot. The core moves, one wrapper lags, you find out at runtime. Building xberg we generate them: one Rust core, one alef.toml, idiomatic SDKs per target, shared fixtures e2e-testing every binding so they can't drift.

We generate code faster than anyone can review by hand. That makes QA critical, not optional: static analysis, type checks, comprehensive e2e/integration tests, agents reviewing against written rules. Then actually test it by hand. Open source it and thousands of users make it stronger.

Building xberg meant every language in the repo dragged in its own linter and runtime, all wired into pre-commit. So I built polylint: two Rust binaries, one config, no runtimes on the default path. Bundles ruff/oxc/taplo/sqruff in-process, tree-sitter for the rest. Write-up below.

If you write Rust that touches storage, look at Apache OpenDAL. One Operator API over 50+ backends (S3, GCS, Azure, HDFS, Redis, local fs), and retry/timeout/metrics/tracing are composable layers you stack, not code you rewrite per backend. Really clean design.

If an LLM's structured output fails schema validation, don't just retry. Same prompt, same odds. Feed the validation error and the model's own bad output back into the next prompt and ask it to fix that field. Flaky retries become first-try-after-feedback.

Benchmark-driven development: spec first, TDD to make it correct, then real benchmarks to keep it honest. The unlock is agents. You can now build serious benchmark and profiling harnesses that most projects could never justify the time for. Correctness and speed, both measured.

rmux is a terminal multiplexer in Rust with typed SDKs (Rust/Python/TS). So your shell becomes programmable: write a program that spawns panes, runs an agent in each, and orchestrates them. Its Claude mode opens each agent in its own tab. We build basemind this way. github.com/helvesec/rmux

Underrated tool of the week: lilbee by Tobias Perlstein. A local-first AI search engine — runs its own multi-model fleet via llama-swap, doc extraction + VLMs via xberg. Cited answers over your files + code, MCP server, TUI/CLI/REST/Python. ~30 stars, criminally unknown. github.com/tobocop2/lilbee

lilbee terminal UI showing a multi-GPU fleet (CUDA0-3) tensor-splitting a model and self-indexing its own docs, returning cited answers.

Every JSON decoder in Python hands you back dict[str, Any] — then you cast and pray. msgspec decodes straight into your type, at C speed. We found it building Litestar — the secret sauce. pydantic's great, but slow even with its Rust core. The whole "decode into T" helper is 3 lines 👇

Python code comparing four ways to decode JSON into a Grant type: json.loads and orjson.loads return dict[str, Any] (cast and pray); pydantic's TypeAdapter.validate_json and msgspec.json.decode return a typed Grant. Below, a 3-line generic deserialize[T] helper built on msgspec that decodes into any type, including list[Grant].

Vibe coding without guardrails is just fast drift. A model will solve the same problem five ways across one codebase. Strong types and an unforgiving linter are not friction, they are what keeps each iteration honest. Guardrails are no longer optional.