3.1 KiB
Vibn Real-Time Updates Architecture
The Problem
Currently, the Vibn frontend relies on static data fetching and manual browser refreshes. Because autonomous AI agents (like the background Coder) operate headlessly via vibn-agent-runner, they mutate the Postgres database directly. The Next.js frontend has no mechanism to know when these mutations occur, leading to stale data in the UI (e.g., the Plan tab not updating when a task is checked off, or the Chat UI not streaming in new messages instantly).
The Solution: Real-Time SWR + Server-Sent Events (SSE)
To solve this holistically without introducing the overhead of WebSockets or a third-party service (like Pusher), we will implement a lightweight Server-Sent Events (SSE) architecture combined with SWR mutation.
1. The Real-Time Event Bus (Postgres LISTEN/NOTIFY)
Postgres has built-in pub/sub capabilities. When an AI agent makes a change to a project (e.g., completes a task or sends a message), we will fire a simple trigger in Postgres.
-- Example Postgres Trigger (To be created)
CREATE OR REPLACE FUNCTION notify_project_update() RETURNS TRIGGER AS $$
BEGIN
PERFORM pg_notify('project_updates', json_build_object('project_id', NEW.id, 'updated_at', NEW.data->>'updatedAt')::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
2. The SSE API Route (/api/projects/[projectId]/stream)
We will create a Next.js API route that keeps an open HTTP connection to the client. This route will subscribe to the Postgres pg_notify channel.
When the database emits an event for projectId, this route pushes a small payload to the browser: data: {"event": "plan_updated"}.
3. The Frontend Hook (useProjectStream)
We will write a global React hook that wraps SWR. When the SSE connection receives an event, it simply tells SWR to re-fetch the data in the background and update the UI instantly.
// Example Hook Concept
export function useProjectStream(projectId: string) {
useEffect(() => {
const eventSource = new EventSource(`/api/projects/${projectId}/stream`);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'plan_updated') mutate(`/api/projects/${projectId}/plan`);
if (data.type === 'chat_updated') mutate(`/api/projects/${projectId}/chat`);
};
return () => eventSource.close();
}, [projectId]);
}
Why this is the best approach for Vibn:
- No New Infrastructure: It uses your existing Postgres database and Next.js server.
- Zero Dependencies: SSE is native to HTTP and browsers. We don't need
socket.io. - Works perfectly with SWR: Instead of trying to push massive JSON payloads over the socket, the socket just acts as a "ping" to tell the client, "Hey, the data is stale, fetch the new state via your existing API routes."
Next Steps for Implementation
- Add the generic SSE API route.
- Build the
useProjectStreamhook and drop it into theapp/[workspace]/project/[projectId]/layout.tsxfile so every tab gets real-time capabilities. - Update the backend API tools (like
plan_task_completeand the newemail_send) to trigger the update events.