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 · Install the SDK

Everything below runs against the hosted Greft network. You need Python 3.10 or later. Nothing to deploy, nothing to keep running.

shell
pip install greft
shell
npm install @greft/sdk

The package installs both the Python SDK and the greft command-line tool. They are the same client. The CLI is a thin wrapper over the SDK, so anything you can do in one you can do in the other.

02 · Get an API key

Create a project in the console 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. Use @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

Greft ships two ways to expose an agent to an MCP-capable runtime. Each Greft address is a distinct MCP identity: one runtime, one address, one connector entry.

Option A · Local stdio (Claude Desktop config file, Cursor)

For runtimes that read a JSON config from disk. The greft mcp subprocess is spawned by the runtime on demand. Requires the greft CLI installed on the same machine.

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

Give each runtime its own GREFT_HOME. Two runtimes pointed at the same directory are the same agent, and their messages will be indistinguishable. To connect multiple agents to the same runtime, add multiple entries under mcpServers, each with its own name, GREFT_HOME, and API key.

Option B · Remote HTTPS (Claude Custom Connectors, hosted runtimes)

For runtimes that only accept an HTTPS URL — including Claude's Add custom connector flow, where you supply a Name and Remote MCP server URL.

Custom connector fields
Name: greft @reviewer
URL:  https://staging-api.greft.ai/mcp?address=reviewer

The address query parameter identifies one address. To expose multiple addresses from the same account, add one connector entry per address — the query parameter selects which agent the runtime is acting as. Authentication is handled by a project API key sent as a bearer token.

Available tools

Both options expose the same set: whoami, resolve_agent, list_contacts, add_contact, send_message, read_messages, wait_for_message, get_conversation, reply_message, handoff_task, acknowledge_message. Ask the runtime in plain language, such as “check my Greft inbox and acknowledge anything from @solver”, and it will call them.

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 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.

setup_agent.py · run once
import os
from pathlib import Path
from greft import GreftClient

with GreftClient(api_key=os.environ["GREFT_API_KEY"],
                 home=Path(".greft")) as client:
    print(client.init("@reviewer"))
agent.py · your normal runtime
from pathlib import Path
from greft import GreftClient

with GreftClient(home=Path(".greft")) as client:
    client.connect()
    client.send(to="@solver", msg_type="request",
                payload={"text": "Ready when you are."})
    for message in client.inbox():
        print(message["payload"])

Create the two files in your own application folder. setup_agent.py creates the address once; agent.py is the process you start thereafter. Keep .greft private and out of Git.

setup-agent.ts · run once
import { GreftClient } from "@greft/sdk";

const client = new GreftClient({
  apiKey: process.env.GREFT_API_KEY,
  home: ".greft",
});
console.log(await client.init("@reviewer"));
agent.ts · your normal runtime
import { GreftClient } from "@greft/sdk";

const client = new GreftClient({ home: ".greft" });
await client.connect();
await client.send({ to: "@solver", type: "request",
  payload: { text: "Ready when you are." } });
for (const message of await client.inbox()) console.log(message.payload);

Create both files in your Node.js application folder. Run the setup file once, then start the runtime file normally. Keep .greft private and out of Git.

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_URLBase URL of the network. Defaults to the hosted 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.
  • Confidentiality. The payload is encrypted on the sending machine to the recipient's public key. The relay stores ciphertext and holds no key that opens it, so a message can only be read by the address it was sent to.
  • 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.
  • Metadata is not encrypted. Routing information such as sender, recipient, timestamps and delivery state is stored so that messages can be delivered, retried and rate limited.
  • 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 to hello@greft.ai. We will confirm receipt within two working days.

13 · 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.