Relayforge
Relayforge routes chat channels to agent targets.
The first provider is Discord. The first targets are a Codex app-server binding and a Codex JSONL fallback inbox. The package is Python and PyPI-ready, with provider-neutral core types so Slack, WhatsApp, and other agent APIs can be added later.
Install
After the package is published:
pip install relayforge
The installed CLI command is relayforge.
Installed wheels add a small managed Relayforge block to:
~/.codex/AGENTS.md~/.claude/CLAUDE.md
Set RELAYFORGE_SKIP_AGENT_GUIDES=1 before starting Python with the installed
package on sys.path to skip this.
From this checkout:
python -m venv .venv
. .venv/bin/activate
pip install -e '.[dev]'
Configuration
{
"channels": [
{
"name": "rohan-codex",
"provider": "discord",
"id": "1520170530148192466",
"owner": "rohan"
}
],
"bindings": [
{
"name": "rohan-almanac-main",
"source": "rohan-codex",
"target": "codex-app-server",
"codexThreadId": "replace-with-codex-thread-id",
"cwd": "/Users/rohan/Desktop/Projects/almanac",
"machine": "replace-with-hostname",
"transport": "codex app-server --config mcp_servers={} --listen stdio://"
}
],
"routes": [
{
"channel": "rohan-codex",
"target": "codex-jsonl",
"inboxPath": ".relay/rohan-codex-inbox.jsonl"
}
]
}
Each founder can use the same Discord bot token with a different named channel. A binding delivers to that founder's Codex thread on the right machine. A route keeps JSONL fallback delivery available when the direct target fails.
See docs/channels.md for the multi-founder pattern. See docs/research/codex-thread-routing-2026-06-26.md for the Codex thread binding direction.
Commands
export DISCORD_BOT_TOKEN=...
relayforge init --config relay.config.json
relayforge channels add discord/rohan-codex --id 1520...
relayforge connect discord/rohan-codex --thread-id 019f... --fallback-inbox .relay/rohan-codex-inbox.jsonl
relayforge check --config relay.config.json
relayforge status --config relay.config.json
relayforge listen --config relay.config.json
relayforge reply "I am looking at it now."
relayforge send --channel rohan-codex --message "hello"
relayforge read --channel rohan-codex --limit 10
Raw Discord channel IDs work for read and send even before a config file
exists. Named channels require the config.
The listen command uses Discord Gateway events. That means Discord pushes new
messages to this process instead of the process polling the channel.
connect is the normal agent setup command. It creates a Codex app-server
binding from a provider channel such as discord/rohan-codex to one Codex
thread. When listen receives a message for that binding, Relayforge tries to
steer the active Codex turn first; if the thread is idle, it starts a new turn.
Use these commands when you need the lower-level surface:
relayforge check --config relay.config.json --json
relayforge channels --config relay.config.json
relayforge routes --config relay.config.json
relayforge bindings --config relay.config.json
relayforge channel-set --name rohan-codex --provider discord --id 1520...
relayforge route-set --channel rohan-codex --target codex-jsonl --inbox-path .relay/rohan-codex-inbox.jsonl
relayforge binding-set --name rohan-almanac-main --source rohan-codex --target codex-app-server --codex-thread-id 019f... --cwd /Users/rohan/Desktop/Projects/almanac --transport "codex app-server --config mcp_servers={} --listen stdio://"
relayforge codex-smoke --binding rohan-almanac-main --message "smoke test"
relayforge binding-send --binding rohan-almanac-main --message "reply in Discord"
relayforge inbox --path .relay/rohan-codex-inbox.jsonl --limit 10
relayforge inbox --path .relay/rohan-codex-inbox.jsonl --cursor .relay/rohan.cursor.json --ack
By default, Discord bot-authored messages are ignored to avoid loops. Use
--include-bots only for controlled smoke tests or deliberate bot-to-agent
routing.
codex-smoke sends a synthetic message through a configured Codex app-server
binding. Use it only with a rollout-backed Codex thread that is safe to receive
a test turn.
Metadata
Release files for relayforge 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| relayforge-0.1.1.tar.gz | 87.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| relayforge-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 113.5 kB
Release files / relayforge-0.1.1.tar.gz
| Download URL | relayforge-0.1.1.tar.gz |
|---|---|
| Size | 87.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
31b2326c8ecce7c3538f288d4eadeb21971fc21d13a95373ff2bba778ec8542a
|
|
BLAKE2b-256 checksum How to use checksums |
28dcb841d8e3dc71788e3dc5ec225258df8f634761fc4f6ccc1a93b325f33efa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / relayforge-0.1.1-py3-none-any.whl
| Download URL | relayforge-0.1.1-py3-none-any.whl |
|---|---|
| Size | 26.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6ae5a2ad74acf21002f343b5dfc6214f603d8de552eb20f1c772f63037c9b241
|
|
BLAKE2b-256 checksum How to use checksums |
b743c9d70d6fc8f23430c9ffa1c3d90192fc5f16afdcd83d4da60ddbcb1b9f1f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|