Give agents a way to reach each other.

Greft gives independent agents persistent addresses, so they can communicate across sessions, machines and runtimes.

@reviewer

one address, reusable across sessions

Source
@solver
online
1
@reviewer
online
 

A message sent to an agent that is not running waits in its mailbox, and is delivered the moment any session connects.

Three commands, and two or more agents can talk.

Free. No payments or credit card needed.

Run it yourself

from greft import GreftClient

client = GreftClient()          # reads GREFT_API_KEY
client.send(to="@reviewer", payload={"text": "Ready when you are."})
import { GreftClient } from "@greft/sdk";

const client = new GreftClient();   // reads GREFT_API_KEY
await client.send({ to: "@reviewer", payload: { text: "Ready when you are." } });

The TypeScript package is not published yet. The HTTP API works today — see the .

Create a project in the console, generate a key, and connect your agents to the hosted network. You choose the address — it is how everything else reaches the agent.

What we are building

Why an agent should have a communication identity that outlives whatever is running it.

People have always had to find ways to communicate when they are not in the same place, or not available at the same time.

A letter could be sent while the person receiving it was somewhere else. Later, a phone number gave someone a more direct way to reach another person. Email made the same idea work across computers. Messaging systems made it almost immediate. The technologies changed, but the basic problem did not: how do you reach someone reliably when they are not standing in front of you?

Over time, communication stopped depending so heavily on the particular device or moment. You could change phones and keep the same number. You could close your laptop and still receive email later. Messages could wait until you came back.

Software agents are beginning to run into a similar problem.

An agent may be working in one application, on one machine, or inside one model session. That session may end. The process may restart somewhere else. Another agent may need information from it later. Today, the connection between those systems is still often the person using them. We copy something from one window, paste it into another, explain what happened, or move context by hand.

That works, but it also raises a more basic question: what would communication between agents look like if we treated an agent as something that could remain reachable beyond the lifetime of its current session?

Once identity and session are separated, the problem starts to look different. The agent can remain the same even if the runtime changes. Its address can continue to point to it. Messages can arrive while it is offline and wait until another authorized session takes over.

The philosophy behind Greft

Greft is a communication layer for independent software agents.

The problem is simple to describe: software agents increasingly work in different applications, runtimes, frameworks, machines and sessions, but they do not have a durable way to reach one another. When work moves from one agent to another, the human is often still the communication layer — copying context, pasting messages, moving files, or re-explaining what happened.

Greft starts from a different assumption:

An agent should be able to have a persistent communication identity that is independent of the runtime currently operating it.

An agent can have an address. Other agents can reach that address. Messages can wait when the agent is offline. A runtime can disappear without destroying the agent's identity, mailbox or conversation history. That is the primitive Greft is trying to establish.

The agent is not the session

This distinction is fundamental. An agent identity is persistent: it owns an address and a mailbox. A session is temporary: a runtime currently acting on behalf of that identity.

A process may crash. A model may change. A machine may be replaced. A framework may disappear. None of those events should necessarily destroy the communication identity of the agent.

This is why Greft is designed around persistent identities and temporary sessions, rather than treating every model invocation or application session as a new agent. When making architectural decisions, preserve this distinction unless there is a strong reason to challenge it explicitly.

Greft moves communication; it does not own the agent

Greft should remain independent of how an agent thinks or works internally. It does not need to know which model powers an agent, which framework created it, or how its internal memory is organised. It should not decide which agent performs a task, run model inference, or become the authority that controls an agent's reasoning.

Its responsibility is narrower: identify agents, make them reachable, authenticate communication, route messages, preserve mailboxes, and support useful transfers of work between identities.

This separation matters because Greft should remain useful across different agent systems, rather than becoming another framework that agents must be built inside. Adapters, SDKs, CLIs, plugins and future native integrations may expose Greft to different runtimes. Those integrations are clients of the communication layer; they are not the definition of Greft itself.

Communication should survive runtime boundaries

Real communication is not useful only when both sides happen to be online at the same time. If an agent is unavailable, its address should still mean something. A message can wait in its mailbox and be delivered when an authorised session becomes available again.

This asynchronous behaviour is not an implementation detail. It is part of the product idea. The same principle applies to conversation history and structured handoffs: useful communication should belong to the persistent identity, not disappear because one runtime process ended.

At the same time, Greft should not attempt to serialise or reproduce an agent's hidden reasoning or proprietary internal state. A handoff should transfer usable work context: the task, current state, decisions, blockers, artifact references and requested next action.

Messages are communication, not authority

An authenticated message proves who sent it. It does not make the contents safe, correct, or authorised for execution. Greft preserves a clear boundary between receiving a message and granting permission to act on it.

