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.

MethodPathHandlerAuthRequestSuccess responseErrorsGateR/W
POST/v1/query/text-searchquery.rs:431 post_query_text_searchquery:read, tenant-bound · Readbody: 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_type400, 401, 404-R
POST/v1/query/text-search/expandquery.rs:837 post_query_text_search_expandquery:read, tenant-bound · Readbody: 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_fingerprint400, 401, 404-R
POST/v1/query/graph-expandquery.rs:148 post_query_graph_expandquery:read, tenant-bound · Readbody: 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-rangequery.rs:274 post_query_time_rangequery:read, tenant-bound · Readbody: 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:

FieldWhat it tells you
dense_lane_activeWhether 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_floorWhether 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, gapsWhich 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.

MethodPathHandlerAuthRequestSuccess responseErrorsGateR/W
GET/v1/admin/projections/metaprojections.rs:14 get_proj_metaadmin:read · AdminReadquery: shard_id (String, req)Projection metadata404-R
GET/v1/admin/projections/modulesprojections.rs:91 get_projection_modulesadmin:read · AdminReadquery: 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, schema404-R
POST/v1/admin/projections/rebuildprojections.rs:39 post_projection_rebuildadmin:write · AdminWrite-keys status, shard, shards, commit_id, frames_processed, living_rows, relations_edges, error500, 501-W
GET/v1/admin/projections/artifacts/{artifactId}/stateprojections.rs:161 get_proj_artifact_stateadmin:read · AdminReadpath 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_micros501-R
GET/v1/admin/projections/artifacts/{artifactId}/relationsprojections.rs:251 get_proj_artifact_relationsadmin:read · AdminReadpath 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 counts501-R
GET/v1/admin/projections/artifacts/{artifactId}/dependentsprojections.rs:347 get_proj_artifact_dependentsadmin:read · AdminReadpath artifactId; query: tenant_id (String, req), dependent_type (String, opt), limit (usize, opt), offset (usize, opt)tenant_id, artifact_id, direction, relations, page501-R
GET/v1/admin/projections/artifacts/{artifactId}/pressure-eventsprojections.rs:424 get_proj_artifact_pressure_eventsadmin:read · AdminReadpath artifactId; query: tenant_id (String, req), open_only (bool, opt), limit (usize, opt), offset (usize, opt)tenant_id, artifact_id, dependents, page501-R · receipt
GET/v1/projections/entity/countprojections.rs:508 get_entity_countany-of query:read, admin:read · Readquery parameters as a string mapkeys count, entity_type, items, predicate, tenant_id501-R
GET/v1/projections/entity/timelineprojections.rs:546 get_entity_timelineany-of query:read, admin:read · Readquery parameters as a string mapkeys timeline, entity_name, entity_type, event_count, object_value, occurred_at, predicate, tenant_id501-R
GET/v1/projections/entity/current-stateprojections.rs:596 get_entity_current_stateany-of query:read, admin:read · Readquery parameters as a string mapkeys current_value, previous_value, entity_name, predicate, occurred_at, not_found, tenant_id501-R
POST/v1/projections/lookupprojections.rs:701 post_projection_lookupadmin:read · AdminWritebody: 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_micros400, 404-R, mounted as a write-class POST
POST/v1/projections/batch_lookupprojections.rs:758 post_projection_batch_lookupadmin:read · AdminWritebody: projection (String, req), keys (Vec String, req)keys results, hits, misses, count, entities, cache_key, confidence_mean, grammar_version, hit, materialized, model, projection, verifier_score404-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.

MethodPathHandlerAuthRequestSuccess responseErrorsGateR/W
GET/v1/events/streamevents.rs:43 event_streamquery:read · Readquery: types (String, opt, comma-separated filter)text/event-stream401-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 with 503. 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.

MethodPathHandlerAuthRequestSuccess responseErrorsGateR/W
GET/v1/ops/factsobserve.rs:22 query_ops_factsany-of query:read, admin:read · Readquery parameters as a string mapkeys facts, total_tokens-,R
GET/v1/ops/errorsobserve.rs:56 query_ops_errorsany-of query:read, admin:read · Readquery parameters as a string mapkeys facts, total_tokens-,R
GET/v1/ops/healthobserve.rs:103 get_ops_healthany-of query:read, admin:read · Read-keys health-,R
POST/v1/bootstrap/pullobserve.rs:156 post_bootstrap_pullany-of query:read, admin:read · Readbody: 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/statusobserve.rs:182 get_bootstrap_statusany-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.

MethodPathHandlerAuthRequestSuccess responseErrorsGateR/W
POST/v1/compute/embedcompute.rs:63 post_compute_embedcompute:embed · FeatureGatedbody: texts (Vec String, req), semantic_profile (SemanticProfile, dflt)schema, embeddings, semantic_profile, receipt_id, receipt_session_id, receipt; on refusal keys availability, capability400, 403, 500, 503CORECRUXD_COMPUTE_PROVIDER, default offW · receipt

Three things are specific to this route:

  • compute:embed is the only scope that authorises it. No admin:* 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.

MethodPathHandlerAuthRequestSuccess responseErrorsGateR/W
GET/v1/cloud/access-contractcloud.rs:16 get_cloud_access_contractany-of admin:read, query:read · Read-Access contract-build feature hosted-surfacesR
GET/v1/gpu1/contractgpu1.rs:320 get_gpu1_contractany-of admin:read, query:read · FeatureGated-Surface contract, including endpoint_configured-build feature, plus CORECRUXD_GPU1_BASE_URLR
POST/v1/gpu1/rerankgpu1.rs:351 post_gpu1_rerankadmin:write, tenant-bound · FeatureGatedbody: 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_METERW
POST/v1/gpu1/answergpu1.rs:327 post_gpu1_answeradmin:write, tenant-bound · FeatureGatedbody: 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 featureW · receipt
POST/v1/gpu1/coveragegpu1.rs:417 post_gpu1_coverageadmin:write, tenant-bound · FeatureGatedbody: 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 featureW
POST/v1/gpu1/enrichgpu1.rs:391 post_gpu1_enrichadmin:write, tenant-bound · FeatureGatedbody: 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 featureW
POST/v1/gpu1/developergpu1.rs:437 post_gpu1_developeradmin:write, tenant-bound · FeatureGatedbody: 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 featureW

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

SymptomMost 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 lexicalNo embedding endpoint is configured. There is no bundled embedder. Check GET /v1/console/settings.
Results returned with below_floor: trueThe 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 populatedThe corpus does not contain those terms. This is a coverage problem, not a ranking problem.
Unrelated requests start getting 503 after opening many SSE streamsEach open stream holds an in-flight slot against CORECRUXD_MAX_INFLIGHT, default 1024.
404 on every /v1/gpu1/* pathCommunity Edition. The hosted-surfaces feature is not compiled in.
200 from /v1/gpu1/answer with answer_available: falseThe build is hosted but CORECRUXD_GPU1_BASE_URL is unset, so you got the fallback envelope.
413 on /v1/compute/embed well under 16 MiBThis route's limit is 512 KiB. Send fewer texts.
403 on /v1/compute/embed with an admin tokencompute:embed is the only accepted scope. There is no admin fallback.
A profile mismatch you cannot explainCompare embedding_fingerprint and semantic_profile_id in the search response against what was indexed. A changed embedding model is otherwise silent.

Sources