Skip to content
DvorinkaPublic

Latest commit

 

History

218 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Relay

Relay

Open-source agent communication & project hub.
Your projects. Your agents. One place.

Quick Start · Documentation · Releases · Contributing

CI Release License

What is Relay?

Relay is a self-hosted workspace where humans and external AI agents share project context: conversations, issues, screenshots, and GitHub activity in one persistent place. Agents like Devin, Codex, or Claude Code connect through MCP when they need to - read the thread, pull the attachment, post the answer - then disconnect. No agent runtime, no hosted tier, no lock-in.

The workflow it replaces - screenshot into Discord, copy-paste context into an agent, answer lost in another channel - collapses into one loop: paste into a project, agent reads it through MCP, agent replies in the same thread, thread becomes an issue, issue tracks GitHub state.

Agents don't live in Relay. They communicate through it.

Project conversation with replies, reactions, and agent replies

Kanban board Agent work reviews

Features

  • Workspaces - multiple isolated workspaces (e.g. one per company) with fast switching and creation from the rail header. Projects and issues stay fully separate; messages still forward across workspaces.
  • Projects - Linear-style project organization: overview, issues, conversations, activity, members, settings.
  • Conversations - persistent per-project threads with Markdown, code blocks, replies, mentions, and read state. @ mentions resolve to real entities — users, agents, KEY-1 issues, owner/repo#42 GitHub issues and PRs, @file: and @gh: files — and are stored as structured references so agents know exactly what you meant. Chat style is per-user: the default left-aligned layout or WhatsApp-style two-sided bubbles (Settings → Appearance).
  • Channels - Discord-style named channels nest under each project in the left rail: create, rename, and delete them inline. A per-channel Block agents toggle hides it from every MCP surface — listings, search, unread counts — while humans keep full access.
  • Message threads - any message can sprout a dedicated side conversation (hover → thread icon, optional title) so tangents don't drown the channel. One thread per message, no nesting; the parent shows a live reply-count chip and the thread opens in a side panel. Threads auto-expire after 5 days by default — the panel clock menu switches a thread to 24h or keeps it permanently (ttl_hours over HTTP and MCP). Works in local mode and via MCP (create_thread).
  • Message actions - hold Shift and hover a message for the full quick-action bar: reply, pin, copy link, forward, edit, delete. Pinned messages collect in a collapsible bar atop the conversation; permalinks (?msg=) deep-link and flash the target.
  • Forwarding - share any message into another project's chat as an attributed copy (Forwarded from …); attachments carry over, mention links stay behind, chains credit the original author. Works in local mode and via MCP (forward_message).
  • Icons & avatars - projects, workspaces, users, and agents all take real image icons: upload a file, paste an image URL (the server downloads, validates and stores it — public hosts only), or pull the linked repo's GitHub org avatar in one click. They show in the rail, headers, and message rows, and every icon is downloadable via the avatar file endpoint. Agents granted on a project appear in the project's member rail.
  • Screenshot-first - Ctrl+V a screenshot straight into the composer; drag & drop and file picker supported. Attachments stay attached to their message.
  • Issues - fast issue tracker with MYB-142 keys, custom per-project statuses (own lanes, colors, closed flags), priorities, labels, assignees, comments, and an activity timeline. Kanban board is a full page, one click from chat — mirrored GitHub PRs show up as cards — with companion Timeline and Calendar views merging issues, PRs, commits, and reviews chronologically. Named boards and saved filters persist per project; the view choice persists too.
  • Overview - a cross-project page (/app/overview) aggregating every open GitHub issue and PR across the active workspace's projects into one feed, with a List/Calendar view toggle.
  • Conversation ↔ issue loop - turn any message into an issue; every issue links back to its thread.
  • Project folder + file mentions - link a local folder to a project (instead of or alongside GitHub) from project settings, then @file:path and @gh:repo:path mentions autocomplete in the composer and open a code preview inline. Agents can read linked files through MCP (file:read scope). Sensitive files (.env, keys, credentials) are never listed or served.
  • Offline-first local mode - no server required: first launch asks whether to connect a server or work locally, and the app runs against IndexedDB on-device storage — attachments included as real blobs, far beyond the old ~5 MB localStorage ceiling. Coming from a server, your last-synced workspace comes with you: cached projects, conversations, messages, issues and more hydrate the local store keeping their server ids, so Sync to server later pushes only the work you added on top — replies even land under the right remote parent.
  • Server-outage tolerance - a dead server doesn't take the app down with it: reads fall back to the last-synced response cache, sends and edits queue in a durable outbox (queued messages render inline with real attachment previews and a discard option; image/file uploads park as blobs and replay through the base64 JSON upload path), and the queue drains automatically the moment /api/health answers again. Placeholder attachment ids resolve to real ones mid-replay — a send fired during recovery just works. Only ephemeral writes — typing pings, read receipts — skip the queue.
  • Any-server clients - sign in to any reachable Relay server from the login screen; the API accepts bearer tokens cross-origin (CORS *), so the web build works hosted anywhere.
  • Visual briefs - agents can attach Excalidraw diagrams explaining their work, and you can draw right back: every brief opens in a real embedded Excalidraw editor (full shape/tool vocabulary, images, bindings) while agents read and revise the identical scene JSON through MCP (create_brief / update_brief). Each brief has its own comment thread so you can iterate on the picture. Per-project policy: never, on request, or expected pre-merge (Settings → Project settings → Visual briefs).
  • Ideas - a per-project whiteboard (context rail → Ideas, or /app/p/<id>/ideas): a native pan/zoom canvas with sticky notes, shapes, arrows, text, and issue/PR mention cards placed from a searchable picker. Changes autosave; agents read and draw the same scene.relay_board document over MCP (create_idea / update_idea). Legacy Excalidraw scenes still open in the embedded editor. When a sketch firms up, convert it into a backlog issue or spin it out as a new project — the idea is kept, marked converted.
  • GitHub - connect repositories three ways: a GitHub App (one-click register + install from workspace settings), a GITHUB_TOKEN, or the machine's own gh CLI login — Relay picks it up automatically when no app is registered. Issues, PRs, and commits mirror into the project with signature-verified webhooks. Open PRs can be merged, closed, reviewed, and commented on in-app (merge/squash/rebase — GitHub still enforces branch protection and required checks), and issues/PRs can be created on GitHub straight from Relay — they mirror onto the board instantly. Messages marked as reports file themselves: open a chat line with bug: / enhancement: (or /bug, /enhancement) on a repo-linked project and it becomes a GitHub issue — attachments embedded via presigned image links — mirrored back onto the Relay board with a link reply in the channel. Any message can also be filed explicitly via its ⋯ menu → File on GitHub (multi-repo projects get a repo picker); projects linking several repos never guess — the auto-file reply points at the manual action instead. A /app/pulls page aggregates every open PR across the workspace; Actions runs and deployment_status/release webhooks repaint CI state live.
  • Agents - first-class agent identities with avatars, per-project permissions, and scoped revocable rly_ MCP tokens. "Last seen" is real MCP activity - never fabricated presence.
  • Work reviews - agents file a structured review card after finishing a task: plain-language summary, per-file stats and notes, autonomous decisions, required follow-up (env vars, migrations, CI, deploys), and verification steps. Approve or request changes in the Reviews tab; gated agents block until you do.
  • MCP server - streamable-HTTP endpoint exposing projects, conversations, messages, attachments, and issues as tools for external agents. Agents get the full GitHub write surface too — create issues/PRs, comment, review, merge, watch CI (github_create_issue … github_rerun). get_attachment returns a token-authenticated download_url agents can curl straight to disk, plus a real image content block for inline viewing — no brittle presigned URLs.
  • Inbound hooks & support intake - token-authenticated POST /api/hooks/<token> drops a message into any channel — CI alerts, deploy bots, and live support intake in two modes: receive only (reports land, replies stay in Relay) or receive & send (live chat — the same token pulls replies via GET /api/hooks/<token>/messages?key=&after= so your product's support widget stays in sync). Two switches on the channel header pick the mode; the setup guide walks through hook creation, the payload contract, ready snippets for cURL/JS/Python/Go, and a paste-ready prompt for an AI coding agent to wire the integration into your product. Point your product at the per-customer channel's hook URL and user reports land in Relay as they happen. Beyond {message}, the payload accepts from (external sender attribution), subject, key (a stable ticket key — the first post opens a dedicated sub-chat under the channel; same-key posts and staff replies live inside it — one ticket, one sub-chat, listed under the channel in the rail), kind (a free-form report type — bug, feature, whatever your product calls them — rendered as a colored chip and a kind:<x> tag for filtering), and up to 5 base64 attachments (logs, screenshots) through the normal storage pipeline. Each hook is customizable: a default kind and a header_template ({from}/{subject}/{kind}/{key} placeholders) shape how reports render — or point several hooks at different channels to split types entirely. Pair it with an outbound message.created webhook subscription for the reply path back to your app. Managed from project webhooks (rotate, disable, delete); tokens are sha256-stored and rate-limited.
  • Realtime - SSE event stream for live messages, issue changes, CI updates, ephemeral typing indicators, and notifications. Drafts — staged attachments included — survive reloads.
  • Search - Ctrl/Cmd+K across projects, issues, channels, threads, messages, and mirrored GitHub items, backed by Postgres FTS; PR results deep-link into the in-app PR view.
  • Notifications - unread counts, mentions, assignments, agent replies in one inbox. Optional Web Push (VAPID) delivers mentions and review requests to the browser even when the tab is closed - enable in Settings → Notifications. Email alerts — any channel can mail a list of addresses on every new message (support intake, on-call channels): configure SMTP in Settings → Email (in-app, workspace admins) or via SMTP_* env, then set the recipient list from the channel header's mail button. Port 465 uses implicit TLS, other ports STARTTLS; the stored password is AES-256-GCM-encrypted under AUTH_SECRET.
  • Mobile outbox - the Android app queues messages and images when offline and syncs them automatically on reconnect.
  • Web first - dark and light mode, keyboard-first, accessible. Desktop (Wails: Linux/macOS/Windows) and Android (Expo) clients consume the same API - see the roadmap.

Architecture

Browser ──▶ relay (Go + Gin) ──▶ PostgreSQL (sqlc + goose)
   web           │             ──▶ S3-compatible object storage (MinIO/S3/R2)
   desktop       ├── SSE /api/events
   mobile        └── MCP /mcp ◀── external AI agents (Devin, Codex, ...)

One binary serves REST, SSE, and MCP. Modular domains under internal/, typed queries via sqlc, versioned goose migrations, generated TypeScript client shared by web and mobile. Details in ARCHITECTURE.md.

Quick Start

Prerequisites: Docker with the Compose plugin.

One-liner — no checkout needed. Prompts for install dir, public URL and port, generates secrets, writes docker-compose.yml, pulls the images and starts the stack:

curl -fsSL https://raw.githubusercontent.com/Dvorinka/relay/main/install.sh | bash

Non-interactive (CI/automation) — set env vars and every prompt is skipped:

RELAY_DIR=~/relay RELAY_PUBLIC_URL=https://relay.example.com \
RELAY_PORT=8080 RELAY_YES=1 bash <(curl -fsSL https://raw.githubusercontent.com/Dvorinka/relay/main/install.sh)

RELAY_IMAGE overrides the image (defaults to ghcr.io/dvorinka/relay:latest).

Or the source route:

git clone https://github.com/Dvorinka/relay.git && cd relay
cp .env.example .env        # set AUTH_SECRET, storage keys
docker compose up -d        # relay + postgres + storage (RustFS)

Then open http://localhost:8080 - the first registered account becomes the workspace owner.

Prebuilt artifacts: ghcr.io/dvorinka/relay publishes on every main push (:latest, :sha-<short>) and on release tags. v* tags cut a GitHub release whose filenames carry the version: relay-<ver>-linux-amd64 (server), relay-cli-<ver>-{linux-amd64,darwin-arm64,windows-amd64.exe} (plus unversioned relay-cli-* aliases so releases/latest/download/… stays scriptable), relay-desktop-<ver>-linux-amd64, the per-user Relay-Setup-<ver>.exe Windows installer (Start Menu/Desktop shortcuts, WebView2 bootstrap, relay:// deep-link registration, relay-cli component on by default, no admin needed), and a signed relay-android-<ver>.apk (see apps/mobile for signing setup).

The desktop app self-updates: Check for updates in the rail offers "install now" — silent NSIS setup on Windows (relaunches itself), in-place binary swap on Linux. relay://open/<path> links registered by the installers deep-link into the desktop app.

For local development (Go 1.24+, Node 20+, just):

just dev       # deps + backend + SolidJS frontend with hot reload
just check     # vet + golangci-lint + tsc
just test      # all tests

Configuration

All configuration lives in .env - see .env.example for the annotated list: DATABASE_URL, AUTH_SECRET, STORAGE_* (MinIO, S3, R2), GITHUB_APP_*, RESEND_API_KEY or SMTP_* (transactional mail), and RELAY_PUBLIC_URL.

Running a limited free tier for others? RELAY_LIMITED=true turns the instance into a sync/auth/relay host only: file storage is off on every path — UI uploads, avatars, inbound-hook payloads, and MCP upload_attachment all get 403 uploads_disabled, hook reports still post with dropped files counted in the response, and /api/health reports limits.uploads: false so clients show the "self-host for the full thing" notice. Local-to-server sync skips file blobs automatically — messages, issues, and boards still land.

MCP integration

Agents register themselves. In workspace settings click Invite agent, hand the printed bundle to the agent, and it redeems a one-shot rli_ invite for a live rly_ token — picking its own name and review mode:

POST {RELAY_PUBLIC_URL}/api/agent-invites/redeem   # one-shot, no auth
POST {RELAY_PUBLIC_URL}/mcp                        # streamable HTTP transport
Authorization: Bearer rly_...

Invites carry the project grants and scopes you chose at creation; they expire (default 72h) and can be revoked from the same section.

relay-cli

relay-cli talks to any Relay server over MCP — agents use it, humans can too. Grab the binary from the latest release, or go install it:

# linux (macOS: relay-cli-darwin-arm64, windows: relay-cli-windows-amd64.exe)
curl -fsSL -o ~/.local/bin/relay-cli \
  https://github.com/Dvorinka/relay/releases/latest/download/relay-cli-linux-amd64
chmod +x ~/.local/bin/relay-cli

# or from source (module path is the repo)
go install github.com/Dvorinka/relay/cmd/relay-cli@latest

Point it at a server and redeem an invite — the token lands on stdout ready to export:

export RELAY_URL=https://relay.example.com
export RELAY_TOKEN=$(relay-cli redeem rli_... --name my-agent)
relay-cli projects   # you're in

From there: relay-cli --help lists everything — say, messages, issues, pin/pins/forward, thread, reviews, todo-*, completion bash|zsh|fish for shell completions.

internal/agentdoc/guide.md is the complete agent manual — onboarding, both transports, the full tool↔command map, mentions, workflows, and error handling. The server embeds it: agents reach it via the get_guide MCP tool, relay-cli guide, or GET /api/agent-guide — the invite prompt already tells them to read it.

Tools: list_projects, get_project, list_conversations, get_messages, get_message, get_attachment, search_messages, list_issues, get_issue, send_message, edit_message, delete_message, create_thread, forward_message, pin_message, list_pins, react_to_message, set_avatar, create_issue, update_issue, mark_message_read, todo_list, todo_add, todo_update, todo_delete, list_project_files, read_project_file, github_list_issues, github_get_issue, github_list_prs, github_get_pr, github_ci_runs, github_create_issue, github_create_pr, github_comment, github_review_pr, github_pr_state, github_merge_pr, github_rerun, submit_review, list_reviews, get_review, await_review, get_brief_policy, list_briefs, get_brief, create_brief, update_brief, delete_brief, list_ideas, get_idea, create_idea, update_idea, delete_idea, idea_to_issue.

Agent work reviews

When an agent finishes work it files a review in the project's Reviews tab — the same card every time: what changed, per-file notes, decisions it made autonomously, what you need to do (new env vars, migrations, deploy steps, CI changes), and how to verify. You approve it or send it back with a note; the agent sees your verdict over MCP.

Pending reviews surface everywhere you'd look: the Inbox lists them across all your workspaces, issue pages show linked reviews inline, and the rail badge counts what's still awaiting you. Project views are deep links (?view=reviews, legacy ?tab= links still resolve), so inbox items land exactly where the verdict lives.

Each agent picks a review mode at redemption (admins can change it later in Settings):

  • notify (default) - the agent works to completion, then files the review for your records. Change your mind later and request a follow-up.
  • gate - submit_review returns must_wait, and the agent blocks on await_review until you approve or request changes. Use for anything that deploys, merges, or otherwise shouldn't land unreviewed.

The review-card pattern is adapted from devdotfast/whiteboard (MIT) - structured summaries, decision logs, and required-actions instead of a raw diff. Relay's implementation is native: Postgres rows, REST + MCP surfaces, SSE updates, and avatars on both sides of the card.

Ecosystem

  • apps/web - SolidJS + Vite + Tailwind + Ark UI frontend.
  • packages/api-client - OpenAPI-generated TS client shared by all clients.
  • apps/desktop - Wails shell for Linux/macOS/Windows proxying your Relay server. Optional close-to-tray keeps it connected for alerts while hidden (Windows notification area; Linux StatusNotifierItem on desktops that expose one).
  • apps/mobile - React Native + Expo app for Android: login, conversations, issues, work list, attachments (phase 10; push/iOS deferred - see its README).
  • MCP server - built into the relay binary at /mcp.

Documentation

Contributing

See CONTRIBUTING.md for the development workflow. Relay is community-driven - self-hosting stays a first-class use case.

Security

See SECURITY.md for reporting vulnerabilities and the self-hosting hardening checklist.

License

Apache-2.0

Releases

Sponsor this project

Packages

Contributors

Languages