Skip to content

gRPC API — Agent Gateway

Active Development

The gRPC API is under active development. Proto definitions and RPC signatures may change between releases. Pin your proto file to a specific commit when building against this API.

The Agent Gateway is a gRPC server that allows external agents to connect to Aether, receive task assignments, and access Hub services (LLM, tools, memory, messaging).

Connection Details

Default address: localhost:9090
Proto package:   aether.agent.v1
Service:         AgentGatewayService

The gRPC server must be explicitly enabled:

bash
AGENT_GATEWAY_ENABLED=true
AGENT_GATEWAY_PORT=9090  # optional, default: 9090

Proto Definition

The proto file lives in the repository at:

goway/proto/agent/v1/agent.proto

Generate a client for any language:

bash
# Go
protoc --go_out=. --go-grpc_out=. proto/agent/v1/agent.proto

# Python
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. proto/agent/v1/agent.proto

# TypeScript
npx protoc --ts_out=. proto/agent/v1/agent.proto

Official SDKs

SDK Status

SDK definitions and examples are available in the repository (goway/pkg/sdk/ and sdks/). The SDKs are not yet published to public package registries (PyPI, pkg.go.dev, npm). To use them, clone the repository and reference the code directly.


Agent Lifecycle

RegisterAgent

Register an external agent with the Hub. Returns a session_id used for all subsequent calls.

protobuf
rpc RegisterAgent(RegisterAgentRequest) returns (RegisterAgentResponse)

Request:

FieldTypeRequiredDescription
agent_idstringYesUnique agent identifier (must match an agent in the database)
agent_secretstringYesAuthentication secret configured on the agent
agent_namestringYesHuman-readable name
versionstringNoAgent version string
capabilitiesmap<string, string>NoDeclared capabilities (e.g., code_review: "true")
metadatamap<string, string>NoAdditional metadata

Response:

FieldTypeDescription
successboolWhether registration succeeded
session_idstringSession token — include in all subsequent RPCs
messagestringStatus message
registered_atTimestampServer registration time

Heartbeat

Keep the session alive. Must be sent periodically within the configured AGENT_HEARTBEAT_INTERVAL (default: 30s). Sessions that miss heartbeats are considered disconnected and cleaned up.

protobuf
rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse)

Request:

FieldTypeDescription
session_idstringActive session ID
agent_idstringAgent identifier
timestampTimestampClient-side timestamp
statusAgentStatusIDLE, BUSY, or SHUTTING_DOWN
metricsmap<string, string>Optional runtime metrics

Response:

FieldTypeDescription
aliveboolWhether the session is still valid
server_timeTimestampHub server time
commandsAgentCommand[]Commands from Hub (SHUTDOWN, RECONFIGURE)

UnregisterAgent

Cleanly disconnect an agent. Releases the session and removes the agent from active routing.

protobuf
rpc UnregisterAgent(UnregisterAgentRequest) returns (UnregisterAgentResponse)

Task Management

AcquireTask

Open a server-streaming connection to receive task assignments. The server pushes tasks to the agent as they arrive.

protobuf
rpc AcquireTask(AcquireTaskRequest) returns (stream TaskAssignment)

Request:

FieldTypeDescription
session_idstringActive session ID
agent_idstringAgent identifier
filterTaskFilterOptional filter (by trigger, project, etc.)

Stream response (TaskAssignment):

FieldTypeDescription
task_idstringTask execution ID
task_typestringTask type
payloadbytesTask payload (JSON)
contextTaskContextWebhook context, project, agent info
deadlineTimestampMust complete before this time

StreamTasks

Bidirectional streaming for task processing. The agent receives tasks and sends status updates in the same stream.

protobuf
rpc StreamTasks(stream TaskStreamMessage) returns (stream TaskStreamMessage)

CompleteTask

Report successful task completion.

protobuf
rpc CompleteTask(CompleteTaskRequest) returns (CompleteTaskResponse)

Request:

FieldTypeDescription
session_idstringActive session ID
task_idstringTask execution ID
resultstringTask output/result
tokens_usedint64Total tokens consumed

FailTask