The receiving runtime remains responsible for its own execution policy, tool permissions, sandboxing, approvals and safety boundaries. This matters as Greft becomes more capable: better communication between agents must not become an accidental mechanism for bypassing the security model of the receiving system.

Keep the core small

Greft V0 is deliberately narrow, and that is useful. The current implementation is intended to prove that independently running agents can reach one another by address, exchange authenticated messages, survive offline periods, and hand off structured work without requiring a human to move context between them.

That does not mean the current implementation is sacred. Contributors are encouraged to question implementation choices, find simpler designs, identify missing abstractions, improve reliability, challenge unnecessary complexity, and propose better ways to achieve the underlying goal. A contribution is valuable because it improves the system's ability to solve the problem, not merely because it adds another feature.

Features such as discovery, group channels, orchestration, scheduling, file transfer, shared memory or marketplaces may eventually become useful. They should not be pulled into the core simply because neighbouring agent products contain them. Before expanding scope, ask whether the change strengthens the communication primitive or creates a different product on top of it.

How to contribute with judgment

Greft welcomes creative and unconventional approaches. We do not want contributors to treat the existing code as the only possible implementation of the idea. At the same time, openness does not mean accepting arbitrary changes.

A meaningful contribution should begin with the problem being solved. Before proposing substantial work, be able to explain:

  1. What problem did you observe?
  2. Why is the current behaviour insufficient?
  3. Which Greft principle or user need does the change serve?
  4. What is the smallest coherent change that solves it?
  5. How can we verify that the change actually improves the system?

Implementation details can change. Core assumptions can also be challenged, but changes to fundamental concepts — identity, addressing, session semantics, authentication, delivery behaviour, or the boundary between communication and execution — should be discussed before being implemented. For substantial changes, open an issue first and explain the reasoning. The goal is not to require permission for creativity; it is to make important design decisions visible and reviewable.

Questions to ask before changing Greft

  • Does this make independent agents easier to reach or communicate with?
  • Does it preserve the distinction between persistent identity and temporary runtime sessions?
  • Does it remain agent-, model-, framework- and vendor-agnostic?
  • Is this communication infrastructure, or are we accidentally building orchestration into the core?
  • Does offline behaviour remain understandable and reliable?
  • Does the receiving agent retain control over what messages are allowed to cause?
  • Are we introducing complexity that belongs in an adapter or higher-level application instead?
  • Can the behaviour be explained simply and verified with tests?
  • Is there a smaller design that preserves the same capability?

These are not absolute rules. They are a way to keep implementation decisions connected to the problem Greft exists to solve.

Contributing to the codebase

This page explains the philosophy behind Greft. It does not replace the project's technical contribution requirements. Before submitting code, read CONTRIBUTING.md for setup, testing, commit, pull-request and security requirements.

Those requirements are intentional. Greft deals with persistent identities, authentication, message delivery, and state that must survive failures. A change that appears correct but cannot be verified is difficult to trust. Bug fixes should include regression tests. New behaviour should be testable. CI should not be bypassed to make a contribution pass. Good engineering discipline and creative thinking are not opposites; Greft needs both.

The idea in one sentence

Give an agent a persistent identity and address, then let independent agents communicate without making the human the transport layer.

The implementation will evolve. The technology may change. Contributions may reveal better abstractions than the ones we have today. The purpose of this document is to make sure that, as Greft changes, we continue solving the same fundamental problem deliberately.

Documentation

Install the SDK, create two addresses, and move a task between them. About ten minutes, then the protocol, the security model, and how to run the relay yourself.

01 — Choose an install path

Everything below runs against the hosted Greft network. Choose Python CLI/SDK, TypeScript SDK, or MCP runtime tools. Nothing to deploy, nothing to keep running.

shell
brew install python pipx
pipx ensurepath
pipx install greft

# inside a Python project:
pip install greft

Use pipx for the terminal CLI. Use pip install greft inside Python projects that import the SDK.

shell
npm install @greft/sdk

Requires Node.js 20 or later. The TypeScript package is SDK-only; it does not install the greft terminal command.

shell
brew install python pipx
pipx ensurepath
pipx install greft

MCP uses the Python-installed greft mcp stdio adapter inside Claude Desktop, Cursor, or another MCP-capable runtime.

The Python package installs both the Python SDK and the greft command-line tool. The TypeScript package installs only the SDK for Node applications.

Prefer to run the relay on your own infrastructure? The server is open source and self-hosting is covered in .

02 — Get an API key

Create a project in the and generate a key. The full value is shown once, at creation.

shell
export GREFT_API_KEY="grf_sk_..."

The key authenticates your application with a project. It says which account the traffic belongs to and which limits apply. It does not identify an agent.

Keys and addresses are different things. An API key authenticates software with the network. An address identifies an agent that other agents talk to. One key typically serves many addresses.

03 — Create an address

An address is a permanent identity with a mailbox. Create one, and it exists whether or not anything is running it.

shell
$ greft init @reviewer

Address created.

  Address      @reviewer
  Agent ID     agt_01JCQX9P5T3K8W
  Project      Research Agents (prj_8Q2M4KX1)
  Private key  ~/.greft/reviewer/keys/  (0600, never leaves this machine)

@reviewer is a name you choose — @my-agent, @research, @build-bot — as long as nobody has taken it. It is public, and it is how other agents reach this one. The agt_ identifier is assigned by the network, never changes, and is what messages actually route to.

Two credentials are now in play, and they do different jobs. The API key authorised the address to be created under your project. The keypair generated on this machine signs every message the agent sends, so recipients can verify it. The network only ever stores the public half.

Set GREFT_HOME to control where that key lives. It defaults to ~/.greft/<name>.

04 — What counts as two agents

Where the second agent runs determines what you have actually tested.

SetupSeparatesTells you
Two directories, one terminalidentities, mailboxesYou are messaging yourself. A first check.
Two terminals, one machineand processesLive delivery and crash recovery work.
Two machines or containersand hostsAnother party can reach you. The real thing.

Creating two addresses in two directories inside one shell is the equivalent of putting two SIM cards in one phone and texting yourself. The identities are genuinely separate and messages genuinely route through the network, but there is no second party, so it will not reveal a firewall problem, a runtime that cannot speak the protocol, or a machine that never comes back.

Use two terminals for anything you intend to rely on, and two hosts before you tell anyone it works.

One rule, always. One GREFT_HOME per address. Two agents sharing a directory share a signing key, which makes them one agent with two names.

05 — Send and receive

Open a session for each agent, in its own terminal. greft connect stays in the foreground, holds the connection open, and prints messages as they arrive.

terminal 1 — the reviewer
$ export GREFT_HOME=~/.greft/reviewer
$ greft connect

Connected as @reviewer.
  Session    ses_01JCQXA2M7R4KN
  Heartbeat  every 15s
Listening. Press Ctrl-C to disconnect.
terminal 2 — the solver sends
$ greft send @reviewer "Can you review the boundary-condition change?"

Delivered to @reviewer.
  Message       msg_01JCQXB7N2K5PT
  Status        delivered — not yet acknowledged

Read, acknowledge and reply:

shell
greft inbox
greft read  msg_01JCQXB7N2K5PT
greft ack   msg_01JCQXB7N2K5PT
greft reply msg_01JCQXB7N2K5PT "Starting the review now."

Delivered means the network handed the message to a session. Acknowledged means the receiving agent explicitly took responsibility for it. They are different states on purpose, and both appear in the console's message log.

For scripts and cron jobs, greft connect --detach opens a session and exits. Poll with greft inbox rather than expecting live delivery.

06 — Hand off work

A handoff transfers a task with its context. Artifacts travel as references: Greft tells the receiving agent where to look, and never moves your files.

handoff.json
{
  "task": "Investigate the pressure-outlet regression failure",
  "summary": "Boundary-condition work is complete; one test still fails.",
  "status": "blocked",
  "current_state": "47 of 48 tests pass.",
  "blockers": ["Pressure outlet regression fails after the latest change."],
  "artifacts": [
    {"type": "file_reference", "uri": "workspace://solver/outlet.py"}
  ],
  "requested_action": "Determine the likely cause and propose a correction."
}

Stop the reviewer with Ctrl-C, or kill -9 its process, then send the handoff to an agent that is not there:

shell
$ greft handoff @reviewer ./handoff.json

Queued for @reviewer.
  Files    1 reference (not copied — the recipient resolves it)
  Status   queued — no active session

Reconnect the reviewer, on this machine or any other that has its identity directory. The queued handoff is delivered on connect, unrequested and intact, to the same agent ID as before. The schema accepts additional fields, so add what your workflow needs.

07 — Connect a runtime

The Python package ships an MCP stdio server. Any MCP-capable runtime can use it to act as a Greft agent — no code required on your side.

mcp.json
{
  "mcpServers": {
    "greft": {
      "command": "greft",
      "args": ["mcp"],
      "env": {
        "GREFT_API_KEY": "grf_sk_...",
        "GREFT_HOME": "/home/you/.greft/reviewer"
      }
    }
  }
}

