---
title: "Slirc 2, new models (Astra, DSv4.1-flash)"
description: "Expectation: 2 days. Reality: a full rewrite in 3 weeks. What I learned from Astra, DeepSeek v4.1 Flash and good old IRC."
pubDate: "2026-10-01"
series: "Building Slirc"
part: 2

---

I really believed I'd have a decent version in two days. HA 🤦‍♂️. I should know better.

The *alpha* version did take two days. Making it good took three weeks and a full rewrite, and I learned a lot of IRC on the way. This is what changed, and what's coming on October 6th (pre-launch).

<div style="position: relative; aspect-ratio: 1622 / 1080;"><iframe src="https://iframe.mediadelivery.net/embed/733733/25ba5a77-536f-4bec-8583-2a97cf5343a2?autoplay=true&muted=true&loop=true&preload=true" loading="lazy" style="border: none; position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 1rem;" allow="accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture" allowfullscreen></iframe></div>

## What you'll notice

Open the app and your messages are already there. Before, every open started from zero: connect, ask the server for the last 250 messages of each room, draw. One or two seconds on a good connection. Apps like WhatsApp make you wait zero.

The full-screen "Reconnecting…" page is gone too. There's a small "Connecting…" in the header while the app catches up in the background.

## How the alpha got there

The first *alpha* was mostly OpenAI's Astra. It took two of my resets and a ton of tokens. Then I switched to cheaper models, and the quality dropped.

I usually spend [70% of the time planning](https://boiling.mauriciowolff.com/#/23) and reviewing plans, until I know exactly how the model will build what I want. This time I wanted to see how Astra plans on its own, so I let it loose, YOLO. It did things I would **never** do, like a 5,800-line `app.js` next to a 1,600-line `index.html`.

> Everything in life is either a victory or a good lesson

This one was a lesson. You **can** vibecode something and use it, sure. But production software still needs a few years of hard-earned lessons. Ideas and architecture are still human things. At least for now.

## Back to planning

So, back to the workflow I know works. I used my last Astra reset to write the plan, split into stages. Each one shippable on its own, checked on a real phone by a real person (me).

DeepSeek v4.1 Flash built most of the features one at a time, with the exact files to touch, acceptance checks, and a stop condition. And man, that DS model delivers. It's **fast**, and cheap enough that I stopped counting attempts and put my attention on the instructions instead.

Between stages I tested on my Linux and Mac desktops and on my iPhone. *Computer use* could do that for me, but I like testing myself. Being a designer, I always end up polishing something along the way.

## Local first, IRC underneath

**IRC is the server.** That's the whole point of Slirc, and I stand by it. But asking the server for everything on every open is what made the app slow.

Now each account gets its own `IndexedDB` on your device: conversations, messages, drafts, sync progress and an outbox. Open a channel and it draws from disk while the socket catches up. Think [Linear](https://linear.app), or the [local-first](https://lofi.so/) crowd, for chat.

The IRC server is still the source of truth. The local database is a cache and outbox. Wipe it and the only thing you lose is a draft you hadn't sent.

![Slirc with DevTools open on its IndexedDB stores](/slirc-indexeddb.webp)

## Every message gets a receipt number

Sending without duplicates was the fun puzzle. Every message now carries a small IRCv3 tag, `+slirc/cid`, which works like a receipt number. The message goes into the outbox and shows up as `pending`. When the server (Ergo) echoes it back with the same number, the app swaps the pending row in place.

When the app wakes up, it asks each room for everything after the last message it has. If a message you sent before the phone fell asleep is in there, the receipt number matches and it's marked delivered. Seems simple, but it took some brain cells.

## Seven throwaway scripts

The plan assumed a lot about how Ergo behaves, and I didn't want to find out the hard way. So before writing any client code, Flash (DSv4.1) wrote seven tiny scripts that spoke raw IRC to the dev server. Good thinking, because 3 of them changed the plan, basically half.

**Receipt numbers break on multi-line messages**, unless they sit on the opening line of the batch. Put the tag on the first inner line and Ergo drops it silently. You only find that by trying.

**Deleted messages can come back.** Ergo never replays a deletion. Ask for history and the message is just gone. So a message you deleted while your phone slept could come back from the local cache. The app now keeps a short list of deleted ids and drops them on sight.

**Always-on accounts are cheap, but need a restart.** Your account stays in its rooms while your phone sleeps, so you never miss a DM and rooms don't fill up with join and quit lines. Thirty idle accounts cost about 28 KB each. The catch is that Ergo only changes that setting on a restart. On a server I promised never to restart, that's a scheduled event.

## A protocol from 1988, with reactions

This project is teaching me a lot about IRC, and I'm loving it. Something that has lasted almost forty years survived a few battles, and the [IRCv3](https://ircv3.net/irc/) specs are a big part of why. Most of **Slirc** is those specs, used the way they were written.

**Message tags** are the big one. You can hang a small label on any message. A reaction in Slirc is a one-line message with a tag pointing at the message it reacts to. Halloy shows a ❤️ under the line. A client that doesn't know the tag just ignores it.

![Halloy connected to Slirc, with a link preview and a 😂 reaction in #dev-open](/slirc-halloy.webp)

The rest of what Slirc leans on:

- `echo-message`: the server repeats my message back, and that's my confirmation.
- `server-time`: two phones agree on order.
- `account-tag`: the agent knows a command came from my account.
- `batch`: a photo with a caption travels as one message.
- `chathistory`: the catch-up.
- `message-redaction`: deletion, with a spec.

Half of these are still labelled *draft* in IRCv3. Ergo ships them, Halloy and Senpai read most of them, and I get history, reactions, replies and deletes on a protocol from **1988**, with other people's clients in the same room.

Under the hood, the two giant files became **29 small HTML partials** stitched together at build time, plus **51 TypeScript modules**. Still Alpine.js + Nanostores.

## So, two days?

**Three weeks, and counting**. The two-day alpha had a 5,800-line file, no sending state, and a spinner every time the phone woke up. I'd call that **a good prototype**.

Getting from there to here took the parts I can't hand off yet. Deciding how it should work, then holding the phone and checking that it does. The models did all the typing, I did a LOT of prompting and steering.

## What's next

- **October 6th:** pre-launch. The waiting list gets into app.slirc.org, and the next post goes up that day at the latest.
- **October 12th:** the source code goes public.
- **After that:** a native iOS app. It wraps the same web app with Capacitor, so there's one codebase. The first screen asks one thing: sign in to slirc.org, or use your own server. 

Mentions and DMs arrive as push notifications, and a tap opens the room. This rewrite is what makes it possible. The local store moves into storage iOS keeps, and the relay means no password on the phone.

TestFlight comes first. If you want an invite, [let me know](mailto:info@slirc.org).

[Join the waiting list](https://slirc.org/#waitlist), and see you on the 6th.
