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": "Alfred Newman",
"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. 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
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.