Side project
Liveportfolio-core
One backend, several portfolio sites that look nothing like each other.
Open to work
Backend Engineer · Civica · Cardiff
Backend engineer: Java and Spring Boot finance microservices, and the discipline that keeps them fast, correct and cheap to run
I build the backend services that finance runs on, and I measure them before I claim anything. Four years of production Java and Spring Boot: one critical finance API taken from 99 seconds to under one by fixing the query rather than hiding it behind a cache, and a payment flow moved onto events over RabbitMQ so a slow downstream call no longer holds a request open. On the side, a Rust service that puts nine model providers behind one API.
caseIn progress
agent-platform
One OpenAI-compatible endpoint in front of every model, hosted or running on the desk.
One critical finance endpoint. The slow query was restructured rather than hidden behind a cache.
Java and Spring Boot, finance. Where the operational habits come from.
Seven hosted services and two running on the machine itself. The application never learns which one answered.
The same four tasks, sent to the cheapest model that could still do them. Checked by running the results, not by asking the agents how they did.
The tools in production now. Depth for the rest is one disclosure away.
Languages
Primary language: Spring Boot services, JPA, tests.
Frameworks
Finance microservices, event listeners, scheduled work.
Data & messaging
Finance schemas, query tuning, index work.
Delivery & ops
CI/CD for pre-release and production pipelines, release ownership.
Languages
Node services and every front end since 2022.
Languages
PostgreSQL: query plans, indexes, Liquibase migrations.
Read it · Ships production code · Sets the standard
What the services are written in, and what the scripts around them are written in.
agent-platform: the server, its desktop client, and the LLM proxy inside it.
Django and Flask, then FastAPI; agent-platform’s model-training worker and the tooling around it.
Build, push, restart: the automation around a deploy.
Algorithms and contest work at university.
A JetBrains IDE plugin, an Android client and a launcher, all shipped.
Flutter apps since 2021, most recently portal_warp and a shared UI kit.
The .NET 8 Kestrel host inside Portal Remote, and desktop tools before it.
One service. Read comfortably, written rarely.
Backend first. The front-end entries are there because the same person shipped both ends of these systems.
Payroll estimation and transaction creation jobs.
Where the 99-second query lived.
The first agent-platform server, before it was rewritten in Rust.
Streaming responses and an OpenAI-compatible surface on one process.
The gateway API at work: one client call fans out across several Spring Boot microservices.
This site, and the two others that share its backend.
Five desktop applications shipped with installers, auto-update and signed release manifests.
Portal Desktop on Svelte 5, plus logs-explorer and wingbot.
Mobile apps and a shared component kit.
A self-contained Kestrel host that runs without .NET installed.
The MSc dissertation app, and the phone half of Portal Remote.
Personal projects only. FastAPI is the Python framework the current work uses.
Personal projects only, mostly Raspberry Pi services.
Where the state lives and how it moves between services.
Payment flow: queues, retries, dead letters.
agent-platform’s store, through sqlx.
Migrations in the Spring Boot 4 work, as Liquibase is in the finance services.
Auth and storage behind the MSc dissertation app.
Delivery & ops
Getting it out, and knowing what it did once it was out.
Daily on GCP to develop and test; own scripts reduce image build and scaling to one command.
Seven installers per release across three platforms, updating themselves from a signed manifest.
@tanvoid0/bot-client, released through a CI pipeline that gates it.
The parts that decide whether the rest of it can be changed safely.
Scrum and Kanban, estimation, technical-debt stories.
A real PostgreSQL under the integration tests rather than an in-memory stand-in.
Workspaces: one backend package consumed by four sites.
Enough of the platform to publish a plugin to the JetBrains Marketplace.
The build for every service at work: multi-module reactor, dependency management, the pipeline that consumes it.
Unit and integration suites for the Node and TypeScript work, gating releases in CI.
Models & agents
Two different things, kept apart on purpose: models a service of mine calls at runtime, and the assistants I write code with. Both are listed because a claim about either is checkable against a repository.
Daily driver. This site and the shared backend behind it were built through it.
Local models behind agent-platform, reached from inside Docker. No key, no egress.
The /v1 surface agent-platform serves, so any existing client points at it unchanged.
Document generation and assessment across three services.
Wrapped behind bot-client alongside four other providers.
Benchmark sets replayed against real inputs: 395 sessions for the cost gate, a scored corpus for the screener.
The other public cases. Every card opens the write-up.
Side project
LiveOne backend, several portfolio sites that look nothing like each other.
Open source
LiveA desktop client for the agent platform: terminal, repositories and agent workspace in one window.
What the finance API was built against, what each decision cost, and what changed.
Context
An agent worth using talks to several model providers, and each arrives with its own SDK, its own auth and its own idea of what a streamed response looks like. The local runtimes are worse: the code that reaches Ollama on a laptop is not the code that reaches it from inside a container, so the thing that works while developing is not the thing that ships. The service began as a FastAPI server and had to end up somewhere it could be embedded in a desktop application, serve other API clients as a real server, and deploy to the cloud with no desktop client at all.
Constraints
Results
Trade-off
Two servers live at once for the duration, and a rule that every migrated route be byte-identical to the one it replaced, which cost the freedom to improve anything while it ran.
Why
A rewrite that has to land all at once is a rewrite that can only fail all at once. Moving domains across a few at a time meant no flag day, and a migration that could be abandoned at any point without losing what had already landed. It ran 5 to 7 August 2026 and the Python server is now deleted.
Trade-off
Four behaviours differ from the server that was replaced.
Why
The rule existed to stop two live servers diverging. With one server there is nothing to diverge from, so keeping it would have preserved bugs on purpose. The four are recorded as changed rather than quietly absorbed.
Trade-off
A header per request, and the server can spend no quota of its own on a caller’s behalf.
Why
A gateway that stores provider keys becomes the most valuable thing on the host. Forwarding removes the category rather than defending it.
Trade-off
A genuinely new upstream needs an allowlist entry rather than a header.
Why
Bring-your-own-key is only safe if bring-your-own-URL is not. Without the constraint the same feature is a way to make the gateway reach internal services on the caller’s behalf.
Trade-off
One more thing that is true at the edge and not visible in the call sites.
Why
Doing it per call site is how a local model becomes a route that only works when the process happens to be outside Docker. Doing it once makes a local runtime a supported upstream like any other.
The public page is the summary. The detailed record is released by an issued link, one recruiter at a time.
Direct line
For a role, a contract, or a question about anything on this page. Replies come from me, not from a form.
If your mail client does not open, write to tanvoid0@gmail.com.
CV
The same facts as this page, laid out the way an applicant tracking system reads them. Built from the public record at deploy time, so it cannot fall behind what the site says.
Open the CVOpens as a document — print or save it as PDF from there. No phone number or postal address on it.
For recruiters & hiring teams
The CV above is the summary. What a link adds is the part a CV has no room for: per-engagement technical detail, certificates, degree transcripts, and the phone number and postal address the public copy leaves off. Released to one recipient at a time.
Already have a link? Open it directly; no account or sign-in is required. Opening it sets one strictly necessary cookie so the record stays open while you read it; nothing on these public pages sets any. See the privacy notice.