Full-stack engineer with a physics-and-maths background. I ship products end to end — a tree-structured knowledge tool running on five platforms, an Obsidian plugin used by thousands, plugins that put Claude Code to work on real repositories, and the Python libraries and services underneath them.
22projects built
7of them full products
10+years writing software
01
Product engineering
One data model, every surface. Web, desktop, mobile and chat clients that stay in sync and keep working offline.
02
AI agent infrastructure
Harnesses that let coding agents work on real repositories without breaking them: isolation, review gates, and a paper trail.
03
Backends & tooling
The unglamorous half: typed APIs, schema migrations, packaging, and libraries other people install.
Languages
TypeScript / JavaScript · Python · Dart / Flutter · Go · C++ · SQL
A note-taking product where everything is a node in one tree — outlines, task breakdowns, kanban boards, structured fields. The same tree runs on the web, a Tauri desktop build, a Flutter mobile app, a Telegram Mini App and a Telegram bot, staying editable offline and reconciling when the network returns.
A free catalogue of university lecture courses on YouTube, arranged by what has to come first. Every course knows what it depends on and what it opens up, so a subject becomes a route instead of a pile of links: pick where you want to end up and the site lays out the path, prerequisite by prerequisite, with the recordings for each step. 236 courses across 39 fields, 12,500 lectures from 309 universities and channels, re-crawled every night.
Two published Claude Code plugins behind one marketplace command. make-better audits an existing codebase across nine topics and applies the fixes; feature builds every change in an isolated git worktree with its own port and lands it through a pull request. Both are configurable per project.
claude — /make-better
$ /make-better# picks the oldest subsystems, reviews each across nine topicsTotal: 104 findings across 5 systems. - 13 bug fixes (applied automatically) - 14 DRY refactors - 11 architecture cleanups - 13 consistency fixes - 6 efficiency wins - 16 tests added - 18 doc-sync updates - 10 completeness fixes - 3 securityeach system on its own branch, merged back with --no-ff. nothing pushed.
make-better — flags
/make-better
Smart defaults — usually the three stalest systems.
/make-better 8
Review eight systems instead.
/make-better flutter 4
Four systems whose names match "flutter".
--no-discover
Skip discovery and go straight to review.
--rebuild
Force a full re-discovery of the subsystem registry first.
--yes
Unattended — no plan mode, no prompts, runs to completion.
A browser dashboard that puts every Claude Code session on one shared time axis — when the agent was working, when the human was reading, every tool call and every subagent run. It reads the transcripts already on disk, so there are no hooks to install, no config to change and no dependencies to audit.
A research platform that turns public social-network data into something you can navigate: it crawls a profile's connections through official APIs, works out which communities the graph splits into, and ranks the traits those communities share. Six services — a React front end, a Flask API, a domain layer, a distributed request worker, a FastAPI analysis service and a token pool — built over three years and now published as one repository.
ntrail — friend graph
the query bar
GET vk.user <id> friends
The friend list of a profile, as a set you can act on
GET vk.community <id> members
Everyone in a group, ready to intersect with another set
split clusters
Break the current set into communities by graph structure
interesting properties
Rank what the current set has in common — school, university, city, age
services
frontend
React 16 + Redux; sigma / vis for the graph, recharts for the distributions
backend
Flask API over PostgreSQL — query objects, selective execution, caching
A configuration library that removes the setup step entirely. Instantiate Config() and it walks up from the current file to the project root, finds the YAML, JSON, INI, .env and Python config files along the way, folds in environment variables, and exposes the result through one typed accessor.
python
$ pip install bestconfig# app/main.py — config.yaml and .env live two levels upfrom bestconfig import Configconfig = Config("myconfig.json")# that was the entire configuration stepmode = config.logger.mode # 'WARNING'port = config.int('PORT') # 5050pw = config.get('DATABASE_PASSWORD')config.to_dict() -> every source, merged
sources it reads without being told
.yaml / .yml / .json
Parsed with native types — `port: 5050` arrives as an int.
.ini / .cfg
Standard section-based config files.
.py
Python config modules, skipped if they construct Config() themselves to avoid recursion.
.env / env_file
KEY=VALUE files, including values that parse as Python literals.
Environment variables
Existing and newly set, merged with the file sources.
dict
Plain dictionaries inserted at runtime via `config.insert(...)`.
accessors
config.name
Attribute access for the common case.
config['name']
Dict access — raises KeyError when absent.
config.get('a.b.c', default)
Dotted path to any depth, with a default instead of an exception.
config.int / float / str / list / dict
Typed getters that return None when the cast fails.
config.get_raw('key')
Opt out of type interpretation entirely.
config.assert_contains('key')
Fail loudly at startup if a required setting never loaded.
An Obsidian plugin that adds a public, collaboratively edited layer to a private vault. Any note can be published to a shared network, linked by URL, found through in-editor autocomplete, and edited by anyone — with changes synchronised back to every participant's vault.
Obsidian — command palette
Create note
Publish a new note into the shared network.
Update note
Pull the latest cloud changes for the note you are in.
Upload current note
Move an existing local note into the shared layer.
Sync base
Reconcile the local mirror with the cloud.
Copy obsidian url
Produce an obsidian://netwik?id=… link anyone with the plugin can open.
Delete remote note
Remove the note network-wide, not just from this vault.
how a shared link resolves
# a link to a note this vault has never seen$ open "obsidian://netwik?id=6f2c…"plugin intercepts the URL schemefetches the note from the shared storewrites it into w/ as ordinary markdownnote opens in the editor, fully linkable