Skip to content

Cortex over REST API

semvec[api] exposes the full multi-agent coordination stack over HTTP. Every primitive in the in-process Cortex API has a REST counterpart, plus several that only exist at the REST layer — observer anomalies, drift event fan-out, trust-score consensus, and per-tenant user partitions.

This is the production surface when more than one process needs to talk to the cortex. For the in-process / single-process case, see the Cortex overview.

Prerequisites

pip install "semvec[api]"

export SEMVEC_LICENSE_KEY="eyJhbGciOiJFZERTQSI..."
export DATABASE_URL="postgresql://user:pw@host/semvec"   # or sqlite:///semvec.db
semvec serve --host 0.0.0.0 --port 8080

For local development:

SEMVEC_ALLOW_ANONYMOUS=1 semvec serve --host 127.0.0.1 --port 8080

Anonymous mode bypasses JWT verification and treats every request as community-tier. Never enable in production. See CLI reference for all flags.

Auth — same Ed25519 JWT across the whole stack

The REST API uses the same JWT as the in-process licensing system. Send via either header on every request except /v1/health and /metrics:

Authorization: Bearer eyJhbGciOiJFZERTQSI...
# or
X-API-Key: eyJhbGciOiJFZERTQSI...

Ownership is per license subject. The server tracks which subject created which cluster / region / observer / session and returns 404 (not 403) when a different subject asks for it — this prevents tenants from probing each other's resource existence. See REST API error handling for the full status table.


The four cortex layers

The REST stack is structured as four concentric layers. You can use any layer in isolation, but most production deployments combine all four.

┌─────────────────────────────────────────────────────────────┐
│  Global Observer  (cross-cluster anomaly detection)         │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  Region  (consensus, drift event fan-out)             │  │
│  │  ┌─────────────────────────────────────────────────┐  │  │
│  │  │  Cluster  (aggregated SemvecAgentNetwork)       │  │  │
│  │  │  ┌───────────────────────────────────────────┐  │  │  │
│  │  │  │  Sessions  (individual SemvecState)       │  │  │  │
│  │  │  └───────────────────────────────────────────┘  │  │  │
│  │  └─────────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
│  Network  (peer transfer, user partitions, trust-weighted)  │
└─────────────────────────────────────────────────────────────┘
Layer What you get Endpoint group
Sessions Per-agent semantic state — every cluster member is a session /v1/run, /v1/store, /v1/session/..., /v1/state/context
Cluster Aggregated view over a set of sessions; weighted-average or attention /v1/cluster/
Region Group of clusters with consensus-driven realignment; drift events fan out here /v1/region/
Observer Cross-cluster anomaly detection across one or more regions /v1/observer/
Network Peer-to-peer state transfer, user partitions, trust-weighted consensus /v1/network/

Layer 1 — Sessions

The base unit. Every cluster member is a session. Two session paths:

# Single-turn run: creates a session if needed, returns context for an LLM
curl -X POST http://localhost:8080/v1/run \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "What did we decide about embedders?"}'
# → {"session_id": "...", "context": "...", "metrics": {...}}

# Learn from the LLM's response
curl -X POST http://localhost:8080/v1/store \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"session_id": "abc", "response": "We picked mpnet 768d for German content."}'

For the full session surface (memories, anchors, triggers, isolation, export/import) see the REST API reference.


Layer 2 — Cluster

A cluster is a set of sessions whose states are aggregated into one shared session (the cluster session, ID equals the cluster ID). Every member contributes to the cluster aggregate; the aggregate can be blended back into members via /feedback.

Create a cluster

curl -X POST http://localhost:8080/v1/cluster/ \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "analyst-team",
    "aggregation_mode": "attention",
    "coupling_factor": 0.3
  }'
# → 201 {"id": "cluster_xxx", "name": "...", "aggregation_mode": "attention", ...}
Field Type Notes
aggregation_mode "weighted_average" | "attention" Pick attention when agents have meaningfully different reliability; weighted-average for the simple case. Wrong values → 400.
coupling_factor float ∈ [0, 1] How aggressively /feedback blends the cluster aggregate back into members.

