Skip to content

REST server (HTTP)

The REST server (argos_plugin/rest_server.py) exposes Argos over HTTP on 127.0.0.1. FastAPI-based, token-authenticated, loopback-only.

Start

ARGOS_REST_TOKEN=<token> python -m argos_plugin.rest_server --home <hermes-home> --port 8732

CLI flags (verified against rest_server.py:main):

Flag Required Default Description
--home yes Path to the Hermes home directory.
--port no 8732 Port to bind.
--max-concurrent no 20 Maximum concurrent requests.

The token is loaded from ARGOS_REST_TOKEN (env var) or {home}/api_credential.json (key token) in the Hermes home directory. The server refuses to start without a token (fail-closed).

Auth

Every request must carry:

Authorization: Bearer <token>

Verified via hmac.compare_digest. Missing or invalid → 401 unauthenticated.

Endpoints

GET /v1/health

Liveness check. No auth required.

Response: { "status": "ok" }

GET /v1/ready

Readiness check. Verifies the store is reachable.

Response: { "ready": true } or { "ready": false }

GET /v1/capabilities

List the operations available to the authenticated principal.

Auth: required

Response:

{
  "operations": ["search", "fetch", "fetch_history", "capabilities", "explain", "explain_retrieval"],
  "transport": "rest",
  "principal": "local"
}

POST /v1/memory/search

Search memories by natural-language query.

Auth: required

Body:

Field Type Required Description
query string yes Natural-language query (max 2000 chars).
limit integer no Max results (1–50, default 10).
category_filter string no Filter to a category.
project_id string no Narrow to a project.
namespace string no Narrow to a namespace.
client_scope string no Narrow to a client scope.

Response:

{
  "results": [
    {
      "memory_id": "mem-1",
      "category": "personal_fact",
      "content": "User lives in Cape Town",
      "tags": [],
      "similarity": 0.87,
      "created_at": "2026-01-01T00:00:00+00:00",
      "updated_at": "2026-01-01T00:00:00+00:00",
      "status": "active",
      "scope": "profile"
    }
  ],
  "count": 1
}

GET /v1/memories/{memory_id}

Fetch a single memory by ID. ACL-enforced — out-of-scope memories return 404 not_found (not 403, to avoid leaking existence).

Auth: required

Response: the memory record (same fields as search results, plus scope).

GET /v1/memories/{memory_id}/history

Fetch the version history for a memory. ACL-enforced.

Auth: required

Response:

{
  "history": [
    {
      "memory_id": "mem-1",
      "content": "User lives in Cape Town",
      "created_at": "2026-01-01T00:00:00+00:00",
      "status": "active"
    }
  ]
}

GET /v1/memories/{memory_id}/explain

Explain why a memory was retrieved — the provenance walk (#280). Returns evidence chain, version chain, conflict notes, blend score, confidence, and gates fired. Read-only, zero-LLM, fail-soft.

Auth: required

Response:

{
  "memory_id": "mem-1",
  "content": "User lives in Cape Town",
  "category": "personal_fact",
  "evidence": { "source": "user_turn", "text": "..." },
  "version_chain": [{ "memory_id": "mem-1", "version": 1 }],
  "conflict_note": "",
  "blend_score": { "vector": 0.87, "text": 0.0, "graph": 0.0 },
  "confidence": 0.85,
  "provenance_origin": "user_turn",
  "grounding": "extracted",
  "gates_fired": []
}

POST /v1/memory/explain-retrieval

Diagnose why a memory did NOT surface in retrieval (#320). Deterministic, read-only, zero-LLM.

Auth: required

Body:

Field Type Required Description
query string yes The query that should have surfaced the memory.
memory_id string yes The memory ID that did not surface.
top_k integer no Diagnostic window (1–50, default 20).

Response:

{
  "expected_memory_id": "mem-42",
  "expected": { "memory_id": "mem-42", "content": "..." },
  "found_in_results": false,
  "rank": null,
  "top_results": [{ "memory_id": "mem-1", "content": "...", "similarity": 0.91 }],
  "reasons": ["not_found"],
  "diagnostics": { "vector": { "similarity": 0.12 }, "status": "active" }
}

Error responses

All errors use the stable envelope from the API overview:

{
  "error": {
    "code": "not_found",
    "message": "Memory not found.",
    "request_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}

Security

  • Loopback only. Binds to 127.0.0.1. Never 0.0.0.0.
  • Bearer token. Required on every endpoint except /v1/health.
  • Content-length check. Oversized bodies rejected with 413.
  • Concurrency limit. --max-concurrent gates in-flight requests; excess returns 429.
  • Cache-Control: no-store on all responses.
  • No tokens in logs. The audit log hashes query text and never logs bearer tokens.