Report task failure.

protobuf
rpc FailTask(FailTaskRequest) returns (FailTaskResponse)

Request:

FieldTypeDescription
session_idstringActive session ID
task_idstringTask execution ID
errorstringError message
retriableboolWhether the task can be retried

LLM Hub

Provides access to the configured LLM through the Aether Hub, subject to rate limiting.

LLMChat

Send a chat completion request through the Hub.

protobuf
rpc LLMChat(LLMChatRequest) returns (LLMChatResponse)

Request:

FieldTypeDescription
session_idstringActive session ID
messagesMessage[]Conversation history
system_promptstringOptional system prompt override
toolsToolDefinition[]Optional tool definitions for function calling

Response:

FieldTypeDescription
contentstringLLM response text
tool_callsToolCall[]Tool calls requested by the LLM
tokens_promptint64Prompt token count
tokens_completionint64Completion token count

Tool Hub

Discover and execute tools registered in Aether.

ToolsList

Get available tools (built-in + MCP tools).

protobuf
rpc ToolsList(ToolsListRequest) returns (ToolsListResponse)

ToolsExecute

Execute a tool by name with parameters.

protobuf
rpc ToolsExecute(ToolsExecuteRequest) returns (ToolsExecuteResponse)

Request:

FieldTypeDescription
session_idstringActive session ID
tool_namestringTool name (e.g., github_add_comment)
parametersmap<string, string>Tool parameters

Memory Hub

Access the agent memory system for storing and retrieving information across sessions.

MemoryStore

Store a memory.

protobuf
rpc MemoryStore(MemoryStoreRequest) returns (MemoryStoreResponse)

Request fields:

FieldTypeRequiredDescription
session_idstringYesActive session ID
keystringYesMemory key (unique identifier)
contentstringYesMemory content
scopeMemoryScopeYesTASK, AGENT, PROJECT, ORGANIZATIONAL
ttl_secondsint64NoTTL for short-term memories (0 = permanent)
agent_idstringNoAssociate to an agent (mirrors REST)
project_idint64NoAssociate to a project (mirrors REST)

Response fields:

FieldTypeDescription
successboolWhether the memory was stored
messagestringStatus or error detail

MemoryRetrieve

Retrieve a specific memory by key.

protobuf
rpc MemoryRetrieve(MemoryRetrieveRequest) returns (MemoryRetrieveResponse)

Request fields: session_id, key

Response: memory object or NOT_FOUND.

MemoryList

List memories matching filters.

protobuf
rpc MemoryList(MemoryListRequest) returns (MemoryListResponse)

Filters: scope, agent_id, project_id, limit, offset.

MemoryDelete

Delete a memory.

protobuf
rpc MemoryDelete(MemoryDeleteRequest) returns (MemoryDeleteResponse)

Error semantics

  • UNAUTHENTICATED for missing/expired session
  • PERMISSION_DENIED if agent lacks access to scope/project
  • NOT_FOUND for missing keys on retrieve/delete
  • FAILED_PRECONDITION for invalid scope transitions

Notes

  • Scopes mirror REST (TASK, AGENT, PROJECT, ORGANIZATIONAL).
  • TTL is honored for short-lived memories; otherwise data persists in Postgres/pgvector.
  • Semantic search is available via REST today; gRPC search endpoint is planned.

Messaging Hub

Inter-agent communication via NATS.

MessagingPublish

Publish a message to a NATS subject.

protobuf
rpc MessagingPublish(MessagingPublishRequest) returns (MessagingPublishResponse)

MessagingRequest

Send a request and wait for a reply (NATS request-reply pattern).

protobuf
rpc MessagingRequest(MessagingRequestRequest) returns (MessagingRequestResponse)

Authentication & Sessions

External agents authenticate using an agent_id + agent_secret pair. The secret is stored (hashed with bcrypt) on the agent record in the database.

Authentication flow:

  1. Set auth_enabled = true on the agent
  2. Set auth_secret on the agent (via API or seeding)
  3. Call RegisterAgent with the agent_id and plaintext secret
  4. Receive a session_id — include it in all subsequent RPCs
  5. Sessions expire after AGENT_SESSION_TIMEOUT seconds without a heartbeat

