Skip to content
Português

Sections

Português

Vitória, Espírito Santo, Brazil

A one person lab, with uptime.

Hardware wallet firmware, management systems for the public sector, and infrastructure that runs on my own servers. Everything ships the same way: push to master, CI runs the tests, a webhook redeploys. Nobody opens SSH at two in the morning.

The Self-Labs mark powering up trace by trace

Reading

1422 commits
across 30 repositories, public and private
17 PRs merged
into other people projects
6 in production
systems live on selflabs.org
13 months
of continuous, traceable operation

Counted through the GitHub API on 25 August 2026, not estimated. They refresh themselves every Monday.

LAB What Self-Labs is

The "self" is literal

Self-hosted, self-custody, self-made. No managed database unless there is no alternative, no deploy that depends on somebody logging into a server, no private key sitting on a machine that talks to the internet.

What lives here is software that has already broken in production, been fixed, and kept running. Signed Bitcoin hardware wallet firmware. A shop that destroys buyer data 72 hours after delivery. A personnel system holding roughly 500 officers. Docker images that rebuild themselves every Sunday and burn no runner minutes when nothing changed.

When something went wrong, the repository says it went wrong, with a date and a file reference. That is not modesty, it is traceability.

MET Method

Five things that repeat in every project

Not wall posters. Each one shows up in the code of at least three projects, and the ones that prove it are listed underneath.

  1. GitOps with no manual step, on my own hardware

    The path never changes: push to master, CI tests and builds the image, the Portainer webhook redeploys. In the Wallet Store pipeline the deploy job fails loudly on any HTTP status outside 2xx. ARM64 builds run on native runners, never under emulation, because the real target is an Orange Pi 5 or an Ampere VPS.

    Proof Wallet StoreDIY guidescateim/cupsALFERES

  2. Verification instead of trust

    The shop and the wallet tracker run on watch-only xpubs and derive every address locally. The shop goes further: a check runs in the entrypoint, before the application server, and refuses to start if the configured keys derive an address other than the expected one. A swapped xpub does not silently turn into a redirected payment.

    Proof Wallet StoreCrypto TrackerJade DIY

  3. Privacy and authorization as executable code

    A policy page protects nobody. A scheduled command destroys the buyer personal data 72 hours after delivery. In ALFERES, administering and commanding are two axes that never mix, and scope is enforced in SQL through a recursive CTE over the chain of command, not in the view. In Escala e Folgas authorization lives in the token app_metadata, because user_metadata is writable by the user.

    Proof Wallet StoreALFERESEscala e FolgasAtlas Logistics

  4. Every external integration fails eventually

    Pricing falls through three sources behind a circuit breaker. On-chain balance is read from a pool of three Electrum servers. The WiFi portal monitor only calls the internet down when three independent targets fail, and still waits three cycles before waking the administrator, because a hiccup is not an outage.

    Proof Crypto TrackerWallet StoreCaptive Portalai-usagebar-win

  5. Root cause, even when it sits in the hardware

    On the TTGO T-Display board the wallet app kept locking the Boot button. Rather than accept a driver workaround, the trail led into the circuit: the RTS line from the USB bridge runs through a 10k resistor into a transistor base that pulls GPIO0 to ground. The fix swaps that resistor for a 100nF capacitor, turning DC coupling into AC. A software bug solved in hardware.

    Proof Jade DIYcateim/cupsCaptive PortalPMES

SEL Selected work

Six worth reading

Chosen for what they prove technically, not for size. The field note on each one is the finding that holds the project up.

Own product in production

Wallet Store

Sells hand-assembled DIY hardware wallets to people who buy self-custody and would rather not leave their tax ID, address and email sitting in a shop's database forever. No account to create, payment in Pix or Bitcoin, and the buyer's personal data is wiped about 72 hours after delivery.

Field note

No private key ever touches the server: the shop loads watch-only xpubs and derives a fresh address per sale from a RANGED descriptor with .derive(index), since calling .script_pubkey() without deriving always hands back /0/0 in embit. The entrypoint runs check_depix before gunicorn and refuses to start the app if the configured xpub derives an address other than the expected one, so a swapped xpub becomes a broken deploy instead of a silently redirected payment. On Liquid the blinding key is derived per address via SLIP-77, HMAC-SHA256 of the master blinding key over the scriptPubKey. And the reaper command destroys the buyer's personal data 72 hours after delivery, which puts privacy law in cron instead of on paper.

  • Django 5
  • React 18 + Vite
  • PostgreSQL 15
  • embit
  • Nova DAO (Pix em DePix)
  • Melhor Envio
  • Docker + Portainer
  • GitHub Actions (runner ARM64)
  • Cloudflare Tunnel

