Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

379 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Relay

Relay is a local-first, AI-friendly task manager. AI and humans "relay" tasks between each other, and the CLI/MCP interfaces are first-class — equal to any GUI.

  • Local-first. Everything runs offline against a single SQLite-compatible database in your data directory. No account, and no Relay-operated server.
  • AI-friendly. The MCP server exposes the same capabilities as the CLI, so an agent can create, search, update, and complete tasks programmatically.
  • Zero-config. Works immediately after install.
  • Optional sync. Off by default. Opt in to sync with infrastructure you own — S3-compatible storage (R2/S3/MinIO) or a libSQL remote (Turso/sqld). See Sync.

Status: Phase 0 (MVP) — Rust CLI + TUI + MCP server on macOS. License: MIT.

Relay desktop GUI — task list grouped by status

Install / Build

cargo build --workspace            # build all crates
cargo run -p relay-cli -- <args>   # run a CLI command

The binary is rly.

Usage

rly add "Write the report" --priority high   # create a task
rly list                                      # list active tasks
rly search "report"                           # full-text search
rly move <id> --status in_progress            # change status
rly done <id>                                 # complete a task
rly tui                                        # launch the TUI
rly serve                                      # start the MCP server (stdio)

Run rly --help for the full command set (projects, tags, comments, links, dependencies, recurrence, export/import).

MCP server

Relay speaks the Model Context Protocol, so agents (Claude Code, Claude Desktop, etc.) get the same capabilities as the CLI. There are two transports — pick based on how many clients share one database.

Single client — stdio

The simplest setup. The MCP client launches its own rly serve process. Add to your MCP client config (e.g. Claude Code ~/.claude.json / Claude Desktop claude_desktop_config.json):

{
  "mcpServers": {
    "relay": { "type": "stdio", "command": "rly", "args": ["serve"] }
  }
}

Multiple clients — shared HTTP daemon

The default turso backend is single-process: it locks the database file, so only one rly serve can hold it at a time. If you open Relay from several projects/windows at once (each spawning its own stdio server), all but the first fail to connect. Run one shared daemon instead and point every client at it over HTTP:

rly serve --http            # listens on http://127.0.0.1:7777/mcp (loopback only)
# rly serve --http --port 8000   # or set `mcp_http_port` in config.toml
{
  "mcpServers": {
    "relay": { "type": "http", "url": "http://127.0.0.1:7777/mcp" }
  }
}

One daemon holds a single database connection and serves all clients concurrently. It binds to loopback only and rejects non-loopback Host headers, so no authentication is needed for local use.

To keep the daemon running, register it with your OS service manager:

macOS — Relay.app (recommended)

macos/install.sh builds a release Relay.app, bundles the rly binary inside it (Relay.app/Contents/MacOS/rly), installs the app to /Applications, and registers a launchd LaunchAgent that supervises the daemon from that bundled binary:

./macos/install.sh

