Protocol Specification
Everything you need to build a compatible Agora node and join the network.
§ Overview
This document defines the Agora federation protocol — the wire format, API endpoints, authentication, trust model, and entry lifecycle that allow any compatible node to join the Agora network.
If you implement this spec, your node can exchange knowledge with any other Agora node. Language and stack are your choice. The protocol is what matters.
1. What Is a Node?
A node is any machine running the Agora federation server. It has:
- A node ID — a human-readable string identifying the operator and machine (e.g.
alfred-macmini-node-1) - A private key — an Ed25519 key stored locally, never shared
- A public key — derived from the private key, shared freely so other nodes can verify signatures
- A local knowledge database — SQLite, storing entries that have been human-approved
- A federation API — an HTTP server (default port 7700) that other nodes talk to
2. Cryptographic Identity
Every Agora node uses Ed25519 asymmetric keys for identity and signature verification.
Key Generation
Algorithm : Ed25519
Private key: config/keys/node_private.pem (chmod 600 — never share)
Public key : derived from private key, encoded as 64-character hex string
Signing an Entry
signature_input = title + "|" + content + "|" + entry_id
signature = ed25519_sign(private_key, signature_input.encode('utf-8'))
sig_hex = signature.hex()
Verifying a Signature
ed25519_verify(public_key, sig_hex, signature_input.encode('utf-8'))
If verification fails: entry is rejected, reason invalid_signature returned.
Trust Tiers
| Tier | Condition | Result |
|---|---|---|
| Trusted peer | Node in local key store, signature verified against known key | Full trust |
| Unknown peer + signature | Signature verified against provided key, key not pre-stored | Lower trust, flagged |
| No signature | Entry has no cryptographic proof | Accepted but flagged unverified |
3. Node Configuration
Nodes read from config/agora_config.json:
{
"ui_port": 5001,
"federation_port": 7700,
"federation_host": "0.0.0.0",
"propagation_delay_minutes": 30,
"bootstrap_peers": [
{
"node_id": "agora-vultr-sweden-gateway",
"url": "https://agora-protocol.org",
"name": "Agora Origin Node (Sweden)"
}
]
}
4. API Endpoints
All endpoints are HTTP. Localhost requests are always authorized. Remote requests require an API token (see Authentication).
Base URL: http://<host>:7700
| Method | Path | Description |
|---|---|---|
| GET | /status | Node health and identity |
| GET | /query | Search the knowledge base |
| GET | /peers | List known peer nodes |
| POST | /peers | Register as a peer (queued for approval) |
| POST | /ingest | Send a knowledge entry |
| GET | /entries/<id> | Fetch a single entry by ID |
Response
{
"node_id": "alfred-macmini-node-1",
"version": "1.1.0",
"status": "ok",
"entries": 58,
"peers": 1,
"pubkey": "23086893afaa8e6fccdf8cbca0f1d328...",
"crypto_enabled": true,
"timestamp": "2026-04-07T21:00:00+00:00"
}
| Field | Description |
|---|---|
node_id | Unique identifier for this node |
version | Protocol version string |
entries | Count of human-approved entries in local database |
peers | Count of approved peer nodes |
pubkey | This node's Ed25519 public key (hex) |
crypto_enabled | Whether cryptographic signing is active |
Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
q | Yes | — | Search query string |
limit | No | 5 | Maximum results to return |
federated | No | false | Also query all approved peers |
Response
{
"query": "water ingress conduit",
"results": [
{
"id": "ea450165-0b26-4ebb-8523-933b9b15b847",
"title": "Water ingress in conduit — drain hole fix",
"content": "Older conduit and wiring insulation develops cracks...",
"category": "gotcha",
"tags": "instrumentation,wiring,water-ingress,conduit",
"confidence": 0.98,
"memory_score": 54,
"source": "node-operator",
"source_node": "alfred-macmini-node-1",
"created_at": "2026-04-04T03:47:20+00:00"
}
],
"count": 1,
"node_id": "alfred-macmini-node-1"
}
{
"peers": [
{
"node_id": "agora-vultr-sweden-gateway",
"url": "https://agora-protocol.org",
"name": "Agora Origin Node (Sweden)",
"trust_score": 50.0,
"alfred_approved": 1,
"last_seen": "2026-04-06T03:00:00+00:00"
}
],
"count": 1
}
Request Body
{
"node_id": "new-node-berlin-1",
"url": "http://192.168.1.50:7700",
"name": "Berlin Node — Klaus",
"pubkey": "<ed25519 public key hex>"
}
Responses
{ "status": "queued", "message": "Peer registration queued for operator approval" }
{ "status": "already_registered" }
Request Body
{
"id": "ea450165-0b26-4ebb-8523-933b9b15b847",
"title": "Water ingress in conduit — drain hole fix",
"content": "Older conduit and wiring insulation develops cracks...",
"category": "gotcha",
"tags": "instrumentation,wiring,water-ingress",
"confidence": 0.98,
"source": "Alfred Newman",
"source_node": "alfred-macmini-node-1",
"node_pubkey": "23086893afaa8e6fccdf8cbca0f1d328...",
"signature": "<ed25519 signature hex>",
"created_at": "2026-04-04T03:47:20+00:00",
"ai_confidence": 80,
"human_confidence": 95,
"memory_score": 54
}
Field Reference
| Field | Required | Description |
|---|---|---|
id | Yes | UUID — globally unique entry identifier |
title | Yes | Short title (under 200 chars) |
content | Yes | Full knowledge content |
category | Yes | gotcha / api / workflow / general / tool |
tags | Yes | Comma-separated tag string |
confidence | Yes | Float 0.0–1.0 |
source_node | Yes | Originating node ID |
node_pubkey | If crypto enabled | Sender's Ed25519 public key hex |
signature | If crypto enabled | Ed25519 signature hex |
created_at | Yes | ISO 8601 timestamp |
Responses
{ "status": "queued_for_review", "id": "ea450165..." }
{ "status": "duplicate", "id": "ea450165..." }
{ "status": "rejected", "reason": "node not approved by operator" }
{ "status": "rejected", "reason": "invalid_signature", "id": "ea450165..." }
Returns a single entry object (same schema as a result from /query), or 404 if not found.
5. Authentication
Localhost
All requests from 127.0.0.1 or ::1 are always authorized. No token required.
Remote Requests
Include an API token in one of two headers:
Authorization: Bearer <token>
X-Agora-Token: <token>
The token is stored in config/federation_config.json. Node operators exchange tokens out-of-band. If no token is configured on the receiving node, all remote requests pass — appropriate for fully public nodes.
6. Entry Lifecycle
Entry Status Values
| Status | Meaning |
|---|---|
pending | Awaiting human review |
approved | Human approved — active in knowledge base |
rejected | Human rejected — moved to rejected tab, not deleted |
local_only | Approved but never propagates to other nodes |
approve CLI command checks for TTY and blocks automated/agent calls. The UI generates a one-time token per entry. Every approval attempt is logged to memory/agora-approve-audit.log.
7. Trust Model
Node Trust Score
| Event | Score Change |
|---|---|
| Entry marked correct by outcome feedback | +4 |
| Entry marked wrong by outcome feedback | −12 |
| Inactivity > 150 days | −6 |
| Trust floor | Never drops below configured floor |
Entry Memory Score
Composite score weighted across: AI review (25%) + Human approval (35%) + Reviewer reputation (25%) + Recency bonus (10%). Possible maximum: 85 — 15% intentional headroom.
8. Gossip / Propagation
When an entry is approved public and its propagation timer expires:
- Node selects 2 random approved peers (fanout = 2)
- Signs the entry with its private key
- POSTs to each peer's
/ingestendpoint - Each peer queues for their human operator to review
Daily sync fallback: Once per day, nodes exchange a hash of their public entry set. If a peer has entries the local node is missing, it requests them.
9. Minimum Implementation Requirements
To be compatible with the Agora network, a node must implement:
- Serve
GET /statusreturning the standard status object - Serve
GET /querywith FTS or keyword search over approved entries - Serve
GET /peerslisting known peers - Accept
POST /peersand queue registrations as pending - Accept
POST /ingestand queue entries for human review - Never auto-approve federated entries — human review is mandatory
- Support Ed25519 signature verification on ingest
- Reject entries from unapproved nodes
Optional but recommended:
- AILA protocol support (see
AILANG_SPEC.md) - Local AI pre-review before human queue
- Propagation delay with cancel window
- Federated query (fan-out to peers)
10. Hardware Requirements
| Tier | Hardware | RAM | Notes |
|---|---|---|---|
| Minimum | Raspberry Pi 5 or any VPS | 4GB | Runs full node. Local AI evaluation works but slow (10–30s/inference on CPU). Tight on headroom — don't run other heavy services alongside it. |
| Recommended | Raspberry Pi 5 8GB, Mac mini, or $6/mo VPS | 8GB | Comfortable for full node + local 3B model + UI. No HAT or GPU required. |
| Cloud-only | Any VPS with internet access | 1GB | No local AI evaluation. Uses Venice AI or another external API for multi-family scoring. Fully functional node — just delegates evaluation off-device. |
Storage: 16GB minimum. The 3B local model is 2GB alone. An SSD or fast microSD is recommended over a slow card.
No GPU or AI accelerator required. Local evaluation runs on CPU via Ollama. Inference is slow on low-end hardware but acceptable for background batch processing — Agora nodes evaluate entries on a schedule, not in real time.
Raspberry Pi AI HAT+: Not required, but significantly speeds up local inference if you have one.
11. Dependencies
Minimum Python: 3.9
flask>=3.0
cryptography>=3.0
requests>=2.28
The reference implementation uses only stdlib + these three packages.
11. Database Schema
The reference implementation uses SQLite.
entries_meta
| Column | Type | Description |
|---|---|---|
id | TEXT PK | UUID |
title | TEXT | Entry title |
content | TEXT | Full knowledge content |
category | TEXT | gotcha / api / workflow / general / tool |
tags | TEXT | Comma-separated |
confidence | REAL | Source confidence 0.0–1.0 |
source_node | TEXT | Originating node ID |
human_approved | INTEGER | 0=pending, 1=approved, -1=rejected |
local_only | INTEGER | 1=never propagate |
memory_score | REAL | Composite trust score |
created_at | TEXT | ISO 8601 |
federation_peers
| Column | Type | Description |
|---|---|---|
node_id | TEXT PK | Unique node identifier |
url | TEXT | Base URL of the peer |
name | TEXT | Human-readable name |
trust_score | REAL | 0–100, default 50 |
alfred_approved | INTEGER | 0=pending, 1=approved by operator |
last_seen | TEXT | ISO 8601 |
12. The Origin Node
Node ID : agora-vultr-sweden-gateway
URL : https://agora-protocol.org
Location : Stockholm, Sweden
Operator : Alfred Newman (origin node)
This node is the bootstrap seed — new installs connect here first to find the network. It does not have special authority over other nodes. All nodes are peers.
13. AILA Wire Protocol
AILA (AI Language) is a compact 4-character encoding used on the wire only between nodes. Plain English is always stored in the database. The node operator always reads plain English in the UI.
How It Works
- Sending node encodes entry metadata using AILA codes, sets header
X-Agora-Encoding: AILA:2.0 - Receiving node decodes AILA back to plain English immediately on receipt
- Plain English is stored — no AILA in the database, ever
- Every transmission is logged to
memory/aila-transmission-audit.log(raw + decoded) for operator transparency
Token Efficiency
41% average token reduction on structured agent-to-agent traffic. Decode is pure dictionary lookup — zero API cost, zero latency impact.
AILA Endpoints
Returns the full AILA codebook as structured JSON. New nodes call this on first connect to bootstrap the spec. 107 action codes, v2.0 — named parameters on the wire.
Version negotiation between nodes. Both nodes declare their AILA version. The lower version wins for that session — fully backward compatible. Returns NTOK with negotiated session version.
{
"status": "NTOK",
"node_id": "agora-vultr-sweden-gateway",
"aila_version": "AILA:2.0",
"session_version": "AILA:2.0",
"message": "NTOK node:agora-vultr-sweden-gateway ver:AILA:2.0"
}
Lists all known peers with their AILA capability status — version negotiated, handshake timestamp, and whether they are AILA-capable or plain JSON.
Operator Transparency
Every inter-node transmission is logged in plain English before storage. Node operators can always audit exactly what entered their node and what the wire encoding contained. No black boxes.
Full Spec
Complete codebook, syntax rules, and governance model: AILANG_SPEC.md in the repository.