closed repository Open service

Firmware and Bitcoin active

Jade DIY

Ships Blockstream Jade firmware, built and Secure Boot signed, for off-the-shelf ESP32 boards, plus the Portuguese guides that turn one of those boards into a Bitcoin hardware wallet that actually works on Windows, Android and iOS. Nobody assembling their own wallet is left without a trustworthy binary or without documentation.

Field note

The stock firmware misbehaves on a T-Display with the CH9102F: opening the serial port from Blockstream Green or SideSwap pins GPIO0 low and kills the Boot button. Ticking Disable ModemHandShake in the driver holds until the first app that overrides it, so the answer had to come from the circuit: RTS leaves the CH9102F, runs through the 10k R19 into the base of Q2, whose collector pulls GPIO0 down, and the modern driver holds RTS at a constant DC level, saturating that base for good. Swapping R19 for a 100nF SMD 0603 ceramic capacitor turns the coupling from DC to AC: the steady level is blocked, the fast reset and boot pulses still get through. A software bug fixed with a soldering iron. The Secure Boot .pem keys are the first line of the .gitignore and were never committed, because losing that key bricks every board already fused to it.

  • ESP-IDF 5.4 / 5.5.4
  • ESP32 e ESP32-S3
  • LILYGO T-Display e Waveshare S3 Touch LCD 2
  • Secure Boot V1 e V2
  • Batch script Windows
  • esp-web-tools (Web Serial)
  • GitHub Actions

CaTeIM/ View code

Public sector in production

ALFERES

Folds into one system what a Military Police personnel section in Espírito Santo, Brazil, kept in Google Sheets stitched together with Apps Script: headcount, leave, benefits, vacation, medical boards, service records, transfers and the official notices. It went live at one unit of roughly 500 officers with about 20 concurrent users at peak, and the unit registry already covers the whole state.

Field note

Access control runs on two axes that never mix: administering means writing and follows unidade_administradora_id, while commanding means reading consolidated numbers and follows the chain of subordination, walked with a recursive CTE over unidade_pai_id. Merge them and anyone who only needs to read totals gets write access to national ID numbers and health records for entire battalions, and health data is a special category under Brazilian privacy law. The filter lands in SQL, not on screen: hiding the button is a courtesy, the clause is what stops you. Same spirit in the migration runner, which runs at boot under pg_advisory_xact_lock with a sha256 per file, so editing a migration that was already applied makes the server refuse to start instead of drifting in silence.

  • Node.js 26 em ESM puro
  • node:http com roteador próprio
  • PostgreSQL 18
  • driver postgres, sem ORM
  • esbuild
  • JavaScript vanilla no frontend
  • Docker + Portainer (GitOps)
  • Caddy
  • Cloudflare Tunnel

closed repository Open service

Own product in production

Radar de Promo

Picks up raw deals from the channels it watches, rewrites the copy with an LLM, swaps the bare URL for a real affiliate link and pushes it to a network of WhatsApp groups and a Telegram channel with nobody in the loop. When a group hits its member cap, the next one is created and the invite already going around starts pointing there.

Field note

No Meta API publishes a link sticker to a Story, so a headful Puppeteer under Xvfb drives an already logged in Meta Business Suite and posts to Instagram and Facebook in one shot. The right sticker only lives under Edit > Stickers > Link, because the 'Add link' button next to it is the swipe-up that Meta itself flags as not supported on Instagram; and its container measures 0x0, so the real box has to be rebuilt from the three handles. After 'Share' the page navigates away and tears down the context, which means 'Execution context was destroyed' is the success signal, not a failure. Where the browser could be killed, it was killed: Amazon became a plain HTTP getShortUrl call with the Associates panel cookie, and Mercado Livre became createLink, both Chromium free.

  • TypeScript
  • Fastify 5
  • Drizzle ORM
  • PostgreSQL 15
  • Evolution GO
  • GramJS
  • Puppeteer
  • Satori + sharp
  • Gemini

closed repository Open service

Firmware and Bitcoin in production

Crypto Tracker

Tracks average cost, unrealized P&L and on-chain balance for Bitcoin, Liquid Bitcoin and DePix by reading the chain itself from a watch-only xpub, on an Orange Pi 5 at home. No private key goes in, no address goes out to somebody else's app, and moving coins between two of your own wallets counts as neither a buy nor a sell.

