HTTP API · 3. Query and retrieval
Thirty registrations answer questions rather than record them: four retrieval routes, twelve projection read models, one event stream, five ops and bootstrap routes, one embedding provider, and seven hosted surfaces that are not compiled into the Community Edition binary at all.
Planes covered: Query and retrieval · Projections · Events (SSE) · Ops self-observation and bootstrap · Compute provider · Hosted surfaces.
Read chapter 0 first for the auth model, request-field notation and error shape.
The four retrieval routes are POSTs that do not mutate anything. /v1/query/* is one of the prefixes where the route-auth contract accepts the same read scopes for GET and non-GET alike, query:read and admin:read, so a read token is sufficient even under enforce. The R/W column says R because that is what these routes do. Elsewhere in this chapter a POST does land in a write class; where that happens the row says so.
3.1 Query and retrieval
Four registrations.
| Method | Path | Handler | Auth | Request | Success response | Errors | Gate | R/W |
|---|---|---|---|---|---|---|---|---|
POST | /v1/query/text-search | query.rs:431 post_query_text_search | query:read, tenant-bound · Read | body: tenant_id (String, req), query (String, req), limit (usize, dflt default_text_search_limit), token_budget (usize, opt), min_score (f32, opt), mode (String, dflt), include_receipt (bool, dflt) | keys results, meta, availability, capability, coverage, coverage_score, below_floor, match_quality, query_terms, missing_tokens, gaps, segments_searched, source_label, score_space, score_merge_rule, mixed_profile_merge_rule, semantic_profile_id, local_semantic_profile, local_semantic_profile_id, embedding_fingerprint, dense_lane_active, reason_code, code, event_type | 400, 401, 404 | - | R |
POST | /v1/query/text-search/expand | query.rs:837 post_query_text_search_expand | query:read, tenant-bound · Read | body: tenant_id (String, req), result_ids (Vec ExpandResultId, req) | keys chunks, doc_id, frame_offset, segment_index, token_count, tokens_loaded, meta, score_space, source_label, semantic_profile_id, local_semantic_profile, local_semantic_profile_id, embedding_fingerprint | 400, 401, 404 | - | R |
POST | /v1/query/graph-expand | query.rs:148 post_query_graph_expand | query:read, tenant-bound · Read | body: tenant_id (String, req), seed_artifact_ids (Vec u32, req), edge_types (Vec String, dflt), max_hops (u32, dflt default_max_hops), budget (usize, dflt default_budget), min_confidence (f32, dflt), include_state (bool, dflt) | artifacts (Vec ArtifactResp), traversal_stats (StatsResp) | 400, 401, 404 | - | R |
POST | /v1/query/time-range | query.rs:274 post_query_time_range | query:read, tenant-bound · Read | body: tenant_id (String, req), start_micros (i64, req), end_micros (i64, req), artifact_ids (Vec u32, dflt), include_relations (bool, dflt), limit (usize, dflt default_time_range_limit) | artifacts (Vec ArtifactResp), traversal_stats (StatsResp) | 400, 401, 404 | - | R |
Read the availability fields, not just the results
POST /v1/query/text-search returns a set of fields whose job is to tell you what the answer is worth. Three matter most in production:
| Field | What it tells you |
|---|---|
dense_lane_active | Whether a dense retrieval lane contributed. Dense retrieval requires an embedding endpoint to be configured on the daemon (CORECRUXD_EMBEDDING_URL and CORECRUXD_EMBEDDING_MODEL, surfaced by GET /v1/console/settings at console.rs:146). There is no bundled embedder. On a daemon with no embedding endpoint this is false and the answer came from the non-dense path. |
coverage, coverage_score, below_floor | Whether the corpus actually covered the query, and whether the result fell under the configured floor. below_floor: true with results present is the shape to watch for: you got rows, and the daemon is telling you not to trust them. |
missing_tokens, gaps | Which query terms found nothing. This is the field that turns "the search is bad" into "the corpus does not contain this". |
embedding_fingerprint, semantic_profile_id and local_semantic_profile_id exist so you can detect a profile mismatch between what was indexed and what is being queried. A silently changed embedding model is otherwise invisible.
POST /v1/query/text-search/expand takes result ids from a prior search and returns the underlying chunks. It exists so a first call can stay inside a token budget and a second call can fetch only what the caller decided to read.
3.2 Projections
Twelve registrations. Projections are materialised read models over the event substrate. Eight sit under /v1/admin/projections/* and require admin:read or admin:write; four are the general entity projections.
Every /v1/admin/projections/artifacts/* route and the rebuild route can return 501. That is the honest signal that the projection dataplane is not present in this build, not a bug in your request.
| Method | Path | Handler | Auth | Request | Success response | Errors | Gate | R/W |
|---|---|---|---|---|---|---|---|---|
GET | /v1/admin/projections/meta | projections.rs:14 get_proj_meta | admin:read · AdminRead | query: shard_id (String, req) | Projection metadata | 404 | - | R |
GET | /v1/admin/projections/modules | projections.rs:91 get_projection_modules | admin:read · AdminRead | query: shard_id (String, dflt) | keys modules, module_id, module_version, module_refs, current_modules, artifact_living_state, artifact_relations, artifact_dependents, pressure_events, code_hash, config_hash, commit_id, dataplane_enabled, historical_replay_available, replay_availability, shard_id, source, status, schema | 404 | - | R |
POST | /v1/admin/projections/rebuild | projections.rs:39 post_projection_rebuild | admin:write · AdminWrite | - | keys status, shard, shards, commit_id, frames_processed, living_rows, relations_edges, error | 500, 501 | - | W |
GET | /v1/admin/projections/artifacts/{artifactId}/state | projections.rs:161 get_proj_artifact_state | admin:read · AdminRead | path artifactId; query: tenant_id (String, req) | tenant_id, artifact_id, present, living_status, confidence, last_validated_at_micros, next_review_at_micros, pressure_level, pressure_reasons_mask, trunk_tier, counts, updated_at_micros | 501 | - | R |
GET | /v1/admin/projections/artifacts/{artifactId}/relations | projections.rs:251 get_proj_artifact_relations | admin:read · AdminRead | path artifactId; query: tenant_id (String, req), direction (String, opt, in or out), relation_type (String, opt), limit (usize, opt), offset (usize, opt) | Same artifact-state shape, with relation counts | 501 | - | R |
GET | /v1/admin/projections/artifacts/{artifactId}/dependents | projections.rs:347 get_proj_artifact_dependents | admin:read · AdminRead | path artifactId; query: tenant_id (String, req), dependent_type (String, opt), limit (usize, opt), offset (usize, opt) | tenant_id, artifact_id, direction, relations, page | 501 | - | R |
GET | /v1/admin/projections/artifacts/{artifactId}/pressure-events | projections.rs:424 get_proj_artifact_pressure_events | admin:read · AdminRead | path artifactId; query: tenant_id (String, req), open_only (bool, opt), limit (usize, opt), offset (usize, opt) | tenant_id, artifact_id, dependents, page | 501 | - | R · receipt |
GET | /v1/projections/entity/count | projections.rs:508 get_entity_count | any-of query:read, admin:read · Read | query parameters as a string map | keys count, entity_type, items, predicate, tenant_id | 501 | - | R |
GET | /v1/projections/entity/timeline | projections.rs:546 get_entity_timeline | any-of query:read, admin:read · Read | query parameters as a string map | keys timeline, entity_name, entity_type, event_count, object_value, occurred_at, predicate, tenant_id | 501 | - | R |
GET | /v1/projections/entity/current-state | projections.rs:596 get_entity_current_state | any-of query:read, admin:read · Read | query parameters as a string map | keys current_value, previous_value, entity_name, predicate, occurred_at, not_found, tenant_id | 501 | - | R |
POST | /v1/projections/lookup | projections.rs:701 post_projection_lookup | admin:read · AdminWrite | body: projection (String, req), mode (String, dflt default_mode), key (String, dflt), vector (Vec f32, dflt), similarity_threshold (f32, dflt default_threshold) | keys hit, hit_count, entities, cache_key, chunk_hash, prompt_hash, confidence_mean, grammar_version, materialized, model, projection, source_tenant_id, total_rows, verifier_model, verifier_score, created_at_micros, last_hit_at_micros | 400, 404 | - | R, mounted as a write-class POST |
POST | /v1/projections/batch_lookup | projections.rs:758 post_projection_batch_lookup | admin:read · AdminWrite | body: projection (String, req), keys (Vec String, req) | keys results, hits, misses, count, entities, cache_key, confidence_mean, grammar_version, hit, materialized, model, projection, verifier_score | 404 | - | R, mounted as a write-class POST |
The two lookup routes are the clearest example of the contract-versus-handler split in the daemon: the route-auth class is AdminWrite because they are POSTs under an admin prefix, while the handler only asks for admin:read. Under enforce you need admin:write to reach a handler that then accepts admin:read. Provision admin:write.
3.3 Events (SSE)
One registration.
| Method | Path | Handler | Auth | Request | Success response | Errors | Gate | R/W |
|---|---|---|---|---|---|---|---|---|
GET | /v1/events/stream | events.rs:43 event_stream | query:read · Read | query: types (String, opt, comma-separated filter) | text/event-stream | 401 | - | R · receipt |
This is the only long-lived response on the HTTP surface, and it interacts with two global controls you should know about before you open one:
- The 30-second router timeout does not close an established stream, because the timeout applies to producing a response, not to the body that follows. It does apply to the initial connect.
- Every open stream occupies an in-flight slot against
CORECRUXD_MAX_INFLIGHT(default 1024). A client pool that opens and never closes streams will eventually load-shed unrelated traffic with503. Close streams you are not reading.
3.4 Ops self-observation and bootstrap
Five registrations. This is the daemon reporting on itself, through the same fact-store machinery it offers you.
| Method | Path | Handler | Auth | Request | Success response | Errors | Gate | R/W |
|---|---|---|---|---|---|---|---|---|
GET | /v1/ops/facts | observe.rs:22 query_ops_facts | any-of query:read, admin:read · Read | query parameters as a string map | keys facts, total_tokens | - | , | R |
GET | /v1/ops/errors | observe.rs:56 query_ops_errors | any-of query:read, admin:read · Read | query parameters as a string map | keys facts, total_tokens | - | , | R |
GET | /v1/ops/health | observe.rs:103 get_ops_health | any-of query:read, admin:read · Read | - | keys health | - | , | R |
POST | /v1/bootstrap/pull | observe.rs:156 post_bootstrap_pull | any-of query:read, admin:read · Read | body: query (String, req), top_k (usize, dflt default_bootstrap_top_k), token_budget (usize, opt) | keys facts, source, total_tokens | - | , | R, mounted as a write-class POST |
GET | /v1/bootstrap/status | observe.rs:182 get_bootstrap_status | any-of query:read, admin:read · Read | - | keys seeded, categories, fact_count, last_seed_at | - | , | R |
The scope correction. The repository's older docs/api-reference.md documents these five as requiring admin:read. The contract is wider: query:read, receipts:read, exports:read and admin:read are all accepted, and the handlers check any-of query:read, admin:read. You do not need an admin token to read ops facts.
These five operations are HTTP-only. The same document lists them as a live gRPC service, CoreCruxObserveV1. That service compiles but is never registered. See chapter 10.
Always pass token_budget on /v1/bootstrap/pull. The route returns total_tokens so you can see what you spent.
3.5 Compute provider
One registration. Daemon-to-daemon embeddings: one daemon offers embedding capacity to another over a dedicated scope.
| Method | Path | Handler | Auth | Request | Success response | Errors | Gate | R/W |
|---|---|---|---|---|---|---|---|---|
POST | /v1/compute/embed | compute.rs:63 post_compute_embed | compute:embed · FeatureGated | body: texts (Vec String, req), semantic_profile (SemanticProfile, dflt) | schema, embeddings, semantic_profile, receipt_id, receipt_session_id, receipt; on refusal keys availability, capability | 400, 403, 500, 503 | CORECRUXD_COMPUTE_PROVIDER, default off | W · receipt |
Three things are specific to this route:
compute:embedis the only scope that authorises it. Noadmin:*fallback. That is deliberate: an embedding provider should be reachable by a peer that can do nothing else.- The body limit is 512 KiB, not the global 16 MiB (compute.rs:25). A large batch is a
413. - With the flag off the route stays mounted and returns an explicit capability-disabled envelope rather than a
404. That is the opposite of most gated routes in this daemon and it is intentional: a peer needs to distinguish "not offered" from "not there".
3.6 Hosted surfaces: not in Community Edition
Seven registrations, compiled in only under the hosted-surfaces Cargo feature (mod.rs:1528). That feature is off in Community Edition, so on a CE binary these paths do not exist, the handlers are not compiled and nothing is mounted.
Status for every row below: FLAG, build feature hosted-surfaces, off in CE.
| Method | Path | Handler | Auth | Request | Success response | Errors | Gate | R/W |
|---|---|---|---|---|---|---|---|---|
GET | /v1/cloud/access-contract | cloud.rs:16 get_cloud_access_contract | any-of admin:read, query:read · Read | - | Access contract | - | build feature hosted-surfaces | R |
GET | /v1/gpu1/contract | gpu1.rs:320 get_gpu1_contract | any-of admin:read, query:read · FeatureGated | - | Surface contract, including endpoint_configured | - | build feature, plus CORECRUXD_GPU1_BASE_URL | R |
POST | /v1/gpu1/rerank | gpu1.rs:351 post_gpu1_rerank | admin:write, tenant-bound · FeatureGated | body: tenant_id (String, req), query (String, req), candidates (Vec Gpu1Evidence, dflt), top_k (usize, dflt), semantic_profile_id (String, dflt), local_semantic_profile_id (String, dflt), options (Value, dflt) | keys results, rank, score, score_space, reason, record_id, rerank_applied, source_label | - | build feature; credit burn needs CORECRUXD_CREDIT_METER | W |
POST | /v1/gpu1/answer | gpu1.rs:327 post_gpu1_answer | admin:write, tenant-bound · FeatureGated | body: tenant_id (String, req), question (String, req), evidence (Vec Gpu1Evidence, dflt), token_budget (usize, dflt), semantic_profile_id (String, dflt), local_semantic_profile_id (String, dflt), context_pack_receipt_id (String, dflt), options (Value, dflt) | keys answer, answer_available, message | - | build feature | W · receipt |
POST | /v1/gpu1/coverage | gpu1.rs:417 post_gpu1_coverage | admin:write, tenant-bound · FeatureGated | body: tenant_id (String, req), query (String, req), evidence (Vec Gpu1Evidence, dflt), coverage_floor (f32, dflt), semantic_profile_id (String, dflt), local_semantic_profile_id (String, dflt) | Coverage verdict | - | build feature | W |
POST | /v1/gpu1/enrich | gpu1.rs:391 post_gpu1_enrich | admin:write, tenant-bound · FeatureGated | body: tenant_id (String, req), tool_name (String, req), tool_parameters (Value, dflt), proposed_action (String, dflt), evidence (Vec Gpu1Evidence, dflt), semantic_profile_id (String, dflt), local_semantic_profile_id (String, dflt) | keys enriched, consequences, proposed_action, tool_name, message | - | build feature | W |
POST | /v1/gpu1/developer | gpu1.rs:437 post_gpu1_developer | admin:write, tenant-bound · FeatureGated | body: tenant_id (String, req), surface (String, req), prompt (String, dflt), route (String, dflt), evidence (Vec Gpu1Evidence, dflt), semantic_profile_id (String, dflt), local_semantic_profile_id (String, dflt), options (Value, dflt) | keys analysis_available, route, surface, message | - | build feature | W |
Even in a hosted build these need CORECRUXD_GPU1_BASE_URL. With it unset, GET /v1/gpu1/contract reports endpoint_configured: false and the compute routes return a fallback envelope rather than an error (gpu1.rs:961). Check answer_available, rerank_applied and analysis_available before you treat a 200 as a result. A 200 here does not mean the remote surface ran.
Note the asymmetry in the auth column: the route-auth contract for /v1/gpu1/* accepts query:read or admin:read, but the five compute handlers themselves require admin:write (gpu1.rs:778). The handler is the stricter gate. Provision admin:write.
3.7 Failure modes on this plane
| Symptom | Most likely cause |
|---|---|
501 from any /v1/projections/* or /v1/admin/projections/artifacts/* | The projection dataplane is not present in this build. Check dataplane_enabled on GET /v1/admin/projections/modules. |
dense_lane_active: false and results that feel lexical | No embedding endpoint is configured. There is no bundled embedder. Check GET /v1/console/settings. |
Results returned with below_floor: true | The daemon scored the match under its floor and is telling you so. Treat the rows as unqualified. |
A query that returns nothing, with missing_tokens populated | The corpus does not contain those terms. This is a coverage problem, not a ranking problem. |
Unrelated requests start getting 503 after opening many SSE streams | Each open stream holds an in-flight slot against CORECRUXD_MAX_INFLIGHT, default 1024. |
404 on every /v1/gpu1/* path | Community Edition. The hosted-surfaces feature is not compiled in. |
200 from /v1/gpu1/answer with answer_available: false | The build is hosted but CORECRUXD_GPU1_BASE_URL is unset, so you got the fallback envelope. |
413 on /v1/compute/embed well under 16 MiB | This route's limit is 512 KiB. Send fewer texts. |
403 on /v1/compute/embed with an admin token | compute:embed is the only accepted scope. There is no admin fallback. |
| A profile mismatch you cannot explain | Compare embedding_fingerprint and semantic_profile_id in the search response against what was indexed. A changed embedding model is otherwise silent. |
Sources
- crates/corecruxd/src/http/query.rs:74, query read scope
- crates/corecruxd/src/http/query.rs:95, tenant-bound
query:read - crates/corecruxd/src/http/events.rs:48, SSE scope
- crates/corecruxd/src/http/compute.rs:29,
compute:embed - crates/corecruxd/src/http/compute.rs:25, 512 KiB body limit
- crates/corecruxd/src/http/cloud.rs:17, cloud access-contract scopes
- crates/corecruxd/src/http/gpu1.rs:321, gpu1 contract scopes
- crates/corecruxd/src/http/gpu1.rs:778, gpu1 compute tenant-bound write scope
- crates/corecruxd/src/http/gpu1.rs:961,
CORECRUXD_GPU1_BASE_URL - crates/corecruxd/src/http/mod.rs:1528,
hosted-surfacesmount block - crates/corecruxd/src/config.rs:1265,
CORECRUXD_COMPUTE_PROVIDERdefault - crates/corecruxd/src/http/console.rs:146, embedding endpoint configuration surface