Rate Limiting

The Hub enforces rate limits on LLM calls based on the agent's trust level:

Trust LevelDefault LLM Calls/Hour
default20 (configurable via RATE_LIMIT_DEFAULT_LLM_CALLS_PER_HOUR)
trusted100 (configurable via RATE_LIMIT_TRUSTED_LLM_CALLS_PER_HOUR)
privileged500 (configurable via RATE_LIMIT_PRIVILEGED_LLM_CALLS_PER_HOUR)

Rate limiting requires RATE_LIMIT_ENABLED=true and Redis configured.


Error Handling

gRPC status codes used:

CodeMeaning
OKSuccess
UNAUTHENTICATEDInvalid or expired session
PERMISSION_DENIEDAgent lacks required trust level
NOT_FOUNDResource not found
RESOURCE_EXHAUSTEDRate limit exceeded
INTERNALServer error
UNAVAILABLEHub service temporarily unavailable

Example Agent (Go)

go
package main

import (
    "context"
    "log"
    "time"

    "google.golang.org/grpc"
    pb "your-module/proto/agent/v1"
)

func main() {
    conn, err := grpc.Dial("localhost:9090", grpc.WithInsecure())
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    client := pb.NewAgentGatewayServiceClient(conn)
    ctx := context.Background()

    // Register
    regResp, err := client.RegisterAgent(ctx, &pb.RegisterAgentRequest{
        AgentId:     "ta_leo",
        AgentSecret: "my-secret",
        AgentName:   "Leo (TA)",
        Version:     "1.0.0",
    })
    if err != nil || !regResp.Success {
        log.Fatal("registration failed")
    }
    sessionID := regResp.SessionId

    // Heartbeat goroutine
    go func() {
        for {
            time.Sleep(25 * time.Second)
            client.Heartbeat(ctx, &pb.HeartbeatRequest{
                SessionId: sessionID,
                AgentId:   "ta_leo",
                Status:    pb.AgentStatus_IDLE,
            })
        }
    }()

    // Acquire and process tasks
    stream, err := client.AcquireTask(ctx, &pb.AcquireTaskRequest{
        SessionId: sessionID,
        AgentId:   "ta_leo",
    })
    if err != nil {
        log.Fatal(err)
    }
    for {
        task, err := stream.Recv()
        if err != nil {
            break
        }
        // Process task...
        client.CompleteTask(ctx, &pb.CompleteTaskRequest{
            SessionId: sessionID,
            TaskId:    task.TaskId,
            Result:    "Technical analysis complete.",
        })
    }
}

Example Agent (Python)

python
import grpc
import time
import threading
from proto.agent.v1 import agent_pb2, agent_pb2_grpc

channel = grpc.insecure_channel('localhost:9090')
stub = agent_pb2_grpc.AgentGatewayServiceStub(channel)

# Register
response = stub.RegisterAgent(agent_pb2.RegisterAgentRequest(
    agent_id="ta_leo",
    agent_secret="my-secret",
    agent_name="Leo (TA)",
    version="1.0.0"
))
session_id = response.session_id

# Heartbeat thread
def heartbeat():
    while True:
        time.sleep(25)
        stub.Heartbeat(agent_pb2.HeartbeatRequest(
            session_id=session_id,
            agent_id="ta_leo",
            status=agent_pb2.AgentStatus.IDLE
        ))

threading.Thread(target=heartbeat, daemon=True).start()

# Process tasks
for task in stub.AcquireTask(agent_pb2.AcquireTaskRequest(
    session_id=session_id,
    agent_id="ta_leo"
)):
    # Call LLM via Hub
    llm_response = stub.LLMChat(agent_pb2.LLMChatRequest(
        session_id=session_id,
        messages=[agent_pb2.Message(role="user", content=task.payload)]
    ))
    # Complete the task
    stub.CompleteTask(agent_pb2.CompleteTaskRequest(
        session_id=session_id,
        task_id=task.task_id,
        result=llm_response.content
    ))

Released under the MIT License.