Add and remove members

# Add an existing session as a cluster member
curl -X POST http://localhost:8080/v1/cluster/cluster_xxx/members \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"session_id": "session_42"}'

# Remove
curl -X DELETE http://localhost:8080/v1/cluster/cluster_xxx/members/session_42 \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY"

Query and feed back

# Run a query against the cluster's shared session
curl -X POST http://localhost:8080/v1/cluster/cluster_xxx/run \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "what's the team consensus on retention strategy?"}'

# Blend the aggregate back into all members
curl -X POST http://localhost:8080/v1/cluster/cluster_xxx/feedback \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY"

Deleting a cluster (DELETE /v1/cluster/{id}) tears down the backing shared session as well.


Layer 3 — Region (consensus + drift events)

A region groups multiple clusters and runs consensus-driven realignment when a fraction of member clusters detect drift inside a rolling time window. Every realignment produces a drift event.

Create a region

curl -X POST http://localhost:8080/v1/region/ \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "eu-west",
    "consensus_threshold": 0.6,
    "vote_window_seconds": 300
  }'
Field Type Notes
consensus_threshold float ∈ [0, 1] Fraction of clusters that must vote for realignment within vote_window_seconds.
vote_window_seconds float Rolling window in which drift events count toward the consensus.

Attach clusters

curl -X POST http://localhost:8080/v1/region/region_xxx/clusters \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cluster_id": "cluster_xxx"}'

Read drift events

Drift events are published internally whenever /v1/run (or /v1/cluster/{id}/run) detects drift on a cluster-backing session. The DriftEventBus fans them out to per-region callbacks; a realignment fires when the threshold is reached inside the window.

curl "http://localhost:8080/v1/region/region_xxx/events?limit=20" \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY"

Each event carries cluster_id, magnitude, timestamp, and phase.


Layer 4 — Global Observer (anomaly detection)

The observer watches one or more regions and emits anomalies. Idempotent per license subjectPOST /v1/observer/ returns the existing observer if you already have one. Three anomaly types fire automatically:

Anomaly When it fires
cross_cluster_convergence 3+ clusters across ≥ 2 regions converge to the same non-initialization phase — a strong signal that the same topic is dominating multiple teams.
systemic_drift More than 50 % of observed clusters show drift indicators in the same window — a system-wide pivot.
cluster_divergence A cluster's interaction_count exceeds 3× the region average — a runaway agent.
# Create / get the observer
curl -X POST http://localhost:8080/v1/observer/ \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY"

# Register a region
curl -X POST http://localhost:8080/v1/observer/regions \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"region_id": "region_xxx"}'

# Read state
curl http://localhost:8080/v1/observer/summary \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY"
# → {"observer_id": "...", "regions_observed": 3, "anomaly_count": 2, ...}

# Read recent anomalies (newest first)
curl http://localhost:8080/v1/observer/anomalies \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY"

# Trigger a manual sample (bypass the auto-sampler)
curl -X POST http://localhost:8080/v1/observer/sample \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY"

# Clear the anomaly log
curl -X DELETE http://localhost:8080/v1/observer/anomalies \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY"

Layer 5 — Network (peer transfer, user partitions, trust)

The network layer is the cross-process / cross-machine surface — StateVectorPacket over HTTP, user partition switching, and a trust-score-weighted consensus engine.

State transfer

curl -X POST http://localhost:8080/v1/network/transfer \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source_instance": "agent_a",
    "target_instance": "agent_b",
    "transfer_type": "SEMANTIC_DELTA",
    "state_vector": [0.1, 0.2, ...]
  }'

transfer_typeFULL_STATE, PARTIAL_STATE, SEMANTIC_DELTA, MEMORY_FRAGMENT, COHERENCE_FIELD — same enum as the in-process StateVectorPacket. The wire format carries the IEEE 754 bit pattern alongside the human-readable float array, so verify_integrity() is bit-exact across the round-trip.

