<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dccc1peace</title>
    <description>The latest articles on DEV Community by Dccc1peace (@dyc19991225).</description>
    <link>https://dev.to/dyc19991225</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4220114%2Fd6a84249-12d9-47a6-9d5c-500911a10aac.png</url>
      <title>DEV Community: Dccc1peace</title>
      <link>https://dev.to/dyc19991225</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dyc19991225"/>
    <language>en</language>
    <item>
      <title>Raising IRC from the Dead with Kiro: Building Seance Relay Chat</title>
      <dc:creator>Dccc1peace</dc:creator>
      <pubDate>Sun, 01 Nov 2026 06:51:34 +0000</pubDate>
      <link>https://dev.to/dyc19991225/raising-irc-from-the-dead-with-kiro-building-seance-relay-chat-1iha</link>
      <guid>https://dev.to/dyc19991225/raising-irc-from-the-dead-with-kiro-building-seance-relay-chat-1iha</guid>
      <description>&lt;p&gt;&lt;em&gt;How I brought a 1988 chat protocol back to life for Kiroween 2026, and what happened when I gave one spec task to Kiro's agent.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By Zhichao Duan (solo entrant)&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post is written as part of the Kiroween 2026 hackathon (Resurrection category: "Bring your favorite dead technology back to life").&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The dead technology: IRC
&lt;/h2&gt;

&lt;p&gt;Internet Relay Chat was written by Jarkko Oikarinen at the University of Oulu, Finland, in August 1988. It was documented as RFC 1459 in 1993 and updated by RFC 2810–2813 in 2000. For decades it was where open-source projects and hobbyist communities lived, and most of them later moved to closed, account-based chat apps.&lt;/p&gt;

&lt;p&gt;The protocol is still very simple. It is a line of text over a TCP socket, and you can type it by hand in telnet. That made it a good fit for the Resurrection category. My goal was to keep the protocol real, so that old clients still work, and add two modern things around it: a browser client and an AI participant.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built: Seance Relay Chat
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Seance Relay Chat&lt;/strong&gt; is a small, self-hostable chat server that speaks real IRC.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A real IRC server on TCP port 6667.&lt;/strong&gt; Classic clients such as Irssi, HexChat and WeeChat connect with &lt;code&gt;NICK&lt;/code&gt;, &lt;code&gt;USER&lt;/code&gt; and &lt;code&gt;JOIN&lt;/code&gt;. It covers the RFC 1459 / RFC 2812 basics: &lt;code&gt;PRIVMSG&lt;/code&gt;, &lt;code&gt;NOTICE&lt;/code&gt;, &lt;code&gt;PING&lt;/code&gt;/&lt;code&gt;PONG&lt;/code&gt;, &lt;code&gt;TOPIC&lt;/code&gt;, &lt;code&gt;NAMES&lt;/code&gt;, &lt;code&gt;QUIT&lt;/code&gt;, plus &lt;code&gt;LIST&lt;/code&gt;, &lt;code&gt;WHO&lt;/code&gt;, read-only &lt;code&gt;MODE&lt;/code&gt;, &lt;code&gt;MOTD&lt;/code&gt;, and an empty &lt;code&gt;CAP LS&lt;/code&gt; so modern clients register cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A browser client over WebSocket&lt;/strong&gt; that joins the &lt;em&gt;same&lt;/em&gt; channels using only a nickname. A message typed in the browser arrives at an Irssi user as a real &lt;code&gt;PRIVMSG&lt;/code&gt; line, and the other way around.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ghost, the AI bot.&lt;/strong&gt; Ghost is just another IRC user in the channel. &lt;code&gt;!ask&lt;/code&gt; answers a question, and &lt;code&gt;!summary&lt;/code&gt; and &lt;code&gt;!tldr&lt;/code&gt; read back recent history. The live demo runs Ghost on a built-in &lt;strong&gt;mock provider&lt;/strong&gt;, so every reply starts with &lt;code&gt;[mock]&lt;/code&gt;. Anyone self-hosting can plug in their own key for any OpenAI-compatible API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite history&lt;/strong&gt;, using Node's built-in &lt;code&gt;node:sqlite&lt;/code&gt;, so there is no native addon.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F05r8k60cfzbr2xv1wmu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F05r8k60cfzbr2xv1wmu6.png" alt="Raw TCP IRC session on the left and the web client on the right, sharing #seance" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  One hub for every client
&lt;/h3&gt;

&lt;p&gt;The whole thing is one Node.js + TypeScript process. Its only runtime dependency is &lt;code&gt;ws&lt;/code&gt;. TCP clients, WebSocket clients and Ghost all implement the same &lt;code&gt;Client&lt;/code&gt; interface and exchange parsed IRC messages through one &lt;code&gt;ChatHub&lt;/code&gt; (&lt;code&gt;src/core/hub.ts&lt;/code&gt;). The hub is pure logic with no sockets, so most tests run against a fake client. Because of this design, a browser user and an Irssi user see exactly the same channel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0bry71kandawmg6hjzy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0bry71kandawmg6hjzy.png" alt="One hub, three kinds of clients: TCP, WebSocket and Ghost feeding ChatHub and SQLite" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Making both worlds safe was the hardest part: a 512-byte IRC line limit, stripping CR/LF/NUL from anything written into a line, flood control for both TCP and WebSocket clients, and an Origin check on the WebSocket endpoint. The app is deployed with Docker on a VPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Kiro fit in
&lt;/h2&gt;

&lt;p&gt;The repository has a &lt;code&gt;.kiro/&lt;/code&gt; folder at its root, committed on purpose. It has three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Steering:&lt;/strong&gt; &lt;code&gt;product.md&lt;/code&gt;, &lt;code&gt;tech.md&lt;/code&gt; and &lt;code&gt;structure.md&lt;/code&gt; (always included), plus &lt;code&gt;security.md&lt;/code&gt;, included by &lt;code&gt;fileMatch&lt;/code&gt; for source, web and deploy files. They tell Kiro what the product is, the stack (Node ≥ 22.13, strict TypeScript, ESM, &lt;code&gt;ws&lt;/code&gt; as the only runtime dependency), where modules live, and the security rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specs:&lt;/strong&gt; &lt;code&gt;irc-core&lt;/code&gt;, &lt;code&gt;web-client&lt;/code&gt; and &lt;code&gt;ai-ghost&lt;/code&gt;, each with &lt;code&gt;requirements.md&lt;/code&gt; ("WHEN … THE SYSTEM SHALL …" acceptance criteria), &lt;code&gt;design.md&lt;/code&gt; and &lt;code&gt;tasks.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hooks:&lt;/strong&gt; type-check on save (&lt;code&gt;PostFileSave&lt;/code&gt; on &lt;code&gt;*.ts&lt;/code&gt;), run &lt;code&gt;npm test&lt;/code&gt; when the agent stops (&lt;code&gt;Stop&lt;/code&gt;), and an optional protocol-review agent hook that I left disabled because agent hooks use credits.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The honest part
&lt;/h3&gt;

&lt;p&gt;I want to be clear about the order of events. &lt;strong&gt;The steering files, the three specs and the hooks were drafted outside Kiro first&lt;/strong&gt;, following Kiro's documented formats. Each file says so in its first line. Tasks 1–4 of &lt;code&gt;irc-core&lt;/code&gt; are checked because that code already existed when the task list was written. Kiro did not write the core server.&lt;/p&gt;

&lt;p&gt;Next I opened the project in the &lt;strong&gt;Kiro IDE 1.4.62&lt;/strong&gt;, signed in on the &lt;strong&gt;free plan&lt;/strong&gt;. Kiro starts the folder in Restricted Mode and does not run tasks or hooks there. Hooks that tried to run before I trusted the folder were blocked (&lt;code&gt;executionDisabledUntrustedWorkspace&lt;/code&gt;). Once I trusted the folder, Kiro showed each spec in its Requirements / Design / Task list view.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbpsrvwqa9wyh6hqzvpc7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbpsrvwqa9wyh6hqzvpc7.png" alt="The irc-core spec open in Kiro IDE, still in Restricted Mode before the folder was trusted" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Running spec task 5.2 in Kiro
&lt;/h3&gt;

&lt;p&gt;I left one follow-up open on purpose: &lt;strong&gt;task 5.2, "Channel operators and KICK"&lt;/strong&gt; in &lt;code&gt;irc-core&lt;/code&gt;. I clicked &lt;strong&gt;Start task&lt;/strong&gt; in &lt;code&gt;tasks.md&lt;/code&gt;, and Kiro's spec-task agent worked on it for &lt;strong&gt;2 minutes 51 seconds&lt;/strong&gt;. It changed three files: &lt;code&gt;src/core/hub.ts&lt;/code&gt;, &lt;code&gt;src/irc/numerics.ts&lt;/code&gt; and &lt;code&gt;test/hub.test.ts&lt;/code&gt;. What it added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the first user to join an empty channel becomes an operator;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NAMES&lt;/code&gt; shows operators with &lt;code&gt;@&lt;/code&gt;, and ISUPPORT advertises &lt;code&gt;PREFIX=(o)@&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MODE #chan +o/-o nick&lt;/code&gt; grants or removes operator status;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;KICK&lt;/code&gt; returns the correct error numerics: 403, 442, 482 and 441.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It added &lt;strong&gt;10 new tests&lt;/strong&gt; and updated one existing test whose expected &lt;code&gt;NAMES&lt;/code&gt; output had changed. Then it ran &lt;code&gt;npm test&lt;/code&gt; itself (after I approved the command): &lt;strong&gt;51 tests, 51 pass, 0 fail&lt;/strong&gt;. Kiro marked 5.2 as done and wrote &lt;code&gt;tasks.meta.json&lt;/code&gt; with the execution history. Kiro's chat showed an estimate of about &lt;strong&gt;3.8 credits&lt;/strong&gt; for the run. I committed and pushed the result as &lt;code&gt;b727fc8&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I did &lt;em&gt;not&lt;/em&gt; see
&lt;/h3&gt;

&lt;p&gt;Kiro's log showed all three hooks loaded. &lt;strong&gt;I saw no sign that &lt;code&gt;run-tests-on-stop&lt;/code&gt; fired when the agent stopped&lt;/strong&gt;, so I'm not claiming it did. The tests passed because the agent ran them itself, not because of the hook.&lt;/p&gt;

&lt;p&gt;One more caveat: the &lt;strong&gt;live demo still runs the earlier build&lt;/strong&gt;, without operators or KICK. That code is in the repo but not deployed yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned / what I'd do differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Spec tasks are a good unit of work for an agent.&lt;/strong&gt; Task 5.2 was a single line in a task list, but the requirements and design around it gave the agent enough context. It changed only the hub, the numerics and the hub tests, and its work came with tests. Small, well-scoped tasks that you can check against a requirements file made reviewing easy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write the spec first, in Kiro.&lt;/strong&gt; I drafted the spec after the code and outside Kiro, which is why tasks 1–4 were already checked. If I did this again, I would start in Kiro with requirements, then design, then tasks, and let the agent build more of the project task by task instead of only the final follow-up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep steering and tasks consistent.&lt;/strong&gt; My &lt;code&gt;product.md&lt;/code&gt; steering file listed "channel operators and bans" as out of scope, while &lt;code&gt;tasks.md&lt;/code&gt; listed operators and KICK as a follow-up. The agent built the task anyway. Next time I'd update the steering when the plan changes so the two don't contradict each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify hooks, don't assume them.&lt;/strong&gt; My hook files loaded, but I never saw the stop hook fire. Next time I'd test each hook on its own first (save a &lt;code&gt;.ts&lt;/code&gt; file, finish a short agent turn) and check the log before relying on it. And trust the workspace early, because Restricted Mode blocks hooks and tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be honest in the repo.&lt;/strong&gt; I put a "What actually happened" log in the README. It was easy to write and makes clear which parts Kiro did and which parts it didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Live demo (mock AI, earlier build): &lt;a href="http://192.74.249.13:8666" rel="noopener noreferrer"&gt;http://192.74.249.13:8666&lt;/a&gt;. Classic clients can connect to &lt;code&gt;192.74.249.13:6667&lt;/code&gt;, then &lt;code&gt;/join #seance&lt;/code&gt; and type &lt;code&gt;!ask is IRC dead?&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/dyc19991225/seance-relay-chat" rel="noopener noreferrer"&gt;https://github.com/dyc19991225/seance-relay-chat&lt;/a&gt; (see "How Kiro was used" in the README)&lt;/li&gt;
&lt;li&gt;Video: &lt;a href="https://youtu.be/InMUVHeS2YI" rel="noopener noreferrer"&gt;https://youtu.be/InMUVHeS2YI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Devpost: &lt;a href="https://devpost.com/software/seance-relay-chat" rel="noopener noreferrer"&gt;https://devpost.com/software/seance-relay-chat&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;IRC isn't dead. It was just waiting for someone to hold a séance.&lt;/p&gt;

&lt;h1&gt;
  
  
  KiroweenHackathon26 #Kiro
&lt;/h1&gt;

</description>
      <category>hackathon</category>
      <category>irc</category>
      <category>ai</category>
      <category>kiro</category>
    </item>
  </channel>
</rss>