Field note

For Bitcoin there is no proprietary explorer in the path: the backend speaks Electrum JSON-RPC over SSL against a pool of three servers, turns the scriptPubKey into an Electrum scripthash and parses the raw transaction locally with embit. On Liquid the confidential addresses come off the descriptor via SLIP77 and unblinding happens on the box itself, with requests round robined between liquid.network and blockstream.info. Deduplication is per wallet, unique on (wallet, txid), so the same txid can sit on both ends of an internal transfer without either side being dropped, and an is_internal flag keeps those amounts neutral in invested and P&L. The price service has a real circuit breaker: three consecutive Binance failures fall through to AwesomeAPI, backed by a 30 second Redis cache and a one hour last known value for emergencies.

  • Django REST Framework
  • Python 3.12
  • React 18
  • PostgreSQL 15
  • Redis 7
  • embit
  • Electrum JSON-RPC
  • Esplora API
  • Orange Pi 5

CaTeIM/ closed repository Open service

Open active

DIY in Practice

Collects 27 Portuguese-language guides on self-hosting and home automation, from Portainer on Debian to Home Assistant, each one shipping the Docker stack ready to paste into Portainer. It started from a practical annoyance: not redoing the same research every time a service has to be reinstalled.

Field note

This is not a pile of tutorials. The convention is written down in CLAUDE.md and all 18 stacks follow it: restart unless-stopped rather than always, no-new-privileges:true, bind mounts under /srv/<service> instead of named volumes, secrets only through interpolation, TZ America/Sao_Paulo, a database healthcheck gated by depends_on condition service_healthy, and one database per stack instead of a shared Postgres. The guides document the trap that actually breaks an install: create the /srv folders over SSH before deploying the stack, or Docker creates them as root and the non-root container cannot write. The Cloudflare Tunnel rule is just as specific, because cloudflared runs bare metal on the host and is not part of the stack: Cloudflare terminates TLS and the container serves plain HTTP, so the public URL variable points at https while the internal protocol stays http. The densest guide covers backing up an Orange Pi 5, aimed squarely at what Timeshift misses, the bootloader in SPI NOR and the raw NVMe sectors, across four disaster scenarios.

  • Docker Compose
  • Portainer
  • Cloudflare Tunnel e Zero Trust
  • Caddy (forward-auth)
  • Authelia + lldap
  • Home Assistant, Mosquitto, Node-RED, ESPHome
  • Tasmota em ESP8266 e ESP32
  • Forgejo + Forgejo Actions
  • Debian e Orange Pi 5 (ARM64)

CaTeIM/ Open service

ARC Full portfolio

Projects

From C firmware on an ESP32 to multi-architecture Docker images. Private repositories appear without a link, because the code belongs to a client or holds keys.

20 projects

Own product in production

Wallet Store

Sells hand-assembled DIY hardware wallets to people who buy self-custody and would rather not leave their tax ID, address and email sitting in a shop's database forever. No account to create, payment in Pix or Bitcoin, and the buyer's personal data is wiped about 72 hours after delivery.

  • Django 5
  • React 18 + Vite
  • PostgreSQL 15
  • embit
  • +5

closed repository Open service

Firmware and Bitcoin active

Jade DIY

Ships Blockstream Jade firmware, built and Secure Boot signed, for off-the-shelf ESP32 boards, plus the Portuguese guides that turn one of those boards into a Bitcoin hardware wallet that actually works on Windows, Android and iOS. Nobody assembling their own wallet is left without a trustworthy binary or without documentation.

  • ESP-IDF 5.4 / 5.5.4
  • ESP32 e ESP32-S3
  • LILYGO T-Display e Waveshare S3 Touch LCD 2
  • Secure Boot V1 e V2
  • +3

CaTeIM/ View code

Public sector in production

ALFERES

Folds into one system what a Military Police personnel section in Espírito Santo, Brazil, kept in Google Sheets stitched together with Apps Script: headcount, leave, benefits, vacation, medical boards, service records, transfers and the official notices. It went live at one unit of roughly 500 officers with about 20 concurrent users at peak, and the unit registry already covers the whole state.

  • Node.js 26 em ESM puro
  • node:http com roteador próprio
  • PostgreSQL 18
  • driver postgres, sem ORM
  • +5

closed repository Open service

Own product in production

Radar de Promo

Picks up raw deals from the channels it watches, rewrites the copy with an LLM, swaps the bare URL for a real affiliate link and pushes it to a network of WhatsApp groups and a Telegram channel with nobody in the loop. When a group hits its member cap, the next one is created and the invite already going around starts pointing there.

  • TypeScript
  • Fastify 5
  • Drizzle ORM
  • PostgreSQL 15
  • +5

closed repository Open service

Firmware and Bitcoin in production

Crypto Tracker

Tracks average cost, unrealized P&L and on-chain balance for Bitcoin, Liquid Bitcoin and DePix by reading the chain itself from a watch-only xpub, on an Orange Pi 5 at home. No private key goes in, no address goes out to somebody else's app, and moving coins between two of your own wallets counts as neither a buy nor a sell.

  • Django REST Framework
  • Python 3.12
  • React 18
  • PostgreSQL 15
  • +5

CaTeIM/ closed repository Open service

Open active

DIY in Practice

Collects 27 Portuguese-language guides on self-hosting and home automation, from Portainer on Debian to Home Assistant, each one shipping the Docker stack ready to paste into Portainer. It started from a practical annoyance: not redoing the same research every time a service has to be reinstalled.

  • Docker Compose
  • Portainer
  • Cloudflare Tunnel e Zero Trust
  • Caddy (forward-auth)
  • +5

CaTeIM/ Open service

Own product stable, no recent commits

Atlas Logistics

Hides the real address of both the sender and the receiver of a parcel by breaking the delivery into hops between intermediaries, each hop carrying its own label and its own code. The shop ships to the first peer, and the customer's final address is only decrypted at the last hop. For shops and buyers who would rather not have their address riding along on a carrier's label.

  • Node.js
  • Express 4
  • PostgreSQL 15
  • AES-256-CBC
  • +5

atlasdao/ closed repository

Firmware and Bitcoin in production

Jade DIY Flasher

Flashes Jade DIY firmware straight from the browser: pick the board, pick the version, click once. No driver, no Python, no ESP-IDF, no command line. Anyone who bought an assembled wallet can update it alone instead of leaving the board stuck on old firmware.

  • TypeScript
  • esptool-js
  • Web Serial API
  • Rollup
  • +4

CaTeIM/ Open service

Public sector in production

PMES Rosters and ISEO

Builds a PMES unit's monthly roster, daily roster and ISEO, then prints all of it on A4 letterhead exactly as the command signs it. Personnel arrive by service number, pulled from state government systems, instead of being retyped once on each of the three screens.

  • Node.js + Express 4
  • PostgreSQL 16
  • JWT + bcryptjs
  • helmet + express-rate-limit
  • +5

closed repository Open service

Infrastructure active

cateim/cups

Turns an old USB printer into a network printer with AirPrint through a CUPS container that rebuilds itself every Sunday, always on whatever CUPS version the base image ships. It runs the same on a PC, a Raspberry Pi or an Orange Pi, because the image is genuinely multi-arch.

  • Docker multi-arch (amd64 + arm64)
  • Ubuntu Rolling e Debian 13 Trixie
  • CUPS, printer-driver-all, hplip
  • Avahi (AirPrint e Bonjour)
  • +5

CaTeIM/ Open service

Infrastructure in production

WiFi Captive Portal

Sells and controls WiFi access by voucher from inside the OpenWrt router itself, with no RADIUS server, no PC left running and nobody on duty handing out passwords. The customer picks a plan on Telegram, pays by Pix and gets the code on their own, three in the morning included. Built for hostels, guesthouses and events, where the guest list turns over every week.

  • OpenWrt 24.10.4 (Xiaomi AX3000T)
  • openNDS
  • PHP 8 (CGI sob uhttpd)
  • SQLite3 com WAL
  • +4

closed repository

Client work delivered

Jordão Prospecting

Gives the field sales rep a phone app that finds stone shops by city and industry code, draws the driving route to each one and logs the visit. It replaces the spreadsheet with Google Maps open in another tab, where nobody knew who had already been called on or how far the shop actually was from base.

  • JavaScript + JSDoc
  • esbuild
  • PWA
  • Cloudflare Pages Functions
  • +5

closed repository Open service

Open active

ai-usagebar-win

Puts your Claude, Codex and other AI plan quotas in the Windows tray, so you see the limit coming before the agent goes quiet mid task. A meter already existed, the ai-usagebar Rust CLI, but it was built for Waybar, GNOME and macOS. This is one .exe that works on a clean machine, with no Rust and no cargo install.

  • C#
  • .NET 8
  • WPF
  • WPF-UI 4.3.0
  • +4

View code

Own product pre-launch

CriarViral

Takes a handful of hook, body and CTA clips and hands back dozens of short videos ready to post, one per combination. It is for creators and affiliates who need daily volume and are not going to assemble each cut by hand.

  • Next.js 16
  • TypeScript
  • PostgreSQL + Prisma 7
  • Redis + BullMQ
  • +5

closed repository

Public sector in production

Shifts and Days Off

Puts each team's 12x24 and 12x72 rotation on a calendar and lets the officer click a day to claim their time off. Every entry records who created it and who last changed it, instead of being settled loosely in a group chat. Five teams run the same fixed cycle and none of them can see or touch another team's days.

  • React 19
  • Vite 8
  • react-big-calendar
  • date-fns
  • +3

closed repository

Infrastructure active

cateim/stirling-pdf

Builds a 100% MIT Docker image of Stirling-PDF every week, with all 50 plus PDF tools and without the 5 user cap the paid edition imposes. It is for self-hosters who want to merge, split, compress, sign and OCR their own files, with authentication pushed out to the edge.

  • GitHub Actions
  • Docker buildx (amd64 + arm64)
  • ubuntu-24.04-arm runners
  • Docker Hub
  • +5

View code

Client work delivered

helpgas Stack

Wraps a legacy ScriptCase and SQL Server 2019 system in containers, deployed by Portainer straight from Git, on a server nobody logs into over SSH day to day. It also brings the backup routine that was missing, ending on an external SSD mounted only while it is being written.

  • ScriptCase
  • SQL Server 2019 Express
  • PHP 8.2 + Apache
  • msodbcsql18
  • +5

closed repository

Infrastructure in production

Home Assistant setup

Whole-house automation running on its own server: Home Assistant as the brain, Mosquitto as the MQTT broker, ESPHome for the ESP32 boards and Node-RED for the flows that turn unreadable in YAML. Data lives on local disk, deployment is a Portainer stack, and no command depends on a vendor cloud. Every automation becomes a guide carrying the whole YAML rather than snippets, because the bar for inclusion is having worked long enough to be trusted.

  • Home Assistant Container
  • Docker via Portainer Stack
  • Mosquitto (MQTT)
  • Node-RED
  • +4

closed repository

Firmware and Bitcoin active

Origo

Oderico project, not mine: I fix bugs there with write access to the repository. It is firmware that turns the dice, coins or cards the owner rolled into a BIP39 seed, on a board with no radio, no storage and no random number generator along the seed path.

  • C
  • ESP-IDF 5.5.4
  • ESP32 T-Display
  • libwally-core
  • +2

oroderico/ View code

Open active

ai-memory

Gives coding agents long term memory: quit Claude Code mid task, open Codex in the same folder and carry on without re-explaining the architecture, the dead ends or the open questions. The project is Fabio Akita's; what shows up here are three commits of mine, merged upstream, that fix the Windows path.

  • Rust (edition 2024)
  • Cargo workspace, 10 crates
  • SQLite (rusqlite + refinery)
  • axum
  • +5

akitaonrails/ View code

CI How it ships

From push to live, with nobody in between

I develop on Windows, in the IDE only. No server software runs on this machine. From here on it is all automatic.

  1. 01 push

    Commit to master on GitHub or on my own Forgejo.

  2. 02 CI

    Tests, typecheck and image build on a runner native to the target architecture.

  3. 03 registry

    Image published with a CalVer tag in the year.month.revision format.

  4. 04 webhook

    The Portainer stack takes the trigger and pulls the new version.

  5. 05 live

    Cloudflare Tunnel connects outbound. No port open on the host.

This page follows the same path: push to master, GitHub Actions builds the site, Wrangler publishes it on the apex.

NO What I do not do

The short list matters more than the long one

  • I do not provision managed services by default

    Database, queue and storage run on my own servers first. When a third party service does come in, the reasoning is written down in the repository.

  • I do not ship a deploy that needs a human

    If rolling out a new version means someone opening SSH, the job is not finished. Redeploy is a webhook, always.

  • I do not put private keys on servers

    Anything touching Bitcoin runs on watch-only xpubs. Signing happens on the owner device, off the machine that talks to the internet.

  • I do not promise what is not running

    A project with no commits for months shows up here as stable with no recent commits, not as active. What is idle says it is idle.

END Contact

If you have a real problem to solve, write to me

Embedded firmware, a web system that has to survive production, self-hosted infrastructure nobody wants to maintain. Tell me what you need and I will say whether it is for me or not.