The bundled path is stable across cargo clean / rebuilds in this checkout, so the daemon (and every client's HTTP connection) survives day-to-day development. The app's Setup window can re-detect the binary and toggle the daemon too.

macOS — launchd LaunchAgent (manual)

~/Library/LaunchAgents/com.example.relay-mcp.plist (replace the binary path with your rly, e.g. the output of which rly):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
  <key>Label</key><string>com.example.relay-mcp</string>
  <key>ProgramArguments</key>
  <array><string>/usr/local/bin/rly</string><string>serve</string><string>--http</string></array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
</dict></plist>
launchctl load ~/Library/LaunchAgents/com.example.relay-mcp.plist
Linux — systemd user service

~/.config/systemd/user/relay-mcp.service:

[Unit]
Description=Relay shared MCP daemon
[Service]
ExecStart=%h/.cargo/bin/rly serve --http
Restart=always
[Install]
WantedBy=default.target
systemctl --user enable --now relay-mcp.service

Desktop GUI (gpui)

The native macOS desktop app (gui/ crate, binary relay-gui) is built with gpui and gpui-component. It is a 3-pane Linear-style client (sidebar / virtualized task list / detail) that drives data entirely through the shared rly serve --http daemon over MCP.

# Run a daemon first (or install the LaunchAgent):
rly serve --http

# Dev run (debug build):
./gui/run.sh

# Build a distributable Relay.app (bundles the rly binary inside, ad-hoc signed):
./gui/bundle.sh            # -> target/Relay.app
./gui/bundle.sh /Applications

# Point at a non-default daemon:
RELAY_MCP_ENDPOINT=http://127.0.0.1:7777/mcp ./gui/run.sh

Requires Rust 1.95 (pinned in rust-toolchain.toml) and, on macOS, the Metal Toolchain component (xcodebuild -downloadComponent MetalToolchain). Full design and the phased build plan are in docs/GUI_GPUI.md.

Note on macos/ (SwiftUI): the original SwiftUI desktop app under macos/ is the predecessor of gui/ and is being superseded by the gpui app. It still builds and runs; it will be removed once the gpui app reaches full parity.

Screens

The desktop GUI is a three-pane, Linear-style shell:

  • Left — sidebar: built-in views (All Tasks, Inbox, Review, Completed) and your projects, each with a live count.
  • Center — work area: the task List (grouped by status) or Board (Kanban), switched from the top-right toggle.
  • Right / overlay — detail: the selected task, or a project overview.
  • Title bar: a global search + command bar (⌘K) and New task (⌘N), available from anywhere. A status dot shows the live connection to the daemon.

Every screenshot below runs against a throwaway sample database.

Task list — all tasks grouped by status, with priority, tags, due dates, and AI · Needs review badges for work an agent finished and handed back.

Task list

Board — the same tasks as a Kanban board, one column per status (Backlog → Todo → In Progress → Review → Completed).

Board view

Task detail
Task detail — status, priority, assignee, due date and project at a glance, with a markdown description, tags, and comments.
New task composer
New task — quick composer. Flip Hand off to Relay AI and an agent drafts the change and opens a PR, landing back in Review for your sign-off.
Command palette
Command palette (⌘K) — jump to any view, task, or project, or run a command by typing.
Project overview
Project overview — burn-up chart, status, milestones, documents, and updates, plus an Issues tab for the project's tasks.
Settings
Settings — appearance/theme, global quick-capture hotkeys, database backend, MCP client, and coding-tool integration.

Storage backends

Relay stores data in ~/Library/Application Support/relay/ (macOS) / $XDG_DATA_HOME/relay/ (Linux). Two backends are available, selected by the RELAY_STORAGE_BACKEND environment variable:

Backend Default DB file Full-text search
turso (Rust) ✅ yes tasks.turso.db Tantivy (ngram)
rusqlite no tasks.db SQLite FTS5

turso is the default (a pure-Rust, SQLite-compatible engine; currently BETA). rusqlite remains as a stable escape hatch. Because the two engines use incompatible on-disk FTS, each backend keeps its own database file and never touches the other. To move data from a legacy rusqlite database into turso:

RELAY_STORAGE_BACKEND=rusqlite rly export --output backup.json
rly import backup.json

Optional sync

Relay is local-only until you opt in. Add a [sync] section to config.toml pointing at storage you control, then:

rly sync status     # dry-run: what would transfer
rly sync push       # upload local changes
rly sync pull       # download remote changes

Two providers, selected at runtime (no rebuild):

  • S3-compatible (R2 / AWS S3 / MinIO) — diff-based object sync with 3-way conflict detection (local / remote / newer / interactive).
  • libSQL (Turso Cloud / self-hosted sqld) — row-level sync via turso.

Secrets come from the environment (RELAY_S3_ACCESS_KEY_ID, RELAY_S3_SECRET_ACCESS_KEY, RELAY_LIBSQL_AUTH_TOKEN). Full setup, the conflict model, and limitations are in docs/SYNC.md.

Architecture

Presentation:  CLI (clap) / TUI (ratatui) / MCP server (rmcp; stdio or shared HTTP)
               Desktop GUI (gpui → MCP/HTTP client; gui/ crate)
Service:       Task / Project / Tag / Comment / Link services (pure CRUD)
Storage:       Storage trait → RusqliteStorage | TursoStorage
Database:      SQLite-compatible (turso default, rusqlite fallback)

The desktop GUI is a separate MCP client: it never opens the database, it connects to the shared rly serve --http daemon over MCP — the same daemon the CLI and Claude Code use. See Desktop GUI and docs/GUI_GPUI.md.

Entity timestamps (created_at / updated_at / deleted_at / completed_at) are RFC3339 (UTC) strings; deletes are logical (deleted_at set, never physical).

See docs/SPEC.md for the full specification.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages