Back to blog
Blog

NanoRelay: How Two AI Agents Coordinate Without a Server

How I built a lightweight message relay between a CLI agent and a Telegram bot using nothing but a shared database table.

6 min
AIInfrastructureAgentsPostgreSQL

I have two AI agents. One lives in my terminal. The other lives on my phone.

The first is a CLI-based coding agent that runs on my laptop inside a workspace full of context files, plans, scripts, and persistent memory. It has access to every tool I use for building: file system, Git, databases, integrations, web search. It is the builder.

The second is a mobile agent running on a VPS inside a Docker container, accessible through Telegram. It can query my database, trigger automation workflows, run SEO audits, monitor leads, and send me a morning briefing at 7 AM. It is the operator.

They needed to talk to each other.

The Problem

These two agents live in completely different environments. The builder runs locally on my workstation. The operator runs inside ephemeral Docker containers on a remote VPS. There is no shared file system, no WebSocket connection, no pub/sub broker between them.

I considered several approaches:

  • Redis pub/sub on the VPS. Adds infrastructure I need to maintain.
  • A webhook relay. Requires exposing an endpoint on my local machine.
  • A shared file on cloud storage. Polling latency, sync conflicts, fragile.

Then I realized I already had the answer: both agents can already talk to the same PostgreSQL database.

The Solution: One Table

sql
CREATE TABLE agent_messages (
    id SERIAL PRIMARY KEY,
    sent_at TIMESTAMPTZ DEFAULT NOW(),
    from_agent TEXT NOT NULL,
    to_agent TEXT NOT NULL,
    message TEXT NOT NULL,
    context TEXT,
    read BOOLEAN DEFAULT FALSE
);

That is the entire coordination layer. No broker. No queue. No WebSocket. A table with six columns.

The builder writes a row when it needs to tell the operator something:

sql
INSERT INTO agent_messages (from_agent, to_agent, message, context)
VALUES ('builder', 'operator', 'SEO audit complete. Score: 85/100.', 'ops');

The operator checks for unread messages every 5 minutes when active. When the builder starts a new session, it checks for unread messages from the operator and marks them as read.

Context Tagging

The context column solved a workspace isolation problem. I run the builder agent across multiple workspaces, each with different concerns:

  • A personal operations hub for freelance and infrastructure work
  • A business workspace for client-facing operations
  • A content workspace for the creative brand

Without context tags, every workspace would see every message. Now each workspace only reads messages tagged for it (or broadcasts tagged for all). The rule is simple: tag by topic, not by where you are sitting. If you are in the operations hub but the message is about content, tag it for the content workspace.

The Dashboard

Messages flow both directions, so I needed visibility. I added a relay panel to my central dashboard that shows all messages with:

  • Direction indicators (who sent to whom)
  • Color-coded context badges
  • Unread highlighting
  • Context filter buttons

The panel is read-only for me. The agents handle read/unread state. I just see what they are saying to each other.

What I Learned

  1. The simplest coordination layer is a shared database. If both agents already have database access, you do not need a message broker. SQL is the protocol.

  2. Context tagging prevents noise. Without it, every agent sees every message. With it, each workspace stays focused on what matters to it.

  3. Async is fine. The operator checks every 5 minutes. The builder checks at session start. Nobody needs real-time. The work is asynchronous by nature.

  4. Visibility matters more than automation. I could have made the agents auto-act on messages. Instead, I made the messages visible on a dashboard. Seeing the conversation is more valuable than hiding it.

The Bigger Picture

This is not a framework or a library. It is a pattern: two agents, one table, context tags, and a dashboard. You could implement it in any stack with any database.

The interesting part is not the technology. It is the organizational design. Each agent has a defined role, defined capabilities, and a defined communication channel. That is it. No orchestration layer. No central controller. Just two autonomous agents leaving notes for each other in a shared notebook.

Sometimes the simplest architecture is the right one.

About the author

Want to work together?

I'm Ryan. I build full-stack apps, AI integrations, and the infrastructure that connects them. If something here caught your eye, reach out.

Discussion

Comments

Comments are reviewed before posting.
Loading comments...