bitbeet

Yago López

Me, made of cubes: brown hair, a short beard, a light sweatshirt and a beetroot in my hand.

Software developer and architect in Tenerife

I design and build web applications and server tools in TypeScript and Python, from the database schema to deployment.

See my projectshello@bitbeet.dev

My own projects

Three projects of mine, told step by step as you scroll.

Open source · AGPL-3.0 · Python

Noust

Deploys web applications to your own Linux servers and manages all those servers from one console.

  1. Many servers, one console

    A central opens an SSH tunnel to each of your servers and drives them through their API, without exposing new ports on them. Before an action runs on several servers you see the plan; the canary goes first and, if it fails, the rest are left alone.

    In the animation, a central box connects to three towers by three tunnels. One tower, the canary, gets the action first; when it succeeds, the other two get it at the same time. On another turn the canary fails and the other two are left untouched.

  2. Build apart, without root

    Every release is built in its own directory, without touching the one that serves. In new applications, installing and building run as noust-build, a system account with no home or shell, and if the isolation test fails the build stops instead of carrying on as root.

    In the animation, the new release is assembled cube by cube inside a frame. A dependency’s script tries to reach three chests and bounces off the frame each time.

  3. Switch without a gap

    In blue/green mode, which is optional, the new release starts next to the old one and only gets traffic if it passes the health check on its own port. Then nginx validates the configuration and reloads, and the old one finishes its requests before it stops.

    In the animation, the new release answers the health checks, nginx moves the traffic to it, and the old one switches off when its drain bar empties.

  4. Rolling back

    If the new release fails its health check, the previous one is activated again on its own and the error carries the probe results and the journal. You can go back to any release still on disk; Noust puts code back, never a database, and asks before going past a schema change.

    In the animation, an arrow marks the active release on a shelf. A new release fails, disappears and the arrow goes back; then it steps back two releases and pauses before a schema-change marker.

  5. Changes that undo themselves

    Every sshd or firewall change you make through Noust arms a systemd timer that undoes it after 300 seconds. You can only keep it by signing in over SSH again: a session that was already open proves nothing.

    In the animation, a change closes the server’s door and starts a timer. If the new login cannot get in, the timer runs out and the door reopens; if it gets in, the change is kept.

  6. Who changed what

    Every change is written to a log whose lines are chained with HMAC-SHA256, so altering one shows: noust audit verify names the first broken line. If you turn approvals on, root-equivalent changes are decided by a second person, not the one who asked.

    In the animation, a request waits for another person to approve it, runs once and adds a block to a chain. Then one block is altered and the verifier stops at it.

  • 85 releases between December 2025 and October 2026, and almost 12,000 tests.
  • deb and rpm packages for Ubuntu, Debian, Fedora and openSUSE, plus PyPI and a container image.
  • A console and an API with roles, passkeys, two-factor sign-in and an HMAC-chained audit log.
  • Edits nginx and Apache configuration without losing comments or formatting.

Technologies: Python · React · systemd · nginx · Docker

Experimental 3D engine · C++20 · OpenGL 4.5

Prisvector

A 3D engine with deferred rendering, cascaded shadows, entities on EnTT and its own editor.

  1. Geometry pass

    Each object is drawn once and writes to four textures at the same time: position and depth, normal and metalness, colour and roughness, emission and occlusion.

    In the animation, the character is drawn once and shows up at the same time in four images: position and depth, normals, colour and occlusion.

  2. Lights add up

    One full-screen pass reads the four textures and adds up the light from every source with a Cook-Torrance PBR model: room for 4 directional, 256 point and 64 spot lights. The result is kept in HDR and ACES tone mapping brings it to the screen.

    In the animation, three coloured lights orbit the character; each lights its own image and the three add up in the final image. Then a divider shows the image without tone mapping and with ACES.

  3. Four cascades

    The sun’s shadow is split across four cascades: four slices of the view frustum, each with its own orthographic box and its own 2,048 × 2,048 map. The cuts blend a logarithmic and a linear scale (λ = 0.75), so near the camera each texel covers less ground.

    In the animation, the camera’s view frustum, seen from above, is cut into four slices of 13, 15, 32 and 140 metres. Each slice gets an orthographic box and a depth map of the same size, so the first one is the sharpest.

  4. Spotlight atlas

    Up to 16 spotlights with shadows share one 4,096 × 4,096 texture cut into 64 tiles of 512. When none is free, the engine reuses the tile that was used longest ago.

    In the animation, four spotlights ask for one tile per frame and the 8 by 8 grid fills up in order. Once full, every new request overwrites the oldest tile.

  5. A built-in editor

    Dockable Dear ImGui panels, ImGuizmo gizmos to move, rotate and scale, and an inspector that edits materials and lights live. The viewport shows the engine’s real output, and debug views expose the G-buffer and the cascades.

    In the animation, six panels dock around a viewport; a click selects the character, a gizmo moves and turns him, and the exposure changes the brightness of the image.

Your browser draws this demonstration with WebGL 2: it reproduces the four-texture layout and the maths of the cascades and the atlas, but it is an explanation, not a capture of the engine.

  • About 12,900 lines of C++20 and 970 of GLSL, not counting blank lines or comments.
  • An editor with seven Dear ImGui windows and debug views of the G-buffer and the cascades.

Technologies: C++20 · OpenGL 4.5 · GLSL · EnTT · Dear ImGui · GLFW

Experimental bot · Rust

Ironflow

An arbitrage bot for prediction markets. A “yes” and a “no” from the same market are worth 1 USD together when the market resolves; if both can be bought for less, the difference is margin.

  1. One lane per task

    Each piece is a Tokio task with its own lane. The book, the fills and the orders talk to the engine through bounded queues; merging, collecting and reconciling wake on a timer.

    Six horizontal lanes. Messages travel along three of them between the market and the engine: the book and the fills come in, the orders go out. The other three, merge, collect and reconcile, tick on a timer and check the inventory.

  2. An incremental book

    An initial snapshot and then only deltas: each price level is inserted or removed in an ordered tree, with exact decimals. If the best “yes” and “no” add up to less than 1 USD, there is margin.

    In the animation, a delta removes the best “yes” (0.46 USD): the next one costs 0.50 and, with the “no” at 0.50, the sum reaches 1.00 and the margin disappears. Another delta inserts a “no” at 0.48: together 0.98, under 1 USD. If the connection drops, the book is rebuilt from a fresh snapshot.

  3. Both legs, or neither

    It sends two passive orders at once with the same quantity, one per side and one tick below the best asking price. If the market rejects one, it cancels the other.

    A yellow signal falls through three checks and splits into two orders of the same size, a green one and a red one, placed one tick below the best price. On the second cycle the market rejects the red one and the green one is cancelled: neither stays.

  4. Each fill, once

    The market reports each fill up to three times; the bot books it once, and only its own share. Every 15 seconds it checks the inventory against the on-chain balances.

    The “yes” fill arrives three times with the same identifier: the first enters the inventory and the other two are dropped. The “no” fill arrives mixed with other participants’ and only the bot’s share goes in. Then a bar compares the inventory with the chain and corrects it if they differ.

  5. If only one side fills

    90 seconds after placing the pair, it cancels whatever is still open. And if the gap between “yes” and “no” in a market goes past 3 shares, it stops sending pairs to that market.

    The “yes” fills and the inventory holds five more “yes” shares than “no”: a balance tips past the cap of 3 and shuts the door on new pairs. Next to the “no” order, a twelve-cube timer fills up (the 90 seconds) and the order is cancelled.

  6. Merge without duplicating

    With 10 complete pairs, it checks how many exist on-chain and merges them into 1 USD each; the rest is collected when the market resolves. If a transaction’s reply is lost, it confirms the hash on another node instead of resending.

    Ten complete pairs pass a check against the chain; a slab carrying a fresh nonce travels to the main node and the pairs turn into ten 1 USD coins. On the second cycle the main node does not answer: the slab is not resent, a hash is checked on the backup node, and the coins appear only once.

The Rust version is archived and its repository is private. What it taught me most: when only one of the two orders fills, holding that side risks more than the pair’s margin; hence the 90-second limit and the per-market imbalance cap. And the pair is not atomic: an order can fill before the cancellation of the other one arrives.

  • About 10,700 lines of Rust in a single crate, with async I/O on Tokio.
  • Every transaction first asks for a fresh nonce, and an ambiguous receipt is confirmed on a backup node without resending the transaction.
  • Reconnection with exponential backoff from 0.5 to 30 s, and the book rebuilt from a fresh snapshot.

Technologies: Rust · Tokio · WebSocket · rust_decimal · alloy

Work

Arenna Labs products and work for clients.

Proggest

A modular ERP for managing staff and fieldwork, where each company switches on what it needs: time and attendance, daily work reports and protective equipment, documents with payslips and signatures, and support for special employment centres.

An Arenna Labs product, where I work. I have been its main developer since October 2025, and it has had more than 100 releases.

  • 140 data models, 747 endpoints and 119 migrations.
  • Several companies on one installation, each with its own modules.
  • An Android app built with Capacitor on the same codebase, published on Google Play.

Technologies: TypeScript · NestJS · Next.js · Prisma · PostgreSQL · Redis · Capacitor

Attalaya

Watches Spain’s public procurement platform and every morning sends each company the tenders that match its territory, sector and contract size.

An Arenna Labs product. I have been building it since August 2026 and it is in production.

  • As of 25 September 2026, about 477,000 tenders in the database, with roughly 300 to 400 new ones each working day.
  • Fixed rules that explain every decision. AI only helps to read the tender documents and never rules a notice out.
  • 2,075 Vitest tests and 259 end-to-end tests with Playwright.

Technologies: TypeScript · Next.js · React · Drizzle · PostgreSQL · Redis

Rebar detailing

Web software for rebar workshops (rebar is the steel reinforcement inside concrete): a shape editor, bar schedules, QR labels, BVBS files for the bending machine, cutting plans and a shop-floor planner.

  • The cutting plan and the planner are deterministic: the same input gives the same result, byte for byte.
  • Queries on each company’s data are filtered by company in a shared layer, and a new data model or route without an access policy fails continuous integration.

Technologies: TypeScript · NestJS · Next.js · Prisma · PostgreSQL · Redis

Franchise car rental

Three applications for a network of car rental franchises: online booking with payment, a management panel and an affiliate portal.

Built at Arenna Labs.

  • The management panel locks the car’s row with SELECT … FOR UPDATE when a booking is created, edited or swapped, so two simultaneous requests cannot book the same car.
  • Every charge and every refund goes through the payment gateway of the franchise that handles the booking.

Technologies: TypeScript · Next.js · React · Prisma · MySQL · BullMQ

And many more.

How I work

I test against real systems
Attalaya has almost 600 tests that run against a real PostgreSQL. Noust’s integration harness boots systemd inside Docker.
I deploy so I can go back
Noust keeps earlier releases built, so going back to one is the same switch in reverse. Its zero-downtime handover for Docker Compose came out of a real deployment that gave about fifteen seconds of 502 errors.
Tests enforce the rules
In the rebar software, a new data model or route without an access policy fails continuous integration.
I control who changes what
Noust chains its audit log with HMAC, supports passkeys and can require a second person to approve sensitive changes.

Tools

Every day: TypeScript, Python, PostgreSQL, Redis, NestJS, Next.js, React, Prisma, Linux, systemd, nginx and Docker.

When needed: Rust and C++ with OpenGL.

Contact

I work at Arenna Labs, in La Laguna. If you have a project in mind or would like to hear more about any of these, get in touch.

hello@bitbeet.dev

La Laguna, Tenerife.