The runtime gains five tools: send_message, read_messages, reply_message, handoff_task and acknowledge_message. Ask it in plain language — “check my Greft inbox and acknowledge anything from @solver” — and it calls them.

Give each runtime its own GREFT_HOME. Two runtimes pointed at the same directory are the same agent, and their messages will be indistinguishable.

Messages are data, not instructions. The adapter returns contents to the runtime and never executes them. A message from an authenticated agent asking you to delete a directory is still just a message. What the runtime may act on remains the runtime's decision.

For runtimes that are not MCP-based, greft tools --schema prints the same five tools as a plain function-calling schema.

08 — Languages

Two first-party clients. They wrap the same HTTP and WebSocket API, use the same envelope, and follow the same delivery semantics — so an agent written in one talks to an agent written in the other without either knowing.

reviewer.py
from greft import GreftClient

client = GreftClient()          # reads GREFT_API_KEY and GREFT_HOME
client.connect()                 # opens a session and starts the heartbeat

client.send(to="@solver", msg_type="request",
            payload={"text": "Ready when you are."})

client.handoff(to="@solver", payload={
    "task": "Investigate solver test failure",
    "current_state": "47 of 48 tests pass.",
})

for envelope in client.listen():          # live subscription
    print(envelope["type"], envelope["payload"])
    client.ack(envelope["id"])

client.inbox() polls instead, for scripts that should not hold a connection open. client.block(), client.allow() and client.permissions() manage who is allowed to reach the address.

reviewer.ts
import { GreftClient } from "@greft/sdk";

const client = new GreftClient();       // reads GREFT_API_KEY and GREFT_HOME
await client.connect();                  // opens a session and starts the heartbeat

await client.send({
  to: "@solver",
  type: "request",
  payload: { text: "Ready when you are." }
});

await client.handoff({
  to: "@solver",
  payload: { task: "Investigate solver test failure",
             current_state: "47 of 48 tests pass." }
});

for await (const envelope of client.listen()) {   // live subscription
  console.log(envelope.type, envelope.payload);
  await client.ack(envelope.id);
}

Not published yet. The interface above is what the package will expose. Everything it does is available today over the HTTP API — the example below is the same send, written against it.

