Conproxy exposes both HTTP and gRPC APIs. By default the proxy listens on a single address for gRPC; the HTTP API listens on the next port (or a separately configured http_listen address).
If proxy.api_key or proxy.security.api_key is set, protected endpoints require the header:
X-Api-Key: <your-key>
When agents are configured ([[proxy.agents]]), each agent uses its own key. The X-Agent-Id header identifies which agent is making the request.
Execute a single search query with caching.
Request:
{
"query": "error handling in rust",
"top_k": 5,
"priority": 1,
"upstream_id": "qdrant-primary",
"upstream_type": "qdrant"
}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
query |
string |
yes | — | Search query (max 10,000 chars) |
top_k |
usize |
no | 10 |
Max results (max 1,000) |
priority |
u8 |
no | 1 |
0=low, 1=normal, 2=high, 3=critical |
upstream_id |
string |
no | — | Route to specific upstream (skip cascade) |
upstream_type |
string |
no | — | Prefer upstreams of this type |
Response:
{
"results": [
{
"id": "doc-42",
"score": 0.89,
"content": "Rust uses the Result type for recoverable errors...",
"metadata": {"source": "rust-book"},
"upstream_id": "qdrant-primary"
}
],
"cache_status": "miss",
"took_ms": 45,
"generated_at": 1709827200000,
"miss_reason": "not_found"
}| Field | Type | Description |
|---|---|---|
results |
SearchResult[] |
Search results |
cache_status |
string |
"hit", "miss", "stale", or "frozen" |
took_ms |
u64 |
Response time in milliseconds |
generated_at |
u64? |
Unix epoch ms (when replay detection is enabled) |
miss_reason |
string? |
Why the cache missed (only on miss) |
SearchResult fields:
| Field | Type | Description |
|---|---|---|
id |
string |
Document/chunk identifier |
score |
f32 |
Relevance score (higher is better) |
content |
string |
Content text |
metadata |
object? |
Optional metadata |
upstream_id |
string? |
Which upstream produced this result |
curl -s http://127.0.0.1:9090/query \
-H 'Content-Type: application/json' \
-H 'X-Api-Key: my-secret' \
-d '{"query": "error handling", "top_k": 5}'Execute multiple queries in one request.
Request:
{
"queries": [
{"query": "error handling", "top_k": 3},
{"query": "async patterns", "top_k": 3}
]
}Response:
{
"responses": [
{"results": [...], "cache_status": "hit", "took_ms": 0},
{"results": [...], "cache_status": "miss", "took_ms": 32}
],
"total_took_ms": 32
}Execute federated search combining local cache and remote upstream.
Request: Same as /query.
Response: Same as /query, with results merged according to the configured merge mode.
Clear all cache entries.
curl -X POST http://127.0.0.1:9090/cache/clear -H 'X-Api-Key: my-secret'Pre-fetch queries to populate the cache.
Request:
{
"queries": ["error handling", "async patterns", "memory safety"]
}Selectively evict cache entries. All criteria AND-combine; omitted fields
place no constraint. When upstream_id is set the upstream-scoped path
runs; otherwise context_id / query_text_prefix / older_than_secs
are AND-combined across the whole cache.
Request:
{
"upstream_id": "qdrant-prod",
"context_id": "tenant_a",
"query_text_prefix": "error",
"older_than_secs": 3600,
"max_entries": 1000,
"expired_only": false
}| Field | Type | Notes |
|---|---|---|
upstream_id |
string? | Empty/None = use filter path |
context_id |
string? | Exact match on context_id field |
query_text_prefix |
string? | Case-insensitive prefix |
older_than_secs |
u64? | Wall-clock age from cached_at_wall |
max_entries |
usize? | Cap on removed entries |
expired_only |
bool | If true, ignore all other filters and drop only past-max-frozen entries |
Response:
{ "evicted": 12, "remaining": 48012 }Single CDC REMOVE event (bulk:<count>) is emitted for peer-mesh
invalidation when a CDC sender is configured.
Verify cache entry integrity using blake3 content hashes.
Get cache statistics broken down by upstream.
List all available contexts.
Get current context metadata and cache stats.
Switch to a different context.
Request:
{
"context_id": "production"
}Create a new context.
Request:
{
"context_id": "staging",
"upstream_url": "http://staging-qdrant:6333",
"collection": "staging-docs"
}Get per-context cache statistics.
Hot-reload configuration from disk without restarting.
curl -X POST http://127.0.0.1:9090/admin/reload -H 'X-Api-Key: my-secret'Pause accepting new queries. In-flight requests drain.
Resume accepting queries after a pause.
Reset all metrics counters to zero.
List all registered agents.
Register a new agent.
Request:
{
"id": "new-agent",
"api_key": "agent-key-123",
"default_context": "default",
"allowed_contexts": ["default", "production"]
}Remove an agent.
Rotate an agent's API key.
Server and cache statistics (JSON).
Query access statistics and hot queries.
Detailed performance metrics (JSON).
Metrics in Prometheus/OpenMetrics text format. Public (no auth required).
Key metrics exposed:
conproxy_pool_upstreams_total
conproxy_pool_upstreams_healthy
conproxy_pool_upstreams_by_type{type="fts|vector_db|hybrid|unknown"}
conproxy_pool_active_connections
conproxy_pool_utilization
Recent request audit log.
Circuit breaker status per upstream.
Request queue status and statistics.
Active client connections.
These endpoints require no authentication.
Health check with upstream status. Returns 200 if the proxy is running.
curl http://127.0.0.1:9090/healthReadiness probe for load balancers and Kubernetes. Returns 200 when the proxy can serve traffic: a vector upstream is healthy, the cache is populated (cache-only mode), or one or more LLM providers are configured (LLM-only mode). 503 only when no vector upstream, no populated cache, and no LLM providers are available.
curl http://127.0.0.1:9090/readyRuntime aggregates snapshot from tokio::runtime::Handle::metrics(). Returns JSON
with num_alive_tasks, num_workers, worker_total_busy_duration_ns,
worker_poll_count, worker_mean_poll_time_ns, global_queue_depth,
budget_forced_yield_count. Always available (no feature flag). Useful for
tracking runtime health under load.
curl http://127.0.0.1:9090/debug/tokioOne-shot task backtraces via tokio::runtime::Handle::dump(). Returns
text/plain with all live task stacks — best for stuck-task diagnosis.
Requires building with both the tokio-taskdump feature and
RUSTFLAGS=--cfg tokio_unstable; otherwise returns 503 with a hint.
RUSTFLAGS=--cfg tokio_unstable \
cargo build --profile profiling --features tokio-taskdump --bin conproxy
curl http://127.0.0.1:9090/debug/tokio/dumpUpstream pool status and statistics.
Peer replication status (CDC/P2P).
| Status | Description |
|---|---|
hit |
Response served from fresh cache |
miss |
Cache miss — fetched from upstream and cached |
stale |
Served stale response while refreshing in background |
frozen |
Upstream offline — serving last known good response |
Proto files are located at:
proto/conproxy/v1/search.proto— Search, Admin, Context, Observability servicesproto/conproxy/cdc/v1/cdc.proto— CDC and Peer services
| RPC | Request | Response | Description |
|---|---|---|---|
Query |
QueryRequest |
QueryResponse |
Single cached query |
BatchQuery |
BatchQueryRequest |
BatchQueryResponse |
Multiple queries |
FederatedQuery |
FederatedQueryRequest |
FederatedQueryResponse |
Federated search |
QueryStream |
QueryRequest |
stream QueryResponse |
Server-streaming results |
| RPC | Request | Response | Description |
|---|---|---|---|
Reload |
ReloadRequest |
ReloadResponse |
Hot-reload config |
Pause |
PauseRequest |
PauseResponse |
Pause queries |
Resume |
ResumeRequest |
ResumeResponse |
Resume queries |
CacheClear |
CacheClearRequest |
CacheClearResponse |
Clear cache |
CacheWarmup |
CacheWarmupRequest |
CacheWarmupResponse |
Warm cache |
CacheEvict |
CacheEvictRequest |
CacheEvictResponse |
Evict entries |
CacheIntegrity |
CacheIntegrityRequest |
CacheIntegrityResponse |
Verify integrity |
MetricsReset |
MetricsResetRequest |
MetricsResetResponse |
Reset metrics |
ListAgents |
ListAgentsRequest |
ListAgentsResponse |
List agents |
CreateAgent |
CreateAgentRequest |
CreateAgentResponse |
Create agent |
DeleteAgent |
DeleteAgentRequest |
DeleteAgentResponse |
Delete agent |
RotateKey |
RotateKeyRequest |
RotateKeyResponse |
Rotate agent key |
| RPC | Request | Response | Description |
|---|---|---|---|
ListContexts |
ListContextsRequest |
ListContextsResponse |
List contexts |
GetCurrentContext |
GetCurrentContextRequest |
ContextInfo |
Current context |
SwitchContext |
SwitchContextRequest |
SwitchContextResponse |
Switch context |
CreateContext |
CreateContextRequest |
CreateContextResponse |
Create context |
GetContextStats |
GetContextStatsRequest |
ContextStats |
Context stats |
| RPC | Request | Response | Description |
|---|---|---|---|
GetStats |
GetStatsRequest |
StatsResponse |
Server stats |
GetQueryStats |
GetQueryStatsRequest |
QueryStatsResponse |
Query stats |
GetAudit |
GetAuditRequest |
AuditResponse |
Audit log |
GetCircuitStatus |
GetCircuitStatusRequest |
CircuitStatusResponse |
Circuit breaker |
GetQueueStats |
GetQueueStatsRequest |
QueueStatsResponse |
Queue stats |
GetClients |
GetClientsRequest |
ClientsResponse |
Active clients |
GetPoolStatus |
GetPoolStatusRequest |
PoolStatusResponse |
Pool status |
GetCacheUpstreams |
GetCacheUpstreamsRequest |
CacheUpstreamsResponse |
Cache by upstream |
GetCacheDistill |
DistillRequest |
stream DistillEntry |
Stream cache entries for the distill command |
| RPC | Request | Response | Description |
|---|---|---|---|
Subscribe |
CdcSubscribeRequest |
stream CdcEvent |
Subscribe to cache mutations |
CdcEvent fields:
| Field | Type | Description |
|---|---|---|
sequence |
uint64 |
Per-node monotonic sequence |
timestamp_ms |
uint64 |
Wall-clock epoch ms |
event_type |
enum |
CDC_INSERT, CDC_REMOVE, CDC_FETCH_START, CDC_FETCH_CANCEL |
query_key |
string |
Cache key |
payload |
bytes |
JSON-serialized QueryResponse (INSERT only) |
upstream_id |
string |
Source upstream |
context_id |
string |
Context isolation |
origin_node_id |
string |
Originating node (echo prevention) |
absolute_expiry_ms |
uint64 |
Wall-clock expiry (0 = none) |
GetCacheDistill is a server-streaming RPC. The client sends a single
DistillRequest and the proxy streams zero or more DistillEntry messages
back. The stream closes naturally when all matching entries have been sent.
See Distill for the workflow and the conproxy distill CLI.
DistillRequest fields:
| Field | Type | Default | Description |
|---|---|---|---|
context |
string |
"" |
Filter to a single context ID (empty = all) |
tier |
uint32 |
0 |
0 = primary, 1 = semantic, 2 = both |
limit |
uint32 |
0 |
Max entries to return (0 = unlimited) |
include_stale |
bool |
false |
Include entries past their fresh TTL |
DistillEntry fields:
| Field | Type | Description |
|---|---|---|
query |
string |
Original query text (empty for legacy entries) |
context_id |
string |
Context the entry belongs to ("default" for pre-distill entries) |
upstream_id |
string |
Upstream that produced the response |
cached_at_ms |
uint64 |
Insertion time (Unix epoch milliseconds) |
extended_count |
uint32 |
Number of TTL extensions applied |
response_json |
bytes |
JSON-encoded QueryResponse payload |
hash_hex |
string |
blake3 query hash (64-char lowercase hex) |
embedding |
repeated float |
Embedding vector (semantic tier only; empty for primary) |
| RPC | Request | Response | Description |
|---|---|---|---|
GetStatus |
PeerStatusRequest |
PeerStatusResponse |
Peer status |
Snapshot |
SnapshotRequest |
stream CdcEvent |
Full cache snapshot |
The proto files are compiled during cargo build via tonic-build. To generate clients in other languages, use the proto files directly:
# Go
protoc --go_out=. --go-grpc_out=. proto/conproxy/v1/search.proto
# Python
python -m grpc_tools.protoc -Iproto --python_out=. --grpc_python_out=. proto/conproxy/v1/search.proto