User partitions

A user partition lets you serialise an entire user's session set under one identifier, then activate a different user's partition without losing the previous one. Useful for multi-tenant agents that switch active users.

# Switch active user (saves current, activates target)
curl -X POST http://localhost:8080/v1/network/users/switch \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"user_id": "user_42"}'

# Currently active user
curl http://localhost:8080/v1/network/users/active \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY"

# Serialize a partition (persists every session under a user_id)
curl -X POST http://localhost:8080/v1/network/users/user_42/serialize \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY"

Trust-weighted consensus

# Propose a network-wide consensus vector
curl -X POST http://localhost:8080/v1/network/consensus \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"proposed_state": [0.1, 0.2, ...], "rationale": "..."}'

# Read current trust scores per registered instance
curl http://localhost:8080/v1/network/consensus/trust \
  -H "Authorization: Bearer $SEMVEC_LICENSE_KEY"

Trust scores update over time based on consensus participation; the engine weights votes by trust, not just count.


Python client (httpx)

For non-trivial integrations, drive the API with httpx:

import httpx

c = httpx.Client(
    base_url="http://localhost:8080/v1",
    headers={"X-API-Key": "eyJhbGciOiJFZERTQSI..."},
    timeout=30.0,
)

# Bootstrap a region with two clusters and an observer
cluster_a = c.post("/cluster/", json={"name": "a", "aggregation_mode": "attention"}).json()
cluster_b = c.post("/cluster/", json={"name": "b", "aggregation_mode": "attention"}).json()

region = c.post("/region/", json={
    "name": "eu-west", "consensus_threshold": 0.6, "vote_window_seconds": 300,
}).json()

c.post(f"/region/{region['id']}/clusters", json={"cluster_id": cluster_a["id"]})
c.post(f"/region/{region['id']}/clusters", json={"cluster_id": cluster_b["id"]})

observer = c.post("/observer/").json()
c.post("/observer/regions", json={"region_id": region["id"]})

# Run the cluster session
run = c.post(f"/cluster/{cluster_a['id']}/run", json={"query": "..."}).json()

# Watch for anomalies
while True:
    anomalies = c.get("/observer/anomalies").json()
    for a in anomalies:
        if a["type"] == "cross_cluster_convergence":
            ...  # trigger downstream action

Persistence & metadata

DATABASE_URL controls the SQLAlchemy backend (default sqlite:///semvec.db; Postgres for production). The hot semantic state lives in-memory per worker (SessionManager); only session / cluster / region / observer metadata + the audit log are persisted. Plan for restarts — set up a Postgres instance and treat the SQLite default as dev-only.

For metadata-only export of a snapshot, use GET /v1/session/{id}/export and POST /v1/session/{id}/import per session — the same checksum-verified to_dict() round-trip the in-process API offers.

Observability

/metrics exposes Prometheus metrics behind Basic Auth (METRICS_USER / METRICS_PASSWORD). The middleware automatically collects semvec_requests_total{method, endpoint, status} and semvec_request_duration_seconds{method, endpoint} per request. Health check (no auth): GET /v1/health returns liveness + active-session count.

When the REST surface is overkill

If you only have:

  • One process
  • Less than ~10 agents
  • No multi-tenant boundary
  • No need for drift events / anomaly detection / trust scores

… use the in-process SemvecAgentNetwork. The REST stack is the production surface; do not pay the JWT / FastAPI / SQL cost when you don't need cross-process coordination.

Where to next

  • Cortex (overview) — the three usage paths, decision tree, and in-process API.
  • semvec.cortex API reference — the in-process classes that back every REST endpoint.
  • REST API reference — full endpoint list including the session, session-control, and literal-cache surfaces.
  • Compliance Pack — append-only event store + signed deletion certificates layered on top of the same REST stack.