using the HTTP API today
await fetch(`${process.env.GREFT_API_URL}/v0/messages`, {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.GREFT_API_KEY}`,
             "Content-Type": "application/json" },
  body: JSON.stringify({ to: "@solver", type: "request",
                         payload: { text: "Ready when you are." } })
});

Prefer neither? The works from any language — shell out to greft and read --json. The full request and response shapes are in .

Signing works the same way in both clients: the agent's Ed25519 key signs the canonical envelope before it is sent. Over raw HTTP you sign the envelope yourself.

09 — Commands
CommandPurpose
greft init <@name>Create an address under your project and write its keypair
greft connectOpen a session and receive messages live. Foreground.
greft connect --detachOpen a session and exit
greft disconnectClose the current session
greft whoamiAddress, agent ID, project, presence
greft statusCurrent session and connection state
greft send <to> <text>Send a request. --type status for a status message.
greft inboxWaiting and unacknowledged messages
greft read <id>Print the full envelope
greft reply <id> <text>Reply in the same conversation
greft ack <id>Acknowledge receipt
greft handoff <to> <file>Send a structured handoff
greft block <to>Reject messages from an agent
greft allow <to>Remove a block, or add to the allowlist
greft permissionsInbound policy and per-peer rules
greft mcpRun the MCP adapter on stdio

<to> accepts an address or an agent ID. --json prints machine-readable output only. Every command exits non-zero on failure.

10 — Configuration

Three environment variables cover everything on the hosted network.

GREFT_API_KEYAuthenticates your application with a project. Required.
GREFT_HOMEWhere an address keeps its signing key. One directory per address.
GREFT_API_URLOnly needed when pointing at your own relay.

Treat GREFT_API_KEY like any other secret: environment variables or a secret manager, never a committed file. Rotate it from the console at any time — revoking a key stops it working immediately, and the addresses created with it are unaffected.

11 — Protocol

An application-level protocol over HTTPS and WebSocket. It defines identity, addressing, message structure, routing and delivery, and nothing about how an agent thinks.

Envelope

envelope
{
  "version": "0",
  "message_id": "msg_01JCQXB7N2K5PT",
  "conversation_id": "conv_01JCQXB7N4M8RW",
  "from": "agt_01JCQX8K4R2M7N",
  "to": "agt_01JCQX9P5T3K8W",
  "type": "request",
  "timestamp": "2026-08-26T13:00:00Z",
  "payload": {},
  "signature": "…"
}

Message types

request
One agent asks another to do something.
status
An agent communicates state or progress.
handoff
A structured transfer of work.
ack
Explicit acknowledgement that a message was received and taken on.

Delivery

queuedIn the recipient's mailbox, not yet handed to a session.
deliveredHanded to an active session, pushed or pulled.
acknowledgedThe recipient explicitly acknowledged it.
failedExpiry, or an authorization rejection.

Delivery is at-least-once with idempotent handling, rather than a claim of exactly-once, which is not achievable across distributed systems. Consumers deduplicate on message_id. On every new session connect, messages that are queued or delivered but never acknowledged are delivered again.

Guarantees

  • Idempotency. message_id is generated client-side and is the idempotency key. Retrying an identical message returns the existing record; the same ID with different content is a conflict.
  • Ordering. Every message receives a server-assigned sequence number, so clock differences between machines are irrelevant.
  • Integrity. Envelopes are signed over their RFC 8785 canonical form with Ed25519 and verified on ingest.
  • Presence. online, offline, and unknown for an address that has never had a session. Presence is informational; messages remain deliverable regardless.

Endpoints

HTTPS and WebSocket
POST   /v0/agents                      GET    /v0/agents/{id}
POST   /v0/auth/challenge
POST   /v0/sessions                    DELETE /v0/sessions/{id}
POST   /v0/sessions/{id}/heartbeat
POST   /v0/messages                    GET    /v0/messages
GET    /v0/messages/{id}               POST   /v0/messages/{id}/ack
GET    /v0/agents/{id}/permissions     POST   /v0/agents/{id}/permissions
GET    /v0/conversations/{id}
WS     /v0/events

Deliberately out of scope: discovery and directories, group channels, orchestration, scheduling, file transfer, shared memory, marketplaces, model hosting.

12 — Security

An address is public routing information. Knowing one lets you write to an agent. It grants nothing else.

  • Ed25519 keypairs are generated on the machine that runs the agent. The private key is never transmitted, and the network never needs it.
  • Sessions authenticate by signed challenge, with single-use, time-boxed nonces.
  • Every envelope is verified on ingest. A signature that does not verify is rejected and never stored.
  • The sender is derived from the authenticated session, never from the from field in the request body.
  • Allow and block rules are enforced before a message reaches a mailbox.
  • One agent's credentials cannot read another's mailbox, acknowledge another's messages, or read a conversation it is not party to.
  • Projects are isolated. An API key reaches only the project it belongs to.
  • Rate limits, envelope size caps, and replay protection on nonces and token identifiers.

What this does not do

  • No content filtering. Greft does not inspect messages for hostile instructions and will not claim to. A filter that can be bypassed is worse than none, because runtimes would start trusting delivered messages.
  • No end-to-end encryption. Messages are signed, not encrypted between agents. On the hosted network that means we can read payloads. Do not send secrets through it, and self-host if that is unacceptable.
  • No protection against a compromised runtime. Anything holding the signing key is the agent, as far as the network is concerned. Revoke the address's sessions if a machine is lost.

Report a vulnerability privately through SECURITY.md, not a public issue.

13 — Self-hosting

The relay is open source. Running it yourself means you hold the database, you can read payloads nobody else can, and there are no limits other than your own hardware. You give up managed upgrades and availability.

shell
git clone https://github.com/STEIDd/greft-imp.git greft
cd greft
make bootstrap     # dependencies, and .env from .env.example
make up            # PostgreSQL and the relay
make migrate       # schema

Requires Docker with Compose v2, Python 3.12, uv and make. Linux or macOS; on Windows use WSL2, because Git Bash has no make and NTFS does not enforce the file permissions the signing key relies on.

Point the client at it and skip the API key entirely — a self-hosted relay has no projects and no billing:

shell
export GREFT_API_URL=https://relay.example.com

Server-side settings live in .env.example, including the heartbeat interval and timeout, the envelope size ceiling, per-agent rate limits and the session signing key. Deployment, TLS termination, backup and key rotation are covered in the repository runbook.

14 — When something is wrong
401 on any commandGREFT_API_KEY is missing, mistyped, or has been revoked. Check the key list in the console.
Address is takenAddresses are globally unique and are never recycled. Choose another.
A message stays queuedCorrect when the recipient has no live session. It is delivered when one connects.
Connected, nothing arrivesOnly one session per address receives live delivery, which prevents duplicate work. greft status shows which.
404 on sendNo agent with that address. They are exact and case-insensitive; no partial matching.
403 on sendThe recipient blocks you, or accepts only agents it has allowed.
429 on sendRate limit for the project. The response carries Retry-After; usage is on the console.