The Lab

Client logos are on the home page; this is a different thing. What we have here is our own infrastructure, run with the discipline of a company ten times our size — because it is where we test everything before we would propose it to you. Each of these is running right now, and everything named below is something you can look up and check.

Custom software · in production

Applications we have to live with

Situation

Three legal entities, three sets of books, and a person retyping bank statements into accounting software every month. The bank has an API. Nobody uses it, because open banking is sold to banks, not to the companies that bank there. That is how the first of these applications came about, and so did the rest — out of a problem of our own, not a brief. Nobody paid us for them, so nothing in them was built merely because it was in the spec — and nobody took delivery either, so there is no getting rid of them. That is exactly what makes them a useful reference: an application you have to use every day will tell you within a year the truth a customer never would.

What we did

Each one stands on a different problem and a different piece of technology — and we built and run all of them ourselves.

Result

Nobody retypes a statement any more: one signature covers the batch, and the month-end that used to be an evening is now a screen you glance at. The rest run the same way. None of them is a prototype we would demo and then switch off — they are things we use, and when they break, they break on us. That is the whole difference from a demo.

What is in them

  • Quvanto

    Accounting for three legal entities. FastAPI over PostgreSQL, statements parsed with pdfplumber, invoices typeset to PDF with WeasyPrint and a pay-by-square QR code on them. Payments leave either as SEPA XML in the pain.001.001.03 format for upload to the bank portal, or straight through the bank payment API, where account access and payment initiation clear in a single authorisation: the whole batch from one screen with one signature.

  • OZORApp

    A PWA for thirty-one people at a festival in Hungary where there is no signal: PHP with no database, state in files, and a service worker that opens the app even when the network will not. Mark the sets you want and it flags the clashes, naming what they collide with. Plus the lineup matched against what you actually listen to on Spotify — nothing is added for you, only offered.

  • Objatie

    Two partners set their expectations across relationship themes independently, and the app reveals them theme by theme — only ever what they have already revealed to each other. Laravel 11 with no build step, serving two domains from one codebase, data in PostgreSQL, and the whole run works anonymously without an account. The AI comment on the result receives only axis names and a coarse band, never the raw answers.

  • Inzerátor

    It sells on Bazos: you photograph the item, a model identifies it, researches the market price through a search service of our own and writes the Slovak listing, you confirm and the app posts it. The model is called through the same gateway as everything else, so the app itself holds no provider key. And since Bazos blocks the hosting provider IP, the work is done by a daemon at home that has no inbound port open at all and picks up jobs through a database notification.

  • Health Bro

    A FastAPI backend on PostgreSQL, reachable only over VPN. It ingests a ring and a Garmin and joins them to what a watch cannot see: supplements, training, lab results, goals. Out of that it derives a readiness state for the day — go hard, or back off. Roughly as much test code as production code, and every warning fails the build.

What it taught us

We learned it in stages, each of them on a different layer.

OZORApp taught us that "offline-first" is not about the network being off, but about a network that lies: on the Hungarian EDGE network a call to the server neither succeeded nor failed — it simply hung, so the service worker waited forever and the app would not load at all, despite being entirely in the cache. Since then no project of ours makes a network call without its own timeout.

Inzerátor taught us to read an obstacle correctly: when a WAF blocks the hosting provider's IP, the wrong answer is to punch a hole into the home network; the right one is to reverse the direction and have a daemon listen on a database channel.

Health Bro taught us that a field name is not a contract — a field whose name promised seven days was quietly carrying fifty-nine, and the payload sent to the model had grown to a hundred and thirty kilobytes before anyone noticed.

Objatie taught us the bug need not be in the code: the deploy uploaded only part of the repository, so a registration living in a file that never shipped worked locally and returned a 500 in production.

And the accounting system taught us the most expensive one: the hard part was never the code. The bank's consent expires roughly daily while the access token lives for months — so the token keeps refreshing over a consent that is already dead, the bank returns an error that never mentions consent, and you go looking for the bug in your own code. Nothing in the documentation says this. You find it the evening before payroll taxes are due, when a batch that cleared last month suddenly does not.

At your company

How many hours a month does someone in your company spend moving numbers from one system into another by hand? We have yet to see a case where the obstacle was technical — it was always that nobody owned the job. And we are not selling you an application we already wrote: we are selling the fact that we have to maintain our own, so you know what you get, and what will break on it a year from now. The first question is always the same one, and it is not technical: which of the things a person does by hand today is even worth writing as software? Some of the time the answer is "none of it, go buy it".

What it looks like

A festival schedule with filters: a strip of days, the "From Spotify" toggle switched on, filters by type and stage, and below them a list of performances carrying a music-note mark.
OZORApp — 385 sets matched against what you actually listen to. The note beside an artist means "this one is in your Spotify". Nothing is added for you; it is only offered.
A list of performances with two starred, each carrying a warning about a clash with named artists.
OZORApp — clashes. A star means "I am going"; when two starred sets overlap in time, the app says so in the list itself and names what they clash with.
A results screen: a circular gauge showing eighty-four percent alignment for a sample couple.
Objatie — the result of a couple's comparison.
A comparison of individual topics, each with a slider and both partners' values side by side.
Objatie — topic by topic. Only what both partners have revealed to each other is ever shown.

The screenshots use sample data, not real people's accounts.

I have the official festival apps on my phone, from Pohoda and from Grape. After two days I stop opening them. This one we had open all week at Ozora — it was the only one that worked where there was no signal, and the only one that showed me where the rest of the group was heading.

Lenka OZORApp · OZORA Festival 2026

Agentization · in production

One gateway, every model, no shadow AI

Situation

Models from four different providers, several applications wanting them, and the default outcome: keys copied into whatever needed them, no budget, no audit, no idea what leaves the building.

What we did

Everything goes through LiteLLM. Twenty model routes, keys issued per project and scoped to specific models, budgets per project, an audit trail of what went where. The local models run on Ollama on our own card — including bge-m3, which computes the embeddings behind Slovak-language search in about a hundred and fifty milliseconds, so the documents being searched never leave. On top of it, agents that operate rather than chat: on cron, unattended, with read allowed and write requiring confirmation.

Result

Every application knocks on one door. Rotating a provider is a config change, not a hunt through repositories. And there is an answer to "what did we send to whom", which is the question that arrives with the auditor.

What it taught us

Before it went live we ran a four-expert adversarial review of our own design. It found a live-confirmed path by which an agent could have leaked its own credentials — in our tooling, that we were about to rely on. We fixed it, verified the fix, and hardened the sandbox around it: deny-by-default egress, read-only filesystem, dropped privileges. The lesson generalises: a tool allowlist that gates on capability but not on path is a whole class of vulnerability. We now assume every read is a read of anything. Nobody found that hole in our system. We found it in our own design, before it had a chance to matter.

At your company

Your developers are already pasting code into a chat window — you just cannot see it. One gateway, keys per team, budgets, an audit log, and the sensitive work pinned to a model that stays inside. First step is usually a two-week audit with a written verdict.

Infrastructure · migration

Moving the network while everyone kept working

Situation

An ISP-issued router doing DNS, DHCP and VPN for everything, with no visibility and no way to segment anything. Replacing it means replacing the three services that every other service silently depends on.

What we did

A MikroTik RB5009 took over one service at a time, each with a way back, over two days. DNS first, then DHCP, then WireGuard — never together. Once the last client had moved to WireGuard, the old OpenVPN was removed. Wi-Fi was consolidated onto Omada access points under our own controller, with guest and IoT traffic separated onto their own SSIDs.

Result

Old router physically unplugged two days after the new one went live. Nothing was announced to users, because nothing needed to be. The legacy VPN is gone — not disabled, gone.

What it taught us

Do not move DNS, DHCP and VPN in one window. It is tempting, because it is one evening instead of three, and it is exactly how you end up debugging a resolver over a VPN that depends on the resolver. Sequence them, and keep the old path alive until the last client has actually moved — not until you think it has. One more thing: when the router is the only resolver on the network, it is the single point that takes everything down with it. We have a dedicated alert rule for that now, because a quiet resolver change once cut off our home automation and we spent an hour looking somewhere else entirely.

At your company

The same shape appears in a company of two hundred: the migration is not the risk, the dependency you did not draw is. We map what depends on what before touching anything, and we sequence the cutover so any single step can be undone in minutes.

Monitoring · built from zero

Monitoring you can actually trust at 3 a.m.

Situation

Plenty of systems, no answer to the only question that matters: is it all up, and if not, what broke first?

What we did

Prometheus, Grafana and Alertmanager, built from nothing. Thirteen scrape jobs, seventy alert rules, fourteen dashboards. Exporters for everything down to the hardware: node_exporter, cAdvisor, smartctl for the disks, blackbox for the probes, mktxp for the router and an exporter for the Wi-Fi controller. The DNS check asks the resolver for real recursion, so it passes only when the resolver genuinely answers. Alerts inhibit each other and arrive in Signal through a bridge we wrote, so a dead resolver pages once instead of forty times.

Result

Every service has a target — down to GPU temperature and days remaining on a TLS certificate. When something breaks, the first alert names the cause — inhibition holds the symptoms back. On top of it sits a status page that answers "is it all up?" in three seconds from a phone, without digging through dashboards.

What it taught us

The audit that came with it found that some of our backups were a sham: they ran, they finished, and they restored nothing. We found it ourselves, fixed it, and now test restores rather than backups. A backup you have never restored is not a backup — it is a belief. That lesson cost us nothing because we went looking; it is the kind that usually costs everything.

At your company

The question is not whether you have Grafana. It is whether an alert firing at 3 a.m. means something, and whether anyone still reads them. We build alert rules with inhibition, so one dead dependency does not page you forty times and train your team to ignore the lot.

Backups · measured

Backups that survive an attacker with root

Situation

A backup on a disk next to the server is a backup right up until someone encrypts that disk too. Everyone knows the 3-2-1 rule; very few installations actually satisfy it, because the "1 offsite" means either a monthly cloud bill or a box at someone whose word you have to take.

What we did

The offsite receiver is a single-board ARM machine with an external disk, physically in another town, reachable only over WireGuard. It runs rest-server in append-only mode, and restic encrypts client-side — so as far as confidentiality goes that remote disk is dumb storage, and the repository password is the only thing that needs guarding. Ten scheduled jobs, GFS retention, and an alert rule in Prometheus for every one of them.

Result

Append-only means a compromised source server cannot rewrite or delete backup history. It can add new data. The old data is not its to touch. Pruning runs from a third machine in a short window, so the right to delete exists for a few minutes a day and does not live on the machine under attack.

What it taught us

We measured because we assumed the nightly run was slow because of the link. It was not. Nine tenths of the nightly run is scanning millions of unchanged files at the source; the transfer itself is a fraction of it, because what goes up is on the order of a gigabyte of changes. The bottleneck is the scan at the source, not the network. Without measuring, we would have spent a month tuning a receiver that was sitting idle. And the second thing we wrote down as plainly as the first: this arrangement survives a compromise of the network core, but not a compromise of the machine the backups are driven from. Write the boundary of a system as precisely as its capabilities, or the reader will imagine a bigger one.

At your company

Two questions decide this, and neither is about software: when did you last actually restore a file from a backup, and can your backups survive an attacker who gets root on the machine being backed up? If the answer to the first is "I do not remember", the second does not need answering yet.

Start with an hour

No deck, no discovery workshop, no invoice. One hour, and you will get a straight answer: this is what we would do, this is what it costs — or, you do not need us for this.

Get in touch