<?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: Vishwas V Pandey</title>
    <description>The latest articles on DEV Community by Vishwas V Pandey (@pandeyvishwas51oss).</description>
    <link>https://dev.to/pandeyvishwas51oss</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%2F4196177%2F94608274-efb6-4903-8b48-57806cf2ac46.png</url>
      <title>DEV Community: Vishwas V Pandey</title>
      <link>https://dev.to/pandeyvishwas51oss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pandeyvishwas51oss"/>
    <language>en</language>
    <item>
      <title>I built an open-source team chat that writes your daily standup (and lets you bring your own Claude)</title>
      <dc:creator>Vishwas V Pandey</dc:creator>
      <pubDate>Wed, 21 Oct 2026 10:50:43 +0000</pubDate>
      <link>https://dev.to/pandeyvishwas51oss/i-built-an-open-source-team-chat-that-writes-your-daily-standup-and-lets-you-bring-your-own-claude-56p5</link>
      <guid>https://dev.to/pandeyvishwas51oss/i-built-an-open-source-team-chat-that-writes-your-daily-standup-and-lets-you-bring-your-own-claude-56p5</guid>
      <description>&lt;p&gt;Most teams pay per seat for chat. Then they pay again for a standup bot. Then again for an AI add-on.&lt;/p&gt;

&lt;p&gt;I wanted one thing I could run myself that did all three, so I built &lt;strong&gt;Forma Chat&lt;/strong&gt;: open-source (Apache-2.0) team chat that runs on your own server, as one Go binary with a SQLite file.&lt;/p&gt;

&lt;p&gt;📺 &lt;a href="https://pandeyvishwas51-oss.github.io/forma-chat/#video" rel="noopener noreferrer"&gt;Watch the 74-second video&lt;/a&gt; · 💻 &lt;a href="https://github.com/pandeyvishwas51-oss/forma-chat" rel="noopener noreferrer"&gt;Code on GitHub&lt;/a&gt;&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%2F3gho0u9gxlfax7xj33n7.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%2F3gho0u9gxlfax7xj33n7.png" alt="Forma Chat: a channel with a thread, reactions and code" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What works today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Public and private channels, DMs and group DMs, threads, reactions, edits with history, pins, mentions, drafts that follow you across devices&lt;/li&gt;
&lt;li&gt;Full-text search (SQLite FTS5), filtered by what you can read before any result, snippet or count exists&lt;/li&gt;
&lt;li&gt;Turn any message into a task; &lt;strong&gt;My work&lt;/strong&gt; ranks your own tasks and shows why each one is near the top&lt;/li&gt;
&lt;li&gt;Daily standups that draft themselves&lt;/li&gt;
&lt;li&gt;Optional AI agents with your own key, or your own Claude Code connected over MCP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's an early release. Files, huddles and mobile apps aren't there yet, and the repo keeps a &lt;a href="https://github.com/pandeyvishwas51-oss/forma-chat/blob/main/docs/feature-status.md" rel="noopener noreferrer"&gt;feature status&lt;/a&gt; page that says exactly what works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standups that write themselves, without an AI bill
&lt;/h2&gt;

&lt;p&gt;Every task change is recorded as an event: created, started, finished, blocked, commented. At the time you choose, Forma builds each person's draft from &lt;strong&gt;their own&lt;/strong&gt; events that day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Done&lt;/strong&gt;: what they finished, started or worked on&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next&lt;/strong&gt;: their top open tasks by priority and due date&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blockers&lt;/strong&gt;: blocked tasks, with the reason they wrote&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%2Fh44xoqztveaxhf8lh1ps.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%2Fh44xoqztveaxhf8lh1ps.png" alt="A standup draft built from the day's tasks" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No model is involved, so it's free, fast and explainable. The privacy rules mattered more to me than the drafting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A draft is visible only to its author until they submit it.&lt;/li&gt;
&lt;li&gt;Work in personal tasks or private channels the team channel can't see shows up only as a private suggestion ("Only you can see this"). It is never put in the summary automatically.&lt;/li&gt;
&lt;li&gt;One summary posts per day. People who didn't submit are listed as "No update yet", never guessed at.&lt;/li&gt;
&lt;li&gt;No keystrokes, no screenshots, no activity scores, no ranking of people.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  AI agents: your key, or your own Claude
&lt;/h2&gt;

&lt;p&gt;An admin creates an agent, gives it the workspace's own Anthropic API key (or any OpenAI-compatible key) and adds it to channels. Mention it and it answers live in a thread, streaming, with a Stop button. It can search messages and create or list tasks, always with the &lt;strong&gt;asking person's&lt;/strong&gt; permissions.&lt;/p&gt;

&lt;p&gt;The rule I spent the most time on: an answer may only use what the channel it's answering in can already see. If you ask in &lt;code&gt;#general&lt;/code&gt;, it won't quote a private channel you happen to be in.&lt;/p&gt;

&lt;p&gt;Or skip the API key and connect the Claude you already use. Create an external agent, add it to channels, make a token, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http forma &lt;span class="se"&gt;\&lt;/span&gt;
  https://chat.example.com/api/v1/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer fma_..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code can then list its channels, read and post messages, search, and list or create tasks, only where that agent was added.&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%2Fk8wp6j8g20uahmff8icm.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%2Fk8wp6j8g20uahmff8icm.png" alt="Claude Code, connected over MCP, replying in a thread" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it's built
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Go modular monolith + SQLite&lt;/strong&gt; in WAL mode, with embedded migrations. The React/TypeScript client is built by Vite and embedded in the binary, so deploying is copying one file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One read policy.&lt;/strong&gt; A single &lt;code&gt;Authorize&lt;/code&gt; function decides whether a person can read a conversation. Search, threads, realtime and agents all go through it, or through its SQL set form, and a test checks the two always agree. Denied and missing look the same (404).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Messages are never silently lost.&lt;/strong&gt; A message is stored before it is acknowledged. A transactional outbox feeds realtime, and WebSocket events are only wake-ups. Clients catch up from a per-conversation change counter, so a reconnect replays what was missed and retries never duplicate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent keys are sealed&lt;/strong&gt; with AES-256-GCM, bound to the agent, and never shown again. Message text and tool results are treated as data for the model, never as instructions.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;You need Go 1.26+ and Node.js 22+:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/pandeyvishwas51-oss/forma-chat &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;forma-chat
make setup build
./bin/forma serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server prints a one-time link to create the owner account. For a populated demo workspace, run &lt;code&gt;node scripts/demo.mjs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I'd love feedback, especially on the standup privacy model and the agent audience rule. Issues and PRs are welcome: &lt;a href="https://github.com/pandeyvishwas51-oss/forma-chat" rel="noopener noreferrer"&gt;https://github.com/pandeyvishwas51-oss/forma-chat&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>go</category>
      <category>selfhosted</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Making two Claude Codes talk over HyperDHT</title>
      <dc:creator>Vishwas V Pandey</dc:creator>
      <pubDate>Tue, 20 Oct 2026 12:35:25 +0000</pubDate>
      <link>https://dev.to/pandeyvishwas51oss/making-two-claude-codes-talk-over-hyperdht-3g5</link>
      <guid>https://dev.to/pandeyvishwas51oss/making-two-claude-codes-talk-over-hyperdht-3g5</guid>
      <description>&lt;p&gt;My friend and I were building two halves of one app: my agent wrote the frontend, his wrote the backend. Every API question meant copying text out of one Claude Code session, pasting it into a chat, and pasting the answer back. The agents were the ones with the questions. Why couldn't they ask each other?&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/pandeyvishwas51-oss/wokitoki" rel="noopener noreferrer"&gt;WokiToki&lt;/a&gt;, an open-source tool that gives each developer's coding agent a walkie-talkie. This post explains how it works, without a server anywhere.&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%2Frgl92re9fpfgbi30e73t.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%2Frgl92re9fpfgbi30e73t.png" alt="How WokiToki works: install, pair with a 6-digit code, agents talk directly" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraint: nothing to host
&lt;/h2&gt;

&lt;p&gt;The obvious design is a relay server both agents connect to. I built that first, and it works, but it means someone runs a server, keeps it up and is trusted with metadata. I wanted two people to be able to start in one minute with nothing but their laptops.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding each other: HyperDHT
&lt;/h2&gt;

&lt;p&gt;HyperDHT is the distributed hash table behind Holepunch's Keet messenger. Any node can announce itself under an Ed25519 public key, and anyone who knows that key can look it up and connect.&lt;/p&gt;

&lt;p&gt;Each WokiToki daemon listens on the DHT under its own identity key. When the other side connects, HyperDHT hole-punches through both home routers and wraps the connection in a mutually authenticated Noise handshake, so each side knows exactly which key it is talking to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dht&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;firewall&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remoteKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isPairedOrInviteOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remoteKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;onConnection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identityKeyPair&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Simplified from &lt;code&gt;p2p-transport.ts&lt;/code&gt;.) The firewall runs before any data flows. A key that is not a paired peer is dropped, unless you have an invite open, and then it gets five tries.&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%2F62tv75l5h1czfkuxa5zg.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%2F62tv75l5h1czfkuxa5zg.png" alt="WokiToki peer-to-peer architecture over HyperDHT" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pairing: an invite and a 6-digit code
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;wokitoki pair create&lt;/code&gt; prints a one-time invite containing your public key and an invite secret. Your friend joins with it, and both screens show a 6-digit verify code derived from the pairing id and both fingerprints. You compare the codes on a call, and each of you approves. Someone who intercepts the invite can't pass this step without the codes differing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Messages: encrypted twice, delivered once
&lt;/h2&gt;

&lt;p&gt;Noise already encrypts the connection, but WokiToki also encrypts each message end to end with libsodium: X25519 key exchange, a per-pairing key derived with BLAKE2b, and XChaCha20-Poly1305 with the message header as associated data. If you later use the optional relay, it only ever sees ciphertext.&lt;/p&gt;

&lt;p&gt;Messages land in a SQLite outbox first. Each one carries a per-peer sequence number, so delivery is at-least-once on the wire and exactly-once in your inbox. If your friend's laptop is closed, the message goes into a mailbox on the DHT, described next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Offline friends: a mailbox on the DHT
&lt;/h2&gt;

&lt;p&gt;The hard part of serverless messaging is the friend whose laptop is closed. HyperDHT nodes can store small signed records for up to 48 hours, so each WokiToki daemon publishes one record per pairing: its acknowledgement plus any messages still waiting for that peer, sealed with keys derived from the pair's session key. Only the two peers can find, write or read it.&lt;/p&gt;

&lt;p&gt;The public DHT drops values much over about 1.2 KB, because they ride UDP, so each sealed message is split into 1 KB immutable chunks plus a manifest of their hashes. The receiving daemon polls the record while the peer is away, fetches the chunks and processes them in order through the same idempotent path as a direct delivery. Its receipt and ack travel back the same way. In a test on the public DHT, a receiver that started after the sender had already gone offline had the message 2 seconds after coming online.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent side: MCP
&lt;/h2&gt;

&lt;p&gt;The daemon exposes a local Unix socket, and &lt;code&gt;wokitoki mcp&lt;/code&gt; turns it into an MCP server with five tools: &lt;code&gt;wokitoki_send_message&lt;/code&gt;, &lt;code&gt;wokitoki_read_inbox&lt;/code&gt;, &lt;code&gt;wokitoki_list_messages&lt;/code&gt;, &lt;code&gt;wokitoki_list_peers&lt;/code&gt; and &lt;code&gt;wokitoki_get_status&lt;/code&gt;. &lt;code&gt;wokitoki setup&lt;/code&gt; registers it with Claude Code, Codex, Cursor, Windsurf and VS Code.&lt;/p&gt;

&lt;p&gt;Two rules keep this safe. Agents get no pairing tools: pairing, approving and revoking need you at the terminal. And incoming text is returned labelled untrusted, so an agent treats it as information, never as instructions.&lt;/p&gt;

&lt;p&gt;The Claude Code plugin adds one more thing, a hook: at session start and on your next prompt, your agent is told how many unread messages are waiting and from whom, never their text.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it doesn't do yet
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The DHT mailbox is best effort: nodes may drop records early, and mail older than 48 hours waits for a direct connection.&lt;/li&gt;
&lt;li&gt;Networks that block UDP need the optional self-hosted relay.&lt;/li&gt;
&lt;li&gt;Message keys are static per pairing (no ratchet), and there has been no external audit.&lt;/li&gt;
&lt;/ul&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; wokitoki
wokitoki setup &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"You"&lt;/span&gt;
wokitoki pair create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or in Claude Code: &lt;code&gt;/plugin install wokitoki --marketplace pandeyvishwas51-oss/wokitoki&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The code, threat model and protocol docs are at &lt;a href="https://github.com/pandeyvishwas51-oss/wokitoki" rel="noopener noreferrer"&gt;https://github.com/pandeyvishwas51-oss/wokitoki&lt;/a&gt; (Apache-2.0). I'd love to hear what breaks.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>p2p</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
