# hev layer — full docs > Concatenated docs surface. Index at https://hevlayer.com/llms.txt. --- ## Search knowledge graph Version: 2 Generated: 2026-08-18T13:09:27.390Z Content hash: 6cea9754ab5ab794e5eb03596f4efef28fce8ebf881e3287488604749e4f5a87 Context: ## Layer (hev layer) Layer is a Kubernetes-native gateway and compute runtime for retrieval systems. Its Rust gateway fronts Turbopuffer and other supported stores with compatible query/write routes plus stable reads, cached fetches, scans, snapshots, history, federated search, automatic query routing, and reasoning-assisted retrieval. The operator reconciles desired state for stores, indexes, pipelines, functions, credentials, agents, external sources, and shared compute rules. ### Core concepts - **Storage and durability** — object storage holds durable history and artifacts; Aerospike provides ephemeral fast access; PostgreSQL is limited to indexing queues and state. - **Query routing and embedding** — Auto chooses lexical, semantic, or fused execution. An inline Embed supplies semantic input in one request and is resolved only when the chosen route needs a vector. - **Pipelines and Functions** — pipelines ingest external data and may change row count; Functions enrich rows already in Layer while preserving their row identity. - **Operations** — InfraRules defines compute pools, autoscaling limits, cache policy, and cold-start tradeoffs. The CLI and dashboard expose resources, workloads, credentials, progress, and failures. - **Identity and access** — ApiKey resources retain credential lifecycle and audit metadata; entitlements separate Layer access from opaque application claims. - **Installation** — AWS infrastructure and the Kubernetes runtime are provisioned separately, with local license verification gating advanced surfaces while core gateway operations remain available. Users commonly ask about the gateway, Turbopuffer compatibility, Auto routing, inline embedding, stable reads, warming the cache, scans, snapshots, pipeline versus Function behavior, compute pools, scale to zero, ApiKey rotation and revocation, warehouses, and operator reconciliation. The committed ask digest lets coding agents answer those questions from reviewable documentation before inspecting or changing a deployment. Glossary: - stable reads: Layer's default read mode: queries never see partially-indexed rows, pinned to an epoch-ms watermark echoed in the x-layer-stable-as-of header. Aliases: stable watermark, watermark, x-layer-stable-as-of. - document cache: Layer's pull-through Aerospike hot cache for document reads, pipeline chunks, and snapshot mirrors; misses fall through to turbopuffer or S3. Aliases: cache, aerospike cache. - HybridText: Layer-only rankby spelling that expands one query string into a BM25 leg plus per-token fuzzy legs, RRF-fused into one typo-tolerant ranking. Aliases: hybrid text fusion, fuzzy search. - Auto: Layer-only rankby spelling that picks hybridtext, semantic, or fused per query from the input's token count and echoes the decision in a routing block. Aliases: query routing, router, routing. - Embed: Query/write-time embedding expression resolved by serving mode: native (upstream), autoscaler (provider), or local/lattice (in-process CPU on the gateway). Aliases: embedding, lattice, local clip. - federated query: POST /v2/query runs one ranking across a set of namespaces and merges results into a single ranked list by distance or rank-interleave. Aliases: multi-namespace query, fan-out. - agentic search: A configured reasoning loop (POST /v2/agents/{name}/query) that reformulates a query, fans out for recall, scores relevance, and returns the standard federated row shape. Aliases: agent, agents endpoint. - snapshot: A content-addressed S3 facet histogram written after a namespace is observed stable; serves facet listings, counts, and checkpoint labels. Aliases: facet snapshot, facet histogram. - scan: On-demand row selection by filter, full-text, hybrid-text, or ANN radius, returning IDs, an exact/approximate count, or distinct field values. Aliases: count, values scan, id scan. - pipeline: A PostgreSQL-backed two-stage state machine: CPU workers stage chunks, GPU workers claim pending documents and write vectors, with leased claims and KEDA scale-to-zero. Aliases: indexing pipeline, chunking, ingestion pipeline. - layer install: The single CLI entrypoint that runs Terraform (AWS provisioning) then Helm (in-cluster release) to bring up a hev layer environment. Aliases: install, installer. - deriveFromStore: Default inbound auth mode where the upstream store's own API key doubles as the gateway bearer token (single-tenant BYOC shape). Aliases: derive from store. - keys mode: Inbound auth mode with gateway-only bearer keys carrying read, write, and admin scopes. Aliases: inbound keys, gateway keys. - compute pool: Named worker placement-and-resources bundle (built-in cpu, cpu-large, gpu) that Pipelines and Functions schedule onto; overridable via InfraRules/default. Aliases: pool, cpu pool, gpu pool. - warm window: Cooldown that holds an autoscaled worker and its node warm after the queue drains, so adjacent batches skip GPU cold starts. Aliases: warmWindowSeconds, cooldown. - Function: Kubernetes resource declaring a stateless per-row function run over rows already in an Index, with gateway-owned discovery, queueing, and retries. Aliases: UDF, user-defined function. - Warehouse: Declared upstream source (Snowflake, Hugging Face, REST) holding identity and credential with verified reachability; pipelines extract from it. Aliases: source system, data source. - ApiKey: Minted credential as a Kubernetes resource: Layer owns mint/verify/revoke/expire, with per-target entitlements, scopes, and opaque claims. Aliases: api key, minted key, scoped key. - license floor: License state where licensed surfaces degrade to CE behavior — no valid key, expired trial, or commercial key past expiration plus grace. Aliases: floor, grace, license state. - InfraRules: Cluster-scoped singleton policy object replacing the built-in compute pools and declaring document-cache and workload scaling rules. Aliases: scaling CRD, infra rules. - install profile: Install footprint selection: demo is the lean evaluation shape (~$40-70/mo at rest); indexing adds a dedicated NVMe document-cache node pool. Aliases: demo profile, indexing profile. Raw JSON: ```json { "version": 2, "generatedAt": "2026-08-18T13:09:27.390Z", "contentHash": "6cea9754ab5ab794e5eb03596f4efef28fce8ebf881e3287488604749e4f5a87", "context": "## Layer (hev layer)\n\nLayer is a Kubernetes-native gateway and compute runtime for retrieval systems. Its Rust gateway fronts Turbopuffer and other supported stores with compatible query/write routes plus stable reads, cached fetches, scans, snapshots, history, federated search, automatic query routing, and reasoning-assisted retrieval. The operator reconciles desired state for stores, indexes, pipelines, functions, credentials, agents, external sources, and shared compute rules.\n\n### Core concepts\n- **Storage and durability** — object storage holds durable history and artifacts; Aerospike provides ephemeral fast access; PostgreSQL is limited to indexing queues and state.\n- **Query routing and embedding** — Auto chooses lexical, semantic, or fused execution. An inline Embed supplies semantic input in one request and is resolved only when the chosen route needs a vector.\n- **Pipelines and Functions** — pipelines ingest external data and may change row count; Functions enrich rows already in Layer while preserving their row identity.\n- **Operations** — InfraRules defines compute pools, autoscaling limits, cache policy, and cold-start tradeoffs. The CLI and dashboard expose resources, workloads, credentials, progress, and failures.\n- **Identity and access** — ApiKey resources retain credential lifecycle and audit metadata; entitlements separate Layer access from opaque application claims.\n- **Installation** — AWS infrastructure and the Kubernetes runtime are provisioned separately, with local license verification gating advanced surfaces while core gateway operations remain available.\n\nUsers commonly ask about the gateway, Turbopuffer compatibility, Auto routing, inline embedding, stable reads, warming the cache, scans, snapshots, pipeline versus Function behavior, compute pools, scale to zero, ApiKey rotation and revocation, warehouses, and operator reconciliation. The committed ask digest lets coding agents answer those questions from reviewable documentation before inspecting or changing a deployment.", "glossary": [ { "term": "stable reads", "aliases": [ "stable watermark", "watermark", "x-layer-stable-as-of" ], "definition": "Layer's default read mode: queries never see partially-indexed rows, pinned to an epoch-ms watermark echoed in the x-layer-stable-as-of header." }, { "term": "document cache", "aliases": [ "cache", "aerospike cache" ], "definition": "Layer's pull-through Aerospike hot cache for document reads, pipeline chunks, and snapshot mirrors; misses fall through to turbopuffer or S3." }, { "term": "HybridText", "aliases": [ "hybrid text fusion", "fuzzy search" ], "definition": "Layer-only rankby spelling that expands one query string into a BM25 leg plus per-token fuzzy legs, RRF-fused into one typo-tolerant ranking." }, { "term": "Auto", "aliases": [ "query routing", "router", "routing" ], "definition": "Layer-only rankby spelling that picks hybridtext, semantic, or fused per query from the input's token count and echoes the decision in a routing block." }, { "term": "Embed", "aliases": [ "embedding", "lattice", "local clip" ], "definition": "Query/write-time embedding expression resolved by serving mode: native (upstream), autoscaler (provider), or local/lattice (in-process CPU on the gateway)." }, { "term": "federated query", "aliases": [ "multi-namespace query", "fan-out" ], "definition": "POST /v2/query runs one ranking across a set of namespaces and merges results into a single ranked list by distance or rank-interleave." }, { "term": "agentic search", "aliases": [ "agent", "agents endpoint" ], "definition": "A configured reasoning loop (POST /v2/agents/{name}/query) that reformulates a query, fans out for recall, scores relevance, and returns the standard federated row shape." }, { "term": "snapshot", "aliases": [ "facet snapshot", "facet histogram" ], "definition": "A content-addressed S3 facet histogram written after a namespace is observed stable; serves facet listings, counts, and checkpoint labels." }, { "term": "scan", "aliases": [ "count", "values scan", "id scan" ], "definition": "On-demand row selection by filter, full-text, hybrid-text, or ANN radius, returning IDs, an exact/approximate count, or distinct field values." }, { "term": "pipeline", "aliases": [ "indexing pipeline", "chunking", "ingestion pipeline" ], "definition": "A PostgreSQL-backed two-stage state machine: CPU workers stage chunks, GPU workers claim pending documents and write vectors, with leased claims and KEDA scale-to-zero." }, { "term": "layer install", "aliases": [ "install", "installer" ], "definition": "The single CLI entrypoint that runs Terraform (AWS provisioning) then Helm (in-cluster release) to bring up a hev layer environment." }, { "term": "deriveFromStore", "aliases": [ "derive from store" ], "definition": "Default inbound auth mode where the upstream store's own API key doubles as the gateway bearer token (single-tenant BYOC shape)." }, { "term": "keys mode", "aliases": [ "inbound keys", "gateway keys" ], "definition": "Inbound auth mode with gateway-only bearer keys carrying read, write, and admin scopes." }, { "term": "compute pool", "aliases": [ "pool", "cpu pool", "gpu pool" ], "definition": "Named worker placement-and-resources bundle (built-in cpu, cpu-large, gpu) that Pipelines and Functions schedule onto; overridable via InfraRules/default." }, { "term": "warm window", "aliases": [ "warmWindowSeconds", "cooldown" ], "definition": "Cooldown that holds an autoscaled worker and its node warm after the queue drains, so adjacent batches skip GPU cold starts." }, { "term": "Function", "aliases": [ "UDF", "user-defined function" ], "definition": "Kubernetes resource declaring a stateless per-row function run over rows already in an Index, with gateway-owned discovery, queueing, and retries." }, { "term": "Warehouse", "aliases": [ "source system", "data source" ], "definition": "Declared upstream source (Snowflake, Hugging Face, REST) holding identity and credential with verified reachability; pipelines extract from it." }, { "term": "ApiKey", "aliases": [ "api key", "minted key", "scoped key" ], "definition": "Minted credential as a Kubernetes resource: Layer owns mint/verify/revoke/expire, with per-target entitlements, scopes, and opaque claims." }, { "term": "license floor", "aliases": [ "floor", "grace", "license state" ], "definition": "License state where licensed surfaces degrade to CE behavior — no valid key, expired trial, or commercial key past expiration plus grace." }, { "term": "InfraRules", "aliases": [ "scaling CRD", "infra rules" ], "definition": "Cluster-scoped singleton policy object replacing the built-in compute pools and declaring document-cache and workload scaling rules." }, { "term": "install profile", "aliases": [ "demo profile", "indexing profile" ], "definition": "Install footprint selection: demo is the lean evaluation shape (~$40-70/mo at rest); indexing adds a dedicated NVMe document-cache node pool." } ], "overview": "## API\n- Agentic search — `api/agents`\n- Auth — `api/agents#auth`\n- Bring your own embedding — `api/agents#bring-your-own-embedding`\n- Configuration — `api/agents#configuration`\n- Provenance and trace — `api/agents#provenance-and-trace`\n- Request — `api/agents#request`\n- Response — `api/agents#response`\n- Validation — `api/agents#validation`\n- Blobs — `api/blobs`\n- Fetch — `api/blobs#fetch`\n- Routes — `api/blobs#routes`\n- Store — `api/blobs#store`\n- Warm Policy — `api/blobs#warm-policy`\n- Checkpoints — `api/checkpoints`\n- Create — `api/checkpoints#create`\n- List — `api/checkpoints#list`\n- Resolve — `api/checkpoints#resolve`\n- Routes — `api/checkpoints#routes`\n- VectorStores And Warehouses — `api/data-supply`\n- VectorStores — `api/data-supply#vectorstores`\n- Warehouses — `api/data-supply#warehouses`\n- Embed — `api/embed`\n- BYO model settings — `api/embed#byo-model-settings`\n- Embed with Auto — `api/embed#embed-with-auto`\n- End-to-end example — `api/embed#end-to-end-example`\n- Image input — `api/embed#image-input`\n- Lattice — `api/embed#lattice`\n- Local CLIP — `api/embed#local-clip`\n- Model provisioning — `api/embed#model-provisioning`\n- Performance accounting — `api/embed#performance-accounting`\n- Query with Embed — `api/embed#query-with-embed`\n- Text→image query — `api/embed#textimage-query`\n- Federated query — `api/federated-query`\n- Consistency — `api/federated-query#consistency`\n- Entitlements — `api/federated-query#entitlements`\n- Fan-out and fuse — `api/federated-query#fan-out-and-fuse`\n- Filters — `api/federated-query#filters`\n- Fusion options — `api/federated-query#fusion-options`\n- Limits — `api/federated-query#limits`\n- Merge — `api/federated-query#merge`\n- Partial failure — `api/federated-query#partial-failure`\n- Response — `api/federated-query#response`\n- Validation — `api/federated-query#validation`\n- Vector merge requires a matching embedding space — `api/federated-query#vector-merge-requires-a-matching-embedding-space`\n- Introduction — `api/introduction`\n- Authentication — `api/introduction#authentication`\n- Cache warm hint — GET /v1/namespaces/{ns}/hint_cache_warm — `api/introduction#cache-warm-hint--get-v1namespacesnshint_cache_warm`\n- Compatibility posture — `api/introduction#compatibility-posture`\n- Cross-cutting conventions — `api/introduction#cross-cutting-conventions`\n- Enhancements to upstream routes — `api/introduction#enhancements-to-upstream-routes`\n- Gateway failures — `api/introduction#gateway-failures`\n- Install — `api/introduction#install`\n- Metadata — GET /v2/namespaces/{ns}/metadata — `api/introduction#metadata--get-v2namespacesnsmetadata`\n- Query — POST /v2/namespaces/{ns}/query — `api/introduction#query--post-v2namespacesnsquery`\n- Write — POST /v2/namespaces/{ns} — `api/introduction#write--post-v2namespacesns`\n- API keys — `api/keys`\n- Authenticate — `api/keys#authenticate`\n- CLI — `api/keys#cli`\n- Key model — `api/keys#key-model`\n- kubectl — `api/keys#kubectl`\n- List and get — `api/keys#list-and-get`\n- Mint — `api/keys#mint`\n- Revoke by default — `api/keys#revoke-by-default`\n- Routes — `api/keys#routes`\n- Using a minted key — `api/keys#using-a-minted-key`\n- License — `api/license`\n- Client Call — `api/license#client-call`\n- Fields — `api/license#fields`\n- Response — `api/license#response`\n- State Effects — `api/license#state-effects`\n- Namespace metadata — `api/namespace-metadata`\n- List namespaces — `api/namespace-metadata#list-namespaces`\n- Request — `api/namespace-metadata#request`\n- The layer block — `api/namespace-metadata#the-layer-block`\n- Pipelines — `api/pipelines`\n- Deploy — `api/pipelines#deploy`\n- Document lifecycle — `api/pipelines#document-lifecycle`\n- Embed — `api/pipelines#embed`\n- Extract and chunk — `api/pipelines#extract-and-chunk`\n- Failure model — `api/pipelines#failure-model`\n- File tree — `api/pipelines#file-tree`\n- Trigger a run — `api/pipelines#trigger-a-run`\n- Wait for completion — `api/pipelines#wait-for-completion`\n- Query & Fetch — `api/query`\n- Batch fetch — `api/query#batch-fetch`\n- Batch query — `api/query#batch-query`\n- Behavior matrix — `api/query#behavior-matrix`\n- Counting matches — `api/query#counting-matches`\n- Fetch — `api/query#fetch`\n- Hybrid text fusion — `api/query#hybrid-text-fusion`\n- Options — `api/query#options`\n- Query by id — `api/query#query-by-id`\n- Query routing — `api/query#query-routing`\n- Rank expressions — `api/query#rank-expressions`\n- Response — `api/query#response`\n- Response — `api/query#response-1`\n- Routing policy — `api/query#routing-policy`\n- Semantics — `api/query#semantics`\n- Single fetch — `api/query#single-fetch`\n- Stable reads — `api/query#stable-reads`\n- Surfacing fallback — `api/query#surfacing-fallback`\n- Tokenization — `api/query#tokenization`\n- Validation — `api/query#validation`\n- Validation — `api/query#validation-1`\n- Response Headers — `api/response-headers`\n- Scan — `api/scans`\n- Auto-Mode Policy — `api/scans#auto-mode-policy`\n- Bounding ranked scans — `api/scans#bounding-ranked-scans`\n- Count Mode — `api/scans#count-mode`\n- Fan-out width — `api/scans#fan-out-width`\n- Filters — `api/scans#filters`\n- Full-text count — `api/scans#full-text-count`\n- High cardinality — `api/scans#high-cardinality`\n- Hybrid text count — `api/scans#hybrid-text-count`\n- ID Mode — `api/scans#id-mode`\n- Operational notes — `api/scans#operational-notes`\n- Precomputed serving — `api/scans#precomputed-serving`\n- Radius count — `api/scans#radius-count`\n- Routes — `api/scans#routes`\n- Sources — `api/scans#sources`\n- Values Mode — `api/scans#values-mode`\n- Query History — `api/search-history`\n- Clickstream entry — `api/search-history#clickstream-entry`\n- Query parameters — `api/search-history#query-parameters`\n- Routes — `api/search-history#routes`\n- Search history entry — `api/search-history#search-history-entry`\n- Storage — `api/search-history#storage`\n- Tag contract — `api/search-history#tag-contract`\n- Writing metadata — `api/search-history#writing-metadata`\n- Snapshot History — `api/snapshots`\n- Activity — `api/snapshots#activity`\n- History — `api/snapshots#history`\n- Manual snapshot — `api/snapshots#manual-snapshot`\n- Routes — `api/snapshots#routes`\n- Snapshot body — `api/snapshots#snapshot-body`\n- Snapshot policy — `api/snapshots#snapshot-policy`\n- Warm cache — `api/warm-cache`\n- Cache-cold behavior — `api/warm-cache#cache-cold-behavior`\n- Hint-cache warm — `api/warm-cache#hint-cache-warm`\n- Layer warm — `api/warm-cache#layer-warm`\n- Write & Stage — `api/write`\n- Stage — `api/write#stage`\n- Status — `api/write#status`\n## Operations\n- Layer CLI — `cli`\n- Ask The Docs — `cli#ask-the-docs`\n- Configuration — `cli#configuration`\n- Delete An Index — `cli#delete-an-index`\n- Environments — `cli#environments`\n- Initialize a Namespace — `cli#initialize-a-namespace`\n- Inspect An Index — `cli#inspect-an-index`\n- Install — `cli#install`\n- Install An AWS Environment — `cli#install-an-aws-environment`\n- Keys — `cli#keys`\n- Manage Snapshots — `cli#manage-snapshots`\n- Pipelines — `cli#pipelines`\n- Run A Function — `cli#run-a-function`\n- TUI — `cli#tui`\n- Vector Store And Warehouse — `cli#vector-store-and-warehouse`\n- Dashboard — `dashboard`\n- Access it needs — `dashboard#access-it-needs`\n- Basic auth — `dashboard#basic-auth`\n- Disabling the dashboard — `dashboard#disabling-the-dashboard`\n- Networking — `dashboard#networking`\n- Operational notes — `dashboard#operational-notes`\n- Pipeline queue states — `dashboard#pipeline-queue-states`\n- Failure Modes — `failure-modes`\n- Client failures — `failure-modes#client-failures`\n- Pipeline stop-writes — `failure-modes#pipeline-stop-writes`\n- Read — `failure-modes#read`\n- Write — `failure-modes#write`\n- Install — `install`\n- Built-in compute pools — `install#built-in-compute-pools`\n- Cluster: recommended — `install#cluster-recommended`\n- Cost notes — `install#cost-notes`\n- Gateway auth modes — `install#gateway-auth-modes`\n- Helm — `install#helm`\n- Image Coordinates — `install#image-coordinates`\n- Install shape — `install#install-shape`\n- install status — `install#install-status`\n- install uninstall — `install#install-uninstall`\n- layer install — `install#layer-install`\n- Layer-Operated Search — `install#layer-operated-search`\n- Local gateway development — `install#local-gateway-development`\n- Outputs — `install#outputs`\n- Profiles — `install#profiles`\n- Required values — `install#required-values`\n- Run the install — `install#run-the-install`\n- Terraform — `install#terraform`\n- What gets installed — `install#what-gets-installed`\n- What it sets up — `install#what-it-sets-up`\n- Agent CRD — `kubernetes/agent-crd`\n- Auth — `kubernetes/agent-crd#auth`\n- Budget — `kubernetes/agent-crd#budget`\n- Indices — `kubernetes/agent-crd#indices`\n- Model — `kubernetes/agent-crd#model`\n- Naming — `kubernetes/agent-crd#naming`\n- Observability — `kubernetes/agent-crd#observability`\n- Output — `kubernetes/agent-crd#output`\n- Retrieval — `kubernetes/agent-crd#retrieval`\n- Status — `kubernetes/agent-crd#status`\n- ApiKey CRD — `kubernetes/apikey-crd`\n- Backup and migration — `kubernetes/apikey-crd#backup-and-migration`\n- Bootstrapping — `kubernetes/apikey-crd#bootstrapping`\n- Entitlements — `kubernetes/apikey-crd#entitlements`\n- Kubernetes RBAC — `kubernetes/apikey-crd#kubernetes-rbac`\n- Minting — `kubernetes/apikey-crd#minting`\n- Spec — `kubernetes/apikey-crd#spec`\n- Verification — `kubernetes/apikey-crd#verification`\n- Function CRD — `kubernetes/function-crd`\n- GPU classifier — `kubernetes/function-crd#gpu-classifier`\n- Lifecycle — `kubernetes/function-crd#lifecycle`\n- Scaling — `kubernetes/function-crd#scaling`\n- Selection — `kubernetes/function-crd#selection`\n- Simple classifier — `kubernetes/function-crd#simple-classifier`\n- Tuning knobs — `kubernetes/function-crd#tuning-knobs`\n- Version markers — `kubernetes/function-crd#version-markers`\n- Worker — `kubernetes/function-crd#worker`\n- Writeback — `kubernetes/function-crd#writeback`\n- Index CRD — `kubernetes/index-crd`\n- Backend — `kubernetes/index-crd#backend`\n- Cache policy — `kubernetes/index-crd#cache-policy`\n- Chunking — `kubernetes/index-crd#chunking`\n- Embedding — `kubernetes/index-crd#embedding`\n- Scan policy — `kubernetes/index-crd#scan-policy`\n- Schema-attribute embedding — `kubernetes/index-crd#schema-attribute-embedding`\n- Search backend policy — `kubernetes/index-crd#search-backend-policy`\n- Serving mode — `kubernetes/index-crd#serving-mode`\n- Snapshot policy — `kubernetes/index-crd#snapshot-policy`\n- Status — `kubernetes/index-crd#status`\n- Operator Overview — `kubernetes/operator`\n- CRDs — `kubernetes/operator#crds`\n- Relationship to the gateway — `kubernetes/operator#relationship-to-the-gateway`\n- Scheduling and node pools — `kubernetes/operator#scheduling-and-node-pools`\n- Pipeline CRD — `kubernetes/pipeline-crd`\n- Chunking — `kubernetes/pipeline-crd#chunking`\n- Pipeline id — `kubernetes/pipeline-crd#pipeline-id`\n- Scaling — `kubernetes/pipeline-crd#scaling`\n- Schedule — `kubernetes/pipeline-crd#schedule`\n- Source — `kubernetes/pipeline-crd#source`\n- Status — `kubernetes/pipeline-crd#status`\n- Target — `kubernetes/pipeline-crd#target`\n- Typed sources — `kubernetes/pipeline-crd#typed-sources`\n- Worker — `kubernetes/pipeline-crd#worker`\n- InfraRules CRD — `kubernetes/scaling-crd`\n- Compute pools — `kubernetes/scaling-crd#compute-pools`\n- Document cache rules — `kubernetes/scaling-crd#document-cache-rules`\n- InfraRules — `kubernetes/scaling-crd#infrarules`\n- Warm window — `kubernetes/scaling-crd#warm-window`\n- Workload scaling — `kubernetes/scaling-crd#workload-scaling`\n- VectorStore CRD — `kubernetes/vectorstore-crd`\n- Connection — `kubernetes/vectorstore-crd#connection`\n- Inbound auth — `kubernetes/vectorstore-crd#inbound-auth`\n- Routing — `kubernetes/vectorstore-crd#routing`\n- Standalone config — `kubernetes/vectorstore-crd#standalone-config`\n- Status — `kubernetes/vectorstore-crd#status`\n- Warehouse CRD — `kubernetes/warehouse-crd`\n- Connection — `kubernetes/warehouse-crd#connection`\n- Deletion — `kubernetes/warehouse-crd#deletion`\n- Hugging Face — `kubernetes/warehouse-crd#hugging-face`\n- Keys — `kubernetes/warehouse-crd#keys`\n- Pipeline source — `kubernetes/warehouse-crd#pipeline-source`\n- REST — `kubernetes/warehouse-crd#rest`\n- REST / HTTP JSON API — `kubernetes/warehouse-crd#rest--http-json-api`\n- Rotation — `kubernetes/warehouse-crd#rotation`\n- Snowflake — `kubernetes/warehouse-crd#snowflake`\n- Snowflake — `kubernetes/warehouse-crd#snowflake-1`\n- Status — `kubernetes/warehouse-crd#status`\n- Supported Warehouses — `kubernetes/warehouse-crd#supported-warehouses`\n- Verification — `kubernetes/warehouse-crd#verification`\n- Quickstart — `quickstart`\n- 1. Clone the repo — `quickstart#1-clone-the-repo`\n- 2. Configure and start the gateway — `quickstart#2-configure-and-start-the-gateway`\n- 3. Initialize the namespace — `quickstart#3-initialize-the-namespace`\n- 4. Run a query — `quickstart#4-run-a-query`\n## Overview\n- Agents — `agents`\n- 1. Install the CLIs — `agents#1-install-the-clis`\n- 2. Add the docs skill — `agents#2-add-the-docs-skill`\n- 3. Add the layer CLI skill — `agents#3-add-the-layer-cli-skill`\n- 4. Ask — `agents#4-ask`\n- The verbs — `agents#the-verbs`\n- Why answers stay grounded — `agents#why-answers-stay-grounded`\n- Concepts — `concepts`\n- Control loops — `concepts#control-loops`\n- Document cache — `concepts#document-cache`\n- Gateway enhancements — `concepts#gateway-enhancements`\n- Glossary — `concepts#glossary`\n- Kubernetes autoscaling — `concepts#kubernetes-autoscaling`\n- Scatter/gather — `concepts#scattergather`\n- Demos — `demos`\n- chart — clinical patient-notes search that shows its routing — `demos#chart--clinical-patient-notes-search-that-shows-its-routing`\n- hybrid-text — hybrid text fusion over SciFact — `demos#hybrid-text--hybrid-text-fusion-over-scifact`\n- lens — text-to-image search with zero GPUs — `demos#lens--text-to-image-search-with-zero-gpus`\n- shelf — book search that shows its routing — `demos#shelf--book-search-that-shows-its-routing`\n- shop — semantic shopping, everything together — `demos#shop--semantic-shopping-everything-together`\n- wiki — all of Simple English Wikipedia, routed and embedded on CPU — `demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu`\n- Document model — `document-model`\n- FAQ — `faq`\n- How do I know whether my license is healthy? — `faq#how-do-i-know-whether-my-license-is-healthy`\n- How do I start a trial? — `faq#how-do-i-start-a-trial`\n- How much will it cost? — `faq#how-much-will-it-cost`\n- What is the licensing for hev layer? — `faq#what-is-the-licensing-for-hev-layer`\n- Who built hev layer? — `faq#who-built-hev-layer`\n- No Guarantees — `guarantees`\n- Commitments — `guarantees#commitments`\n- Introduction — `index`\n- Licensing — `licensing`\n- Client SDKs — `licensing#client-sdks`\n- End-to-End Runbook — `licensing#end-to-end-runbook`\n- Governing Terms — `licensing#governing-terms`\n- Install the Key — `licensing#install-the-key`\n- License Claims — `licensing#license-claims`\n- License States — `licensing#license-states`\n- Metrics — `licensing#metrics`\n- Renewals — `licensing#renewals`\n- Start a Trial — `licensing#start-a-trial`\n- Limits — `limits`\n- No limits — `limits#no-limits`\n- Changelog — `roadmap`\n- 0.4 — `roadmap#04`\n- API hardening — `roadmap#api-hardening`\n- Lifecycle and operability — `roadmap#lifecycle-and-operability`\n- Polish — `roadmap#polish`\n- Search — `roadmap#search`\n- Surfaces — `roadmap#surfaces`\n- Up Next — `roadmap#up-next`\n- Tradeoffs — `tradeoffs`", "suggestions": [ "How do I retire an API key without losing its audit trail?", "What's the difference between a pipeline and a Function?", "How do stable reads behave while an index is updating?", "What happens when the document cache is unavailable?", "How do compute pools and scale to zero work?" ], "nodes": [ { "id": "agents", "kind": "section", "title": "Agents", "heading": null, "group": "Overview", "url": "/docs/agents", "summary": "The Layer docs are queryable from the command line by coding agents: the same engine behind the site's search ships as the ask CLI (no scraping, MCP server, or API key), and the layer CLI lets agents operate environments, indexes, pipelines, UDFs, and Function runs. Skills are plain SKILL.md files pasted into any harness's skill directory or AGENTS.md.", "facts": [ { "kind": "code", "literal": "⌘K", "chunkId": "agents" }, { "kind": "code", "literal": "layer", "chunkId": "agents" }, { "kind": "code", "literal": "SKILL.md", "chunkId": "agents" }, { "kind": "code", "literal": "AGENTS.md", "chunkId": "agents" }, { "kind": "value", "literal": "Callout.astro", "chunkId": "agents" } ], "sources": [ { "chunkId": "agents", "url": "/docs/agents", "anchor": null } ], "mode": "agent-primary", "terms": [ "layer", "docs", "queryable", "command", "line", "coding", "agents", "same", "engine", "behind", "site", "search", "ships", "scraping", "server", "lets", "operate", "environments", "indexes", "pipelines", "udfs", "function", "runs", "skills", "plain", "skill", "files", "pasted", "harness", "directory", "callout", "astro", "agent", "file", "work", "across", "harnesses", "these", "read", "cite" ] }, { "id": "agents#1-install-the-clis", "kind": "section", "title": "Agents", "heading": "1. Install the CLIs", "group": "Overview", "url": "/docs/agents#1-install-the-clis", "summary": "Install the self-contained ask binary with go install; any harness that can run shell commands can use it. From a Layer checkout, build the layer CLI with go build when the agent should operate environments rather than only search docs.", "facts": [ { "kind": "code", "literal": "go install github.com/hev/ask/cmd/ask@latest", "chunkId": "agents#1-install-the-clis" }, { "kind": "code", "literal": "go build -o layer ./apps/layer-cli", "chunkId": "agents#1-install-the-clis" }, { "kind": "code", "literal": "ask", "chunkId": "agents#1-install-the-clis" }, { "kind": "code", "literal": "layer", "chunkId": "agents#1-install-the-clis" } ], "sources": [ { "chunkId": "agents#1-install-the-clis", "url": "/docs/agents#1-install-the-clis", "anchor": "1-install-the-clis" } ], "mode": "agent-primary", "terms": [ "install", "clis", "self", "contained", "binary", "harness", "shell", "commands", "layer", "checkout", "build", "agent", "should", "operate", "environments", "rather", "only", "search", "docs", "github", "latest", "apps", "command", "instead", "searching" ] }, { "id": "agents#2-add-the-docs-skill", "kind": "section", "title": "Agents", "heading": "2. Add the docs skill", "group": "Overview", "url": "/docs/agents#2-add-the-docs-skill", "summary": "Creates a hevlayer-docs SKILL.md in the harness's skill directory that teaches an agent to answer Layer questions from the docs via keyless ask verbs (search, section get, overview, glossary get) against the hevlayer.com/api/ask endpoint, citing returned section URLs.", "facts": [ { "kind": "code", "literal": "AGENT_SKILL_HOME", "chunkId": "agents#2-add-the-docs-skill" }, { "kind": "code", "literal": "~/.codex/skills", "chunkId": "agents#2-add-the-docs-skill" }, { "kind": "code", "literal": "~/.claude/skills", "chunkId": "agents#2-add-the-docs-skill" } ], "sources": [ { "chunkId": "agents#2-add-the-docs-skill", "url": "/docs/agents#2-add-the-docs-skill", "anchor": "2-add-the-docs-skill" } ], "mode": "agent-primary", "terms": [ "docs", "skill", "creates", "hevlayer", "harness", "directory", "teaches", "agent", "answer", "layer", "questions", "keyless", "verbs", "search", "section", "overview", "glossary", "against", "endpoint", "citing", "returned", "urls", "home", "codex", "skills", "claude", "agentskillhome", "such", "code", "codexhome", "mkdir", "name", "description", "query", "user", "asks", "about", "turbopuffer", "gateway", "stable" ] }, { "id": "agents#3-add-the-layer-cli-skill", "kind": "section", "title": "Agents", "heading": "3. Add the layer CLI skill", "group": "Overview", "url": "/docs/agents#3-add-the-layer-cli-skill", "summary": "Creates a hevlayer-layer-cli SKILL.md that separates read-only inspection (layer -o json env/index/pipeline/udf list and get), docs lookup via layer ask, and mutating operations; only layer run needs Kubernetes access, and the skill instructs confirming environment, gateway URL, kube context, and namespace before mutating.", "facts": [ { "kind": "code", "literal": "layer", "chunkId": "agents#3-add-the-layer-cli-skill" } ], "sources": [ { "chunkId": "agents#3-add-the-layer-cli-skill", "url": "/docs/agents#3-add-the-layer-cli-skill", "anchor": "3-add-the-layer-cli-skill" } ], "mode": "agent-primary", "terms": [ "layer", "skill", "creates", "hevlayer", "separates", "read", "only", "inspection", "json", "index", "pipeline", "list", "docs", "lookup", "mutating", "operations", "needs", "kubernetes", "access", "instructs", "confirming", "environment", "gateway", "kube", "context", "namespace", "before", "agent", "should", "inspect", "operate", "through", "keeps", "separate", "agentskillhome", "codexhome", "home", "codex", "skills", "mkdir" ] }, { "id": "agents#4-ask", "kind": "section", "title": "Agents", "heading": "4. Ask", "group": "Overview", "url": "/docs/agents#4-ask", "summary": "Worked example of the ask search verb: a query returns ranked results with title, heading, url, group, and snippet; the agent then runs section get on the winning id and answers with the citation.", "facts": [ { "kind": "code", "literal": "ask --endpoint https://hevlayer.com/api/ask search \"cache is down\"", "chunkId": "agents#4-ask" }, { "kind": "code", "literal": "{\n \"results\": [\n {\n \"title\": \"Concepts\",\n \"heading\": \"Document cache\",\n \"url\": \"/docs/concepts#document-cache\",\n \"group\": \"Overview\",\n \"snippet\": \"The document cache does two jobs: pull-through document reads...\"\n }\n ]\n}", "chunkId": "agents#4-ask" }, { "kind": "code", "literal": "section get", "chunkId": "agents#4-ask" } ], "sources": [ { "chunkId": "agents#4-ask", "url": "/docs/agents#4-ask", "anchor": "4-ask" } ], "mode": "agent-primary", "terms": [ "worked", "example", "search", "verb", "query", "returns", "ranked", "results", "title", "heading", "group", "snippet", "agent", "runs", "section", "winning", "answers", "citation", "endpoint", "https", "hevlayer", "cache", "down", "concepts", "document", "docs", "overview", "does", "jobs", "pull", "through", "reads", "here", "typically" ] }, { "id": "agents#the-verbs", "kind": "section", "title": "Agents", "heading": "The verbs", "group": "Overview", "url": "/docs/agents#the-verbs", "summary": "The four ask verbs: overview returns orientation plus the full section map with stable ids; search returns ranked sections with snippets and deep links; section get returns one section's summary, exact identifiers, and source URL; glossary get resolves a product term through its aliases.", "facts": [ { "kind": "code", "literal": "overview", "chunkId": "agents#the-verbs" }, { "kind": "code", "literal": "search \"\"", "chunkId": "agents#the-verbs" }, { "kind": "code", "literal": "section get \"\"", "chunkId": "agents#the-verbs" }, { "kind": "code", "literal": "glossary get \"\"", "chunkId": "agents#the-verbs" }, { "kind": "code", "literal": "watermark", "chunkId": "agents#the-verbs" } ], "sources": [ { "chunkId": "agents#the-verbs", "url": "/docs/agents#the-verbs", "anchor": "the-verbs" } ], "mode": "agent-primary", "terms": [ "verbs", "four", "overview", "returns", "orientation", "plus", "full", "section", "stable", "search", "ranked", "sections", "snippets", "deep", "links", "summary", "exact", "identifiers", "source", "glossary", "resolves", "product", "term", "through", "aliases", "query", "watermark", "verb", "context", "resolved" ] }, { "id": "agents#why-answers-stay-grounded", "kind": "section", "title": "Agents", "heading": "Why answers stay grounded", "group": "Overview", "url": "/docs/agents#why-answers-stay-grounded", "summary": "Search runs over a committed, reviewable digest of the same docs corpus, with every anchor CI-verified against rendered pages, so cited deep links always resolve. Every verb is a keyless read; the docs are also available as plain text at /llms.txt and /llms-full.txt, but the CLI ranks, resolves aliases, and costs fewer tokens.", "facts": [ { "kind": "value", "literal": "llms.txt", "chunkId": "agents#why-answers-stay-grounded" }, { "kind": "value", "literal": "llms-full.txt", "chunkId": "agents#why-answers-stay-grounded" } ], "sources": [ { "chunkId": "agents#why-answers-stay-grounded", "url": "/docs/agents#why-answers-stay-grounded", "anchor": "why-answers-stay-grounded" } ], "mode": "agent-primary", "terms": [ "answers", "stay", "grounded", "search", "runs", "committed", "reviewable", "digest", "same", "docs", "corpus", "every", "anchor", "verified", "against", "rendered", "pages", "cited", "deep", "links", "always", "resolve", "verb", "keyless", "read", "also", "available", "plain", "text", "llms", "full", "ranks", "resolves", "aliases", "costs", "fewer", "tokens", "these", "heading", "renders" ] }, { "id": "api/agents", "kind": "section", "title": "Agentic search", "heading": null, "group": "API", "url": "/docs/api/agents", "summary": "Agentic search runs a configured reasoning loop over one or more namespaces via POST /v2/agents/{name}/query: a model reads the query, fans out diverse phrasings for recall, ranks candidates for relevance, and returns a fused ranking in the standard federated row shape. It is a better-ranked result set, not a generated answer; model, turn budget, indices, and output shaping are bound on the Agent resource.", "facts": [ { "kind": "code", "literal": "POST /v2/agents/{name}/query", "chunkId": "api/agents" }, { "kind": "code", "literal": "/v2/query", "chunkId": "api/agents" }, { "kind": "code", "literal": "Agent", "chunkId": "api/agents" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/agents" } ], "sources": [ { "chunkId": "api/agents", "url": "/docs/api/agents", "anchor": null } ], "mode": "source-primary", "terms": [ "agentic", "search", "runs", "configured", "reasoning", "loop", "more", "namespaces", "post", "agents", "name", "query", "model", "reads", "fans", "diverse", "phrasings", "recall", "ranks", "candidates", "relevance", "returns", "fused", "ranking", "standard", "federated", "shape", "better", "ranked", "result", "generated", "answer", "turn", "budget", "indices", "output", "shaping", "bound", "agent", "resource" ] }, { "id": "api/agents#auth", "kind": "section", "title": "Agentic search", "heading": "Auth", "group": "API", "url": "/docs/api/agents#auth", "summary": "Agent auth follows the standard API model and federated-query auth behavior for multi-namespace queries; a minted key additionally needs an agent entitlement on its ApiKey to invoke the agent.", "facts": [ { "kind": "code", "literal": "agent.", "chunkId": "api/agents#auth" } ], "sources": [ { "chunkId": "api/agents#auth", "url": "/docs/api/agents#auth", "anchor": "auth" } ], "mode": "source-primary", "terms": [ "auth", "agent", "follows", "standard", "model", "federated", "query", "behavior", "multi", "namespace", "queries", "minted", "additionally", "needs", "entitlement", "apikey", "invoke", "name", "same", "other", "endpoints", "follow" ] }, { "id": "api/agents#bring-your-own-embedding", "kind": "section", "title": "Agentic search", "heading": "Bring your own embedding", "group": "API", "url": "/docs/api/agents#bring-your-own-embedding", "summary": "The agent fans out lexically on your query text and semantically on a query vector you supply — Layer never embeds query text. The one supplied vector is used for every planned semantic leg (reformulations are not embedded); omitting it drops the semantic legs to lexical-only, which is correct when there's no vector column or client embedder.", "facts": [ { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/agents/support-search/query\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"query\": \"auth errors after the june upgrade\",\n \"vector\": [0.0123, -0.0456, 0.0789],\n \"top_k\": 20\n }'", "chunkId": "api/agents#bring-your-own-embedding" }, { "kind": "code", "literal": "/v2/query", "chunkId": "api/agents#bring-your-own-embedding" }, { "kind": "code", "literal": "vector", "chunkId": "api/agents#bring-your-own-embedding" }, { "kind": "code", "literal": "query", "chunkId": "api/agents#bring-your-own-embedding" } ], "sources": [ { "chunkId": "api/agents#bring-your-own-embedding", "url": "/docs/api/agents#bring-your-own-embedding", "anchor": "bring-your-own-embedding" } ], "mode": "source-primary", "terms": [ "bring", "embedding", "agent", "fans", "lexically", "query", "text", "semantically", "vector", "supply", "layer", "never", "embeds", "supplied", "every", "planned", "semantic", "reformulations", "embedded", "omitting", "drops", "legs", "lexical", "only", "correct", "there", "column", "client", "embedder", "curl", "post", "gateway", "agents", "support", "search", "authorization", "bearer", "content", "type", "application" ] }, { "id": "api/agents#configuration", "kind": "section", "title": "Agentic search", "heading": "Configuration", "group": "API", "url": "/docs/api/agents#configuration", "summary": "Everything the request omits — model and credential, deadline, indices, fan-out and fusion weighting, output shaping — is bound on the Agent resource, which can be authored equivalently via kubectl or the SDK's agent apply call.", "facts": [ { "kind": "code", "literal": "Agent", "chunkId": "api/agents#configuration" }, { "kind": "code", "literal": "kubectl get agent -o yaml", "chunkId": "api/agents#configuration" }, { "kind": "code", "literal": "client.agent(\"support-search\").apply()", "chunkId": "api/agents#configuration" } ], "sources": [ { "chunkId": "api/agents#configuration", "url": "/docs/api/agents#configuration", "anchor": "configuration" } ], "mode": "source-primary", "terms": [ "configuration", "everything", "request", "omits", "model", "credential", "deadline", "indices", "fusion", "weighting", "output", "shaping", "bound", "agent", "resource", "authored", "equivalently", "kubectl", "apply", "call", "yaml", "client", "support", "search", "spellings", "same", "object" ] }, { "id": "api/agents#provenance-and-trace", "kind": "section", "title": "Agentic search", "heading": "Provenance and trace", "group": "API", "url": "/docs/api/agents#provenance-and-trace", "summary": "With provenance on, the response gains an agent echo and each row carries a $agent field with retrievalScore (first-leg rank), relevanceScore (model-graded), the planned query variant, and its index; the agent block reports turns, deadlineHit, recallDepth, relevanceWeight, and the planned queries. The full reasoning trace goes to search history whether or not it is echoed.", "facts": [ { "kind": "code", "literal": "agent", "chunkId": "api/agents#provenance-and-trace" }, { "kind": "code", "literal": "$agent", "chunkId": "api/agents#provenance-and-trace" }, { "kind": "code", "literal": "$agent.retrievalScore", "chunkId": "api/agents#provenance-and-trace" }, { "kind": "code", "literal": "fanout", "chunkId": "api/agents#provenance-and-trace" }, { "kind": "code", "literal": "$agent.relevanceScore", "chunkId": "api/agents#provenance-and-trace" }, { "kind": "code", "literal": "$agent.query", "chunkId": "api/agents#provenance-and-trace" }, { "kind": "code", "literal": "$agent.queryIndex", "chunkId": "api/agents#provenance-and-trace" }, { "kind": "code", "literal": "agent.queries", "chunkId": "api/agents#provenance-and-trace" }, { "kind": "code", "literal": "agent.turns", "chunkId": "api/agents#provenance-and-trace" }, { "kind": "code", "literal": "agent.deadlineHit", "chunkId": "api/agents#provenance-and-trace" }, { "kind": "code", "literal": "output.trace", "chunkId": "api/agents#provenance-and-trace" } ], "sources": [ { "chunkId": "api/agents#provenance-and-trace", "url": "/docs/api/agents#provenance-and-trace", "anchor": "provenance-and-trace" } ], "mode": "source-primary", "terms": [ "provenance", "trace", "response", "gains", "agent", "echo", "carries", "field", "retrievalscore", "first", "rank", "relevancescore", "model", "graded", "planned", "query", "variant", "index", "block", "reports", "turns", "deadlinehit", "recalldepth", "relevanceweight", "queries", "full", "reasoning", "goes", "search", "history", "whether", "echoed", "fanout", "queryindex", "output", "both", "scores", "rows", "4821", "namespace" ] }, { "id": "api/agents#request", "kind": "section", "title": "Agentic search", "heading": "Request", "group": "API", "url": "/docs/api/agents#request", "summary": "The request body is just query, an optional vector, and topk — query and vector are data, not config, and there are no per-request overrides of the agent's configured behavior. Examples are shown in Python, Go, TypeScript, and cURL.", "facts": [ { "kind": "code", "literal": "response = await client.agent(\"support-search\").query({\n \"query\": \"auth errors after the june upgrade\",\n \"top_k\": 20,\n})", "chunkId": "api/agents#request" }, { "kind": "code", "literal": "response, err := client.Agent(\"support-search\").Query(ctx, &hevlayer.AgentQueryRequest{\n Query: \"auth errors after the june upgrade\",\n TopK: 20,\n})", "chunkId": "api/agents#request" }, { "kind": "code", "literal": "const response = await client.agent(\"support-search\").query({\n query: \"auth errors after the june upgrade\",\n top_k: 20,\n});", "chunkId": "api/agents#request" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/agents/support-search/query\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"query\": \"auth errors after the june upgrade\",\n \"top_k\": 20\n }'", "chunkId": "api/agents#request" }, { "kind": "code", "literal": "query", "chunkId": "api/agents#request" }, { "kind": "code", "literal": "vector", "chunkId": "api/agents#request" }, { "kind": "code", "literal": "top_k", "chunkId": "api/agents#request" }, { "kind": "code", "literal": "Agent", "chunkId": "api/agents#request" } ], "sources": [ { "chunkId": "api/agents#request", "url": "/docs/api/agents#request", "anchor": "request" } ], "mode": "source-primary", "terms": [ "request", "body", "just", "query", "optional", "vector", "topk", "data", "config", "there", "overrides", "agent", "configured", "behavior", "examples", "shown", "python", "typescript", "curl", "response", "await", "client", "support", "search", "auth", "errors", "after", "june", "upgrade", "hevlayer", "agentqueryrequest", "const", "post", "layer", "gateway", "agents", "authorization", "bearer", "content", "type" ] }, { "id": "api/agents#response", "kind": "section", "title": "Agentic search", "heading": "Response", "group": "API", "url": "/docs/api/agents#response", "summary": "The response is the federated query shape — rows with $namespace, $rank, and native $score/$dist, plus merge and namespaces blocks — and by default is byte-identical to a federated query, so clients cannot tell a reasoning loop produced it. Enabling output.provenance on the agent surfaces the scores.", "facts": [ { "kind": "code", "literal": "{\n \"rows\": [\n { \"id\": \"T-4821\", \"$namespace\": \"tickets\", \"$rank\": 1, \"$score\": 9.7, \"subject\": \"SSO login fails after upgrade\" }\n ],\n \"merge\": { \"method\": \"weighted-rrf\", \"route\": \"dual-score\" },\n \"namespaces\": [\n { \"namespace\": \"tickets\", \"stable_as_of\": 1747300000123, \"matched\": 20 }\n ]\n}", "chunkId": "api/agents#response" }, { "kind": "code", "literal": "rows", "chunkId": "api/agents#response" }, { "kind": "code", "literal": "$namespace", "chunkId": "api/agents#response" }, { "kind": "code", "literal": "$rank", "chunkId": "api/agents#response" }, { "kind": "code", "literal": "$score", "chunkId": "api/agents#response" }, { "kind": "code", "literal": "$dist", "chunkId": "api/agents#response" }, { "kind": "code", "literal": "merge", "chunkId": "api/agents#response" }, { "kind": "code", "literal": "namespaces", "chunkId": "api/agents#response" }, { "kind": "code", "literal": "output.provenance", "chunkId": "api/agents#response" } ], "sources": [ { "chunkId": "api/agents#response", "url": "/docs/api/agents#response", "anchor": "response" } ], "mode": "source-primary", "terms": [ "response", "federated", "query", "shape", "rows", "namespace", "rank", "native", "score", "dist", "plus", "merge", "namespaces", "blocks", "default", "byte", "identical", "clients", "cannot", "tell", "reasoning", "loop", "produced", "enabling", "output", "provenance", "agent", "surfaces", "scores", "4821", "tickets", "subject", "login", "fails", "after", "upgrade", "method", "weighted", "route", "dual" ] }, { "id": "api/agents#validation", "kind": "section", "title": "Agentic search", "heading": "Validation", "group": "API", "url": "/docs/api/agents#validation", "summary": "Status table for agent queries: unknown/not-Ready agent is 404; missing agent entitlement or out-of-grant index is 403; empty query or mismatched vector dimensionality is 422; a deadline hit returns 200 best-effort or 504 depending on onDeadline; provider unreachable on both primary and fallback is 502.", "facts": [ { "kind": "code", "literal": "{name}", "chunkId": "api/agents#validation" }, { "kind": "code", "literal": "Ready", "chunkId": "api/agents#validation" }, { "kind": "code", "literal": "agent.", "chunkId": "api/agents#validation" }, { "kind": "code", "literal": "query", "chunkId": "api/agents#validation" }, { "kind": "code", "literal": "vector", "chunkId": "api/agents#validation" }, { "kind": "code", "literal": "onDeadline: bestEffort", "chunkId": "api/agents#validation" }, { "kind": "code", "literal": "agent.deadlineHit: true", "chunkId": "api/agents#validation" }, { "kind": "code", "literal": "onDeadline: error", "chunkId": "api/agents#validation" } ], "sources": [ { "chunkId": "api/agents#validation", "url": "/docs/api/agents#validation", "anchor": "validation" } ], "mode": "source-primary", "terms": [ "validation", "status", "table", "agent", "queries", "unknown", "ready", "missing", "entitlement", "grant", "index", "empty", "query", "mismatched", "vector", "dimensionality", "deadline", "returns", "best", "effort", "depending", "ondeadline", "provider", "unreachable", "both", "primary", "fallback", "name", "besteffort", "deadlinehit", "true", "error", "condition", "known", "minted", "lacks", "bound", "outside", "namespace", "present" ] }, { "id": "api/blobs", "kind": "section", "title": "Blobs", "heading": null, "group": "API", "url": "/docs/api/blobs", "summary": "Blobs store opaque bytes durably in Layer's S3 bucket, served through the gateway with Aerospike as a pull-through hot cache; rows store only a blob:// string reference, never the bytes. Use them for media or binary payloads that need a durable home outside the vector engine while riding the gateway read path.", "facts": [ { "kind": "code", "literal": "image_blob: \"blob://products/\"", "chunkId": "api/blobs" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/blobs" } ], "sources": [ { "chunkId": "api/blobs", "url": "/docs/api/blobs", "anchor": null } ], "mode": "source-primary", "terms": [ "blobs", "store", "opaque", "bytes", "durably", "layer", "bucket", "served", "through", "gateway", "aerospike", "pull", "cache", "rows", "only", "blob", "string", "reference", "never", "media", "binary", "payloads", "need", "durable", "home", "outside", "vector", "engine", "while", "riding", "read", "path", "image", "products", "sha256", "codetabs", "astro", "content", "addressed", "serve" ] }, { "id": "api/blobs#fetch", "kind": "section", "title": "Blobs", "heading": "Fetch", "group": "API", "url": "/docs/api/blobs#fetch", "summary": "Fetching a blob by namespace and sha256 returns the bytes with immutable cache headers (Cache-Control and ETag); the gateway sniffs common image types for Content-Type and otherwise returns application/octet-stream.", "facts": [ { "kind": "code", "literal": "image = await client.get_blob(\"products\", stored.sha256)", "chunkId": "api/blobs#fetch" }, { "kind": "code", "literal": "image, err := client.GetBlob(ctx, \"products\", stored.Sha256)", "chunkId": "api/blobs#fetch" }, { "kind": "code", "literal": "const image = await client.getBlob(\"products\", stored.sha256);", "chunkId": "api/blobs#fetch" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v1/namespaces/products/blobs/$SHA256\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -o image.jpg", "chunkId": "api/blobs#fetch" }, { "kind": "code", "literal": "Cache-Control: public, max-age=31536000, immutable\nETag: \"\"", "chunkId": "api/blobs#fetch" }, { "kind": "code", "literal": "jpeg", "chunkId": "api/blobs#fetch" }, { "kind": "code", "literal": "png", "chunkId": "api/blobs#fetch" }, { "kind": "code", "literal": "gif", "chunkId": "api/blobs#fetch" }, { "kind": "code", "literal": "webp", "chunkId": "api/blobs#fetch" }, { "kind": "code", "literal": "Content-Type", "chunkId": "api/blobs#fetch" }, { "kind": "code", "literal": "application/octet-stream", "chunkId": "api/blobs#fetch" } ], "sources": [ { "chunkId": "api/blobs#fetch", "url": "/docs/api/blobs#fetch", "anchor": "fetch" } ], "mode": "source-primary", "terms": [ "fetch", "fetching", "blob", "namespace", "sha256", "returns", "bytes", "immutable", "cache", "headers", "control", "etag", "gateway", "sniffs", "common", "image", "types", "content", "type", "otherwise", "application", "octet", "stream", "await", "client", "products", "stored", "getblob", "const", "curl", "layer", "namespaces", "blobs", "authorization", "bearer", "public", "31536000", "jpeg", "webp", "layergatewayurl" ] }, { "id": "api/blobs#routes", "kind": "section", "title": "Blobs", "heading": "Routes", "group": "API", "url": "/docs/api/blobs#routes", "summary": "Two routes: PUT /v1/namespaces/{ns}/blobs stores raw bytes by sha256 and returns a blob:// reference; GET /v1/namespaces/{ns}/blobs/{sha256} serves bytes from the Aerospike raw cache, falling back to S3 and backfilling.", "facts": [ { "kind": "code", "literal": "PUT /v1/namespaces/{ns}/blobs", "chunkId": "api/blobs#routes" }, { "kind": "code", "literal": "blob://", "chunkId": "api/blobs#routes" }, { "kind": "code", "literal": "GET /v1/namespaces/{ns}/blobs/{sha256}", "chunkId": "api/blobs#routes" } ], "sources": [ { "chunkId": "api/blobs#routes", "url": "/docs/api/blobs#routes", "anchor": "routes" } ], "mode": "source-primary", "terms": [ "routes", "namespaces", "blobs", "stores", "bytes", "sha256", "returns", "blob", "reference", "serves", "aerospike", "cache", "falling", "back", "backfilling", "route", "method", "behavior", "store", "return", "serve" ] }, { "id": "api/blobs#store", "kind": "section", "title": "Blobs", "heading": "Store", "group": "API", "url": "/docs/api/blobs#store", "summary": "Storing bytes is content-addressed: the same bytes always return the same ref, sha256, and size, and the returned ref is written as a normal row attribute. Empty bodies and bodies over the gateway's blob size cap are rejected, and binary bytes never traverse namespace writes — the removed document-blobs payload shape is still rejected.", "facts": [ { "kind": "code", "literal": "with open(\"image.jpg\", \"rb\") as f:\n stored = await client.put_blob(\"products\", f.read())\n\nprint(stored.ref)", "chunkId": "api/blobs#store" }, { "kind": "code", "literal": "body, _ := os.ReadFile(\"image.jpg\")\nstored, err := client.PutBlob(ctx, \"products\", body, nil)", "chunkId": "api/blobs#store" }, { "kind": "code", "literal": "import fs from \"node:fs/promises\";\n\nconst bytes = await fs.readFile(\"image.jpg\");\nconst stored = await client.putBlob(\"products\", bytes);", "chunkId": "api/blobs#store" }, { "kind": "code", "literal": "curl -X PUT \"$LAYER_GATEWAY_URL/v1/namespaces/products/blobs\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/octet-stream\" \\\n --data-binary @image.jpg", "chunkId": "api/blobs#store" }, { "kind": "code", "literal": "{\n \"ref\": \"blob://products/9f86d081884c7d659a2feaa0c55ad015...\",\n \"sha256\": \"9f86d081884c7d659a2feaa0c55ad015...\",\n \"size\": 48213\n}", "chunkId": "api/blobs#store" }, { "kind": "code", "literal": "{\n \"id\": \"B0123\",\n \"vector\": [0.1, 0.2],\n \"image_blob\": \"blob://products/9f86d081884c7d659a2feaa0c55ad015...\"\n}", "chunkId": "api/blobs#store" }, { "kind": "code", "literal": "ref", "chunkId": "api/blobs#store" }, { "kind": "code", "literal": "blobs", "chunkId": "api/blobs#store" }, { "kind": "code", "literal": "/v2/namespaces/{ns}", "chunkId": "api/blobs#store" } ], "sources": [ { "chunkId": "api/blobs#store", "url": "/docs/api/blobs#store", "anchor": "store" } ], "mode": "source-primary", "terms": [ "store", "storing", "bytes", "content", "addressed", "same", "always", "return", "sha256", "size", "returned", "written", "normal", "attribute", "empty", "bodies", "gateway", "blob", "rejected", "binary", "never", "traverse", "namespace", "writes", "removed", "document", "blobs", "payload", "shape", "still", "open", "image", "stored", "await", "client", "products", "read", "print", "body", "readfile" ] }, { "id": "api/blobs#warm-policy", "kind": "section", "title": "Blobs", "heading": "Warm Policy", "group": "API", "url": "/docs/api/blobs#warm-policy", "summary": "Blob reads are pull-through: a cache miss reads S3 and backfills Aerospike best-effort; a per-object write-through is available via a warm query parameter on PUT. Bulk cache warming for blobs is deliberately excluded from the first slice pending a blob-attribute declaration and explicit cache budget.", "facts": [ { "kind": "code", "literal": "PUT ...?warm=true", "chunkId": "api/blobs#warm-policy" }, { "kind": "code", "literal": "hint_cache_warm?blobs=true", "chunkId": "api/blobs#warm-policy" } ], "sources": [ { "chunkId": "api/blobs#warm-policy", "url": "/docs/api/blobs#warm-policy", "anchor": "warm-policy" } ], "mode": "source-primary", "terms": [ "warm", "policy", "blob", "reads", "pull", "through", "cache", "miss", "backfills", "aerospike", "best", "effort", "object", "write", "available", "query", "parameter", "bulk", "warming", "blobs", "deliberately", "excluded", "first", "slice", "pending", "attribute", "declaration", "explicit", "budget", "true", "hint", "today", "hintcachewarm", "intentionally", "part", "needs", "namespace", "attributes", "references", "because" ] }, { "id": "api/checkpoints", "kind": "section", "title": "Checkpoints", "heading": null, "group": "API", "url": "/docs/api/checkpoints", "summary": "Checkpoints label the newest durable snapshot body for a namespace with an immutable name, storing a small label record in S3 without scanning or writing rows. Use them when a downstream app needs to browse, diff, or drop data by a named catalog cut; the response includes the snapshot watermark, content sha, and rows added since the previous checkpoint.", "facts": [ { "kind": "code", "literal": "catalog_run_id", "chunkId": "api/checkpoints" }, { "kind": "code", "literal": "watermark_ms", "chunkId": "api/checkpoints" }, { "kind": "code", "literal": "sha", "chunkId": "api/checkpoints" }, { "kind": "code", "literal": "row_count", "chunkId": "api/checkpoints" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/checkpoints" } ], "sources": [ { "chunkId": "api/checkpoints", "url": "/docs/api/checkpoints", "anchor": null } ], "mode": "source-primary", "terms": [ "checkpoints", "label", "newest", "durable", "snapshot", "body", "namespace", "immutable", "name", "storing", "small", "record", "without", "scanning", "writing", "rows", "downstream", "needs", "browse", "diff", "drop", "data", "named", "catalog", "response", "includes", "watermark", "content", "added", "since", "previous", "checkpoint", "count", "codetabs", "astro", "labels", "watermarks", "give", "application", "stable" ] }, { "id": "api/checkpoints#create", "kind": "section", "title": "Checkpoints", "heading": "Create", "group": "API", "url": "/docs/api/checkpoints#create", "summary": "Creating a checkpoint posts a label and returns the namespace, label, watermark, sha, and row count; re-posting the same label returns the existing checkpoint unchanged. Labels are namespace-local with a restricted character set, and creation returns 412 precondition-failed if no durable snapshot body exists yet.", "facts": [ { "kind": "code", "literal": "checkpoint = await client.create_checkpoint(\"products\", {\n \"label\": \"catalog-2026-06-15\",\n})", "chunkId": "api/checkpoints#create" }, { "kind": "code", "literal": "checkpoint, err := client.CreateCheckpoint(ctx, \"products\",\n &hevlayer.CreateCheckpointRequest{Label: \"catalog-2026-06-15\"})", "chunkId": "api/checkpoints#create" }, { "kind": "code", "literal": "const checkpoint = await client.createCheckpoint(\"products\", {\n label: \"catalog-2026-06-15\",\n});", "chunkId": "api/checkpoints#create" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/checkpoints\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"label\": \"catalog-2026-06-15\"}'", "chunkId": "api/checkpoints#create" }, { "kind": "code", "literal": "{\n \"namespace\": \"products\",\n \"label\": \"catalog-2026-06-15\",\n \"watermark_ms\": 1749513600000,\n \"sha\": \"3f9e8b21...\",\n \"row_count\": 10000\n}", "chunkId": "api/checkpoints#create" }, { "kind": "code", "literal": "label", "chunkId": "api/checkpoints#create" }, { "kind": "code", "literal": "412 precondition_failed", "chunkId": "api/checkpoints#create" } ], "sources": [ { "chunkId": "api/checkpoints#create", "url": "/docs/api/checkpoints#create", "anchor": "create" } ], "mode": "source-primary", "terms": [ "create", "creating", "checkpoint", "posts", "label", "returns", "namespace", "watermark", "count", "posting", "same", "existing", "unchanged", "labels", "local", "restricted", "character", "creation", "precondition", "failed", "durable", "snapshot", "body", "exists", "await", "client", "products", "catalog", "2026", "createcheckpoint", "hevlayer", "createcheckpointrequest", "const", "curl", "post", "layer", "gateway", "namespaces", "checkpoints", "authorization" ] }, { "id": "api/checkpoints#list", "kind": "section", "title": "Checkpoints", "heading": "List", "group": "API", "url": "/docs/api/checkpoints#list", "summary": "Listing checkpoints returns them with a limit query param (default 50, capped at 500) and an opaque before cursor for pagination.", "facts": [ { "kind": "code", "literal": "page = await client.list_checkpoints(\"products\", limit=20)", "chunkId": "api/checkpoints#list" }, { "kind": "code", "literal": "page, err := client.ListCheckpoints(ctx, \"products\",\n &hevlayer.ListCheckpointsParams{Limit: 20})", "chunkId": "api/checkpoints#list" }, { "kind": "code", "literal": "const page = await client.listCheckpoints(\"products\", { limit: 20 });", "chunkId": "api/checkpoints#list" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces/products/checkpoints?limit=20\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/checkpoints#list" }, { "kind": "code", "literal": "{\n \"checkpoints\": [\n {\n \"namespace\": \"products\",\n \"label\": \"catalog-2026-06-15\",\n \"watermark_ms\": 1749513600000,\n \"sha\": \"3f9e8b21...\",\n \"row_count\": 10000\n }\n ],\n \"next_cursor\": null\n}", "chunkId": "api/checkpoints#list" }, { "kind": "code", "literal": "limit", "chunkId": "api/checkpoints#list" }, { "kind": "code", "literal": "before", "chunkId": "api/checkpoints#list" }, { "kind": "code", "literal": "next_cursor", "chunkId": "api/checkpoints#list" } ], "sources": [ { "chunkId": "api/checkpoints#list", "url": "/docs/api/checkpoints#list", "anchor": "list" } ], "mode": "source-primary", "terms": [ "list", "listing", "checkpoints", "returns", "limit", "query", "param", "default", "capped", "opaque", "before", "cursor", "pagination", "page", "await", "client", "products", "listcheckpoints", "hevlayer", "listcheckpointsparams", "const", "curl", "layer", "gateway", "namespaces", "authorization", "bearer", "namespace", "label", "catalog", "2026", "watermark", "1749513600000", "3f9e8b21", "count", "10000", "next", "null", "layergatewayurl", "layergatewayapikey" ] }, { "id": "api/checkpoints#resolve", "kind": "section", "title": "Checkpoints", "heading": "Resolve", "group": "API", "url": "/docs/api/checkpoints#resolve", "summary": "Resolving one checkpoint by label returns its record, or 404 not-found when the label does not exist in that namespace.", "facts": [ { "kind": "code", "literal": "checkpoint = await client.get_checkpoint(\"products\", \"catalog-2026-06-15\")", "chunkId": "api/checkpoints#resolve" }, { "kind": "code", "literal": "checkpoint, err := client.GetCheckpoint(ctx, \"products\", \"catalog-2026-06-15\")", "chunkId": "api/checkpoints#resolve" }, { "kind": "code", "literal": "const checkpoint = await client.getCheckpoint(\"products\", \"catalog-2026-06-15\");", "chunkId": "api/checkpoints#resolve" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces/products/checkpoints/catalog-2026-06-15\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/checkpoints#resolve" }, { "kind": "code", "literal": "404 not_found", "chunkId": "api/checkpoints#resolve" } ], "sources": [ { "chunkId": "api/checkpoints#resolve", "url": "/docs/api/checkpoints#resolve", "anchor": "resolve" } ], "mode": "source-primary", "terms": [ "resolve", "resolving", "checkpoint", "label", "returns", "record", "found", "does", "exist", "namespace", "await", "client", "products", "catalog", "2026", "getcheckpoint", "const", "curl", "layer", "gateway", "namespaces", "checkpoints", "authorization", "bearer", "layergatewayurl", "layergatewayapikey", "notfound" ] }, { "id": "api/checkpoints#routes", "kind": "section", "title": "Checkpoints", "heading": "Routes", "group": "API", "url": "/docs/api/checkpoints#routes", "summary": "Three routes under /v2/namespaces/{ns}/checkpoints: POST creates or returns an immutable label, GET lists newest first, and GET with a label resolves one checkpoint.", "facts": [ { "kind": "code", "literal": "POST /v2/namespaces/{ns}/checkpoints", "chunkId": "api/checkpoints#routes" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/checkpoints", "chunkId": "api/checkpoints#routes" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/checkpoints/{label}", "chunkId": "api/checkpoints#routes" } ], "sources": [ { "chunkId": "api/checkpoints#routes", "url": "/docs/api/checkpoints#routes", "anchor": "routes" } ], "mode": "source-primary", "terms": [ "routes", "three", "under", "namespaces", "checkpoints", "post", "creates", "returns", "immutable", "label", "lists", "newest", "first", "resolves", "checkpoint", "route", "method", "behavior", "create", "return", "list", "resolve" ] }, { "id": "api/data-supply", "kind": "section", "title": "VectorStores And Warehouses", "heading": null, "group": "API", "url": "/docs/api/data-supply", "summary": "Read-only gateway routes expose declared data-supply resources: VectorStore is the serving-side connection, Warehouse the source-side connection used by pipelines. Responses are credential-safe (Secret reference names and keys, never contents), require a read-scoped key, and creation/editing happens through Kubernetes CRDs or the dashboard.", "facts": [ { "kind": "code", "literal": "VectorStore", "chunkId": "api/data-supply" }, { "kind": "code", "literal": "Warehouse", "chunkId": "api/data-supply" }, { "kind": "code", "literal": "read", "chunkId": "api/data-supply" } ], "sources": [ { "chunkId": "api/data-supply", "url": "/docs/api/data-supply", "anchor": null } ], "mode": "source-primary", "terms": [ "read", "only", "gateway", "routes", "expose", "declared", "data", "supply", "resources", "vectorstore", "serving", "side", "connection", "warehouse", "source", "pipelines", "responses", "credential", "safe", "secret", "reference", "names", "keys", "never", "contents", "require", "scoped", "creation", "editing", "happens", "through", "kubernetes", "crds", "dashboard", "vector", "stores", "upstream", "warehouses", "layer", "exposes" ] }, { "id": "api/data-supply#vectorstores", "kind": "section", "title": "VectorStores And Warehouses", "heading": "VectorStores", "group": "API", "url": "/docs/api/data-supply#vectorstores", "summary": "GET /v2/vectorstores lists declared stores with kind, default marker, endpoint, credential Secret reference, inbound auth mode, and reachability status; a per-name GET returns one object, and a turbopuffer dashboard URL appears only when the org id is set on the spec.", "facts": [ { "kind": "code", "literal": "curl -H \"Authorization: Bearer $LAYER_API_KEY\" \\\n \"$LAYER_BASE_URL/v2/vectorstores\"", "chunkId": "api/data-supply#vectorstores" }, { "kind": "code", "literal": "GET /v2/vectorstores/{name}", "chunkId": "api/data-supply#vectorstores" }, { "kind": "code", "literal": "turbopufferUrl", "chunkId": "api/data-supply#vectorstores" }, { "kind": "code", "literal": "spec.turbopuffer.orgId", "chunkId": "api/data-supply#vectorstores" } ], "sources": [ { "chunkId": "api/data-supply#vectorstores", "url": "/docs/api/data-supply#vectorstores", "anchor": "vectorstores" } ], "mode": "source-primary", "terms": [ "vectorstores", "lists", "declared", "stores", "kind", "default", "marker", "endpoint", "credential", "secret", "reference", "inbound", "auth", "mode", "reachability", "status", "name", "returns", "object", "turbopuffer", "dashboard", "appears", "only", "spec", "curl", "authorization", "bearer", "layer", "base", "turbopufferurl", "orgid", "layerapikey", "layerbaseurl", "prod", "true", "https", "east", "region", "org123", "secretref" ] }, { "id": "api/data-supply#warehouses", "kind": "section", "title": "VectorStores And Warehouses", "heading": "Warehouses", "group": "API", "url": "/docs/api/data-supply#warehouses", "summary": "GET /v2/warehouses lists warehouses with kind, source identity (e.g. Snowflake account/user/role), key-pair Secret reference, verification interval, and status; phase is Pending, Verified, or Failed with a failure reason, and consumers counts pipelines and API keys still referencing the warehouse.", "facts": [ { "kind": "code", "literal": "curl -H \"Authorization: Bearer $LAYER_API_KEY\" \\\n \"$LAYER_BASE_URL/v2/warehouses\"", "chunkId": "api/data-supply#warehouses" }, { "kind": "code", "literal": "GET /v2/warehouses/{name}", "chunkId": "api/data-supply#warehouses" }, { "kind": "code", "literal": "phase", "chunkId": "api/data-supply#warehouses" }, { "kind": "code", "literal": "Pending", "chunkId": "api/data-supply#warehouses" }, { "kind": "code", "literal": "Verified", "chunkId": "api/data-supply#warehouses" }, { "kind": "code", "literal": "Failed", "chunkId": "api/data-supply#warehouses" }, { "kind": "code", "literal": "status.failureReason", "chunkId": "api/data-supply#warehouses" }, { "kind": "code", "literal": "status.consumers", "chunkId": "api/data-supply#warehouses" } ], "sources": [ { "chunkId": "api/data-supply#warehouses", "url": "/docs/api/data-supply#warehouses", "anchor": "warehouses" } ], "mode": "source-primary", "terms": [ "warehouses", "lists", "kind", "source", "identity", "snowflake", "account", "user", "role", "pair", "secret", "reference", "verification", "interval", "status", "phase", "pending", "verified", "failed", "failure", "reason", "consumers", "counts", "pipelines", "keys", "still", "referencing", "warehouse", "curl", "authorization", "bearer", "layer", "base", "name", "failurereason", "layerapikey", "layerbaseurl", "prod", "acme", "xy12345" ] }, { "id": "api/embed", "kind": "section", "title": "Embed", "heading": null, "group": "API", "url": "/docs/api/embed", "summary": "Bring-your-own Hugging Face embedding models work over the same Turbopuffer-compatible Embed wire: embed.serving.prefer selects native (upstream computes), autoscaler (configured inference provider), local (in-process Lattice or CLIP on the gateway CPU), or lattice (alias for local Lattice). Modes are explicit — a provider failure returns an error rather than switching modes.", "facts": [ { "kind": "code", "literal": "embed", "chunkId": "api/embed" }, { "kind": "code", "literal": "Embed", "chunkId": "api/embed" }, { "kind": "code", "literal": "embed.serving", "chunkId": "api/embed" }, { "kind": "code", "literal": "prefer: native", "chunkId": "api/embed" }, { "kind": "code", "literal": "prefer: autoscaler", "chunkId": "api/embed" }, { "kind": "code", "literal": "prefer: local", "chunkId": "api/embed" }, { "kind": "code", "literal": "erikkaum/lattice-retrieval", "chunkId": "api/embed" }, { "kind": "code", "literal": "prefer: lattice", "chunkId": "api/embed" }, { "kind": "code", "literal": "embed.serving.prefer", "chunkId": "api/embed" }, { "kind": "code", "literal": "native", "chunkId": "api/embed" }, { "kind": "code", "literal": "autoscaler", "chunkId": "api/embed" }, { "kind": "code", "literal": "local", "chunkId": "api/embed" }, { "kind": "code", "literal": "lattice", "chunkId": "api/embed" }, { "kind": "value", "literal": "StoreSwitch.astro", "chunkId": "api/embed" }, { "kind": "value", "literal": "Upstream.astro", "chunkId": "api/embed" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/embed" }, { "kind": "value", "literal": "turbopuffer.com", "chunkId": "api/embed" } ], "sources": [ { "chunkId": "api/embed", "url": "/docs/api/embed", "anchor": null } ], "mode": "source-primary", "terms": [ "bring", "hugging", "face", "embedding", "models", "work", "same", "turbopuffer", "compatible", "embed", "wire", "serving", "prefer", "selects", "native", "upstream", "computes", "autoscaler", "configured", "inference", "provider", "local", "process", "lattice", "clip", "gateway", "alias", "modes", "explicit", "failure", "returns", "error", "rather", "switching", "erikkaum", "retrieval", "storeswitch", "astro", "codetabs", "stock" ] }, { "id": "api/embed#byo-model-settings", "kind": "section", "title": "Embed", "heading": "BYO model settings", "group": "API", "url": "/docs/api/embed#byo-model-settings", "summary": "BYO settings use a provider-namespaced Hugging Face repo id with no gateway allowlist: revision pins a checkpoint, instructions add document/query prefixes for asymmetric models (both affect the query-cache key), modality image selects CLIP towers, and chunk splits text before write-time embedding. None of these fields is forwarded upstream, and client interoperability is unchanged.", "facts": [ { "kind": "code", "literal": "embed.revision", "chunkId": "api/embed#byo-model-settings" }, { "kind": "code", "literal": "embed.instructions.document", "chunkId": "api/embed#byo-model-settings" }, { "kind": "code", "literal": "embed.instructions.query", "chunkId": "api/embed#byo-model-settings" }, { "kind": "code", "literal": "embed.modality: image", "chunkId": "api/embed#byo-model-settings" }, { "kind": "code", "literal": "prefer: autoscaler", "chunkId": "api/embed#byo-model-settings" }, { "kind": "code", "literal": "prefer: local", "chunkId": "api/embed#byo-model-settings" }, { "kind": "code", "literal": "embed.chunk", "chunkId": "api/embed#byo-model-settings" }, { "kind": "code", "literal": "Embed", "chunkId": "api/embed#byo-model-settings" } ], "sources": [ { "chunkId": "api/embed#byo-model-settings", "url": "/docs/api/embed#byo-model-settings", "anchor": "byo-model-settings" } ], "mode": "source-primary", "terms": [ "model", "settings", "provider", "namespaced", "hugging", "face", "repo", "gateway", "allowlist", "revision", "pins", "checkpoint", "instructions", "document", "query", "prefixes", "asymmetric", "models", "both", "affect", "cache", "modality", "image", "selects", "clip", "towers", "chunk", "splits", "text", "before", "write", "time", "embedding", "none", "these", "fields", "forwarded", "upstream", "client", "interoperability" ] }, { "id": "api/embed#embed-with-auto", "kind": "section", "title": "Embed", "heading": "Embed with Auto", "group": "API", "url": "/docs/api/embed#embed-with-auto", "summary": "An inline Embed inside an Auto rank expression lets routing execute a semantic or fused leg in one request: the first tuple field owns the lexical legs while Embed's field option independently selects the attribute supplying the embedding profile. The route is chosen before Embed resolves, so short hybridtext-routed inputs never call the embedding provider; without a vector or inline Embed the router defers with executed false.", "facts": [ { "kind": "code", "literal": "{\n \"rank_by\": [\"title\", \"Auto\", \"how plants turn sunlight into food\", {\n \"vector\": [\"Embed\", \"how plants turn sunlight into food\", {\n \"field\": \"text\"\n }]\n }],\n \"top_k\": 10\n}", "chunkId": "api/embed#embed-with-auto" }, { "kind": "code", "literal": "Embed", "chunkId": "api/embed#embed-with-auto" }, { "kind": "code", "literal": "Auto", "chunkId": "api/embed#embed-with-auto" }, { "kind": "code", "literal": "title", "chunkId": "api/embed#embed-with-auto" }, { "kind": "code", "literal": "field", "chunkId": "api/embed#embed-with-auto" }, { "kind": "code", "literal": "text", "chunkId": "api/embed#embed-with-auto" }, { "kind": "code", "literal": "hybrid_text", "chunkId": "api/embed#embed-with-auto" }, { "kind": "code", "literal": "performance", "chunkId": "api/embed#embed-with-auto" }, { "kind": "code", "literal": "routing.executed: false", "chunkId": "api/embed#embed-with-auto" }, { "kind": "value", "literal": "routing.policy", "chunkId": "api/embed#embed-with-auto" }, { "kind": "value", "literal": "routing.executed", "chunkId": "api/embed#embed-with-auto" } ], "sources": [ { "chunkId": "api/embed#embed-with-auto", "url": "/docs/api/embed#embed-with-auto", "anchor": "embed-with-auto" } ], "mode": "source-primary", "terms": [ "embed", "auto", "inline", "inside", "rank", "expression", "lets", "routing", "execute", "semantic", "fused", "request", "first", "tuple", "field", "owns", "lexical", "legs", "while", "option", "independently", "selects", "attribute", "supplying", "embedding", "profile", "route", "chosen", "before", "resolves", "short", "hybridtext", "routed", "inputs", "never", "call", "provider", "without", "vector", "router" ] }, { "id": "api/embed#end-to-end-example", "kind": "section", "title": "Embed", "heading": "End-to-end example", "group": "API", "url": "/docs/api/embed#end-to-end-example", "summary": "Worked end-to-end Lattice example: declare the profile on a string attribute's schema, write rows, and query by meaning with Embed — the gateway embeds both sides in-process with no external provider, echoing embedding measurements in performance. The Wikipedia × Lattice demo (github.com/hev/wiki) runs exactly this contract over 1.74M paragraph rows; naming a derived embed field without its model argument returns 422.", "facts": [ { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/articles/query\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"rank_by\": [\"text\", \"ANN\", [\"Embed\", \"largest planet in the solar system\"]],\n \"top_k\": 3,\n \"include_attributes\": [\"title\", \"text\"]\n }'", "chunkId": "api/embed#end-to-end-example" }, { "kind": "code", "literal": "{\n \"rows\": [\n { \"id\": \"planet-1\", \"$dist\": 0.137, \"title\": \"Planet\",\n \"text\": \"Jupiter is the biggest planet in the Solar System.\" }\n ],\n \"performance\": {\n \"embedding_tokens\": 7,\n \"embedding_ms\": 1 // in-process lookup — no network hop to a provider\n }\n}", "chunkId": "api/embed#end-to-end-example" }, { "kind": "code", "literal": "Embed", "chunkId": "api/embed#end-to-end-example" }, { "kind": "code", "literal": "text", "chunkId": "api/embed#end-to-end-example" }, { "kind": "code", "literal": "prefer: lattice", "chunkId": "api/embed#end-to-end-example" }, { "kind": "code", "literal": "performance", "chunkId": "api/embed#end-to-end-example" }, { "kind": "code", "literal": "rank_by", "chunkId": "api/embed#end-to-end-example" }, { "kind": "code", "literal": "embed_", "chunkId": "api/embed#end-to-end-example" }, { "kind": "code", "literal": "{model}", "chunkId": "api/embed#end-to-end-example" }, { "kind": "code", "literal": "422", "chunkId": "api/embed#end-to-end-example" }, { "kind": "code", "literal": "a model name must be provided", "chunkId": "api/embed#end-to-end-example" }, { "kind": "value", "literal": "wiki.hevlayer.com", "chunkId": "api/embed#end-to-end-example" }, { "kind": "value", "literal": "1.74M", "chunkId": "api/embed#end-to-end-example" }, { "kind": "value", "literal": "github.com", "chunkId": "api/embed#end-to-end-example" } ], "sources": [ { "chunkId": "api/embed#end-to-end-example", "url": "/docs/api/embed#end-to-end-example", "anchor": "end-to-end-example" } ], "mode": "source-primary", "terms": [ "example", "worked", "lattice", "declare", "profile", "string", "attribute", "schema", "write", "rows", "query", "meaning", "embed", "gateway", "embeds", "both", "sides", "process", "external", "provider", "echoing", "embedding", "measurements", "performance", "wikipedia", "demo", "github", "wiki", "runs", "exactly", "contract", "paragraph", "naming", "derived", "field", "without", "model", "argument", "returns", "curl" ] }, { "id": "api/embed#image-input", "kind": "section", "title": "Embed", "heading": "Image input", "group": "API", "url": "/docs/api/embed#image-input", "summary": "A local CLIP image profile accepts an HTTP(S) URL or base64 string, fetching at most 20 MiB per URL. Rate-limited hosts map to 429 upstream-error and server errors to 503 service-unavailable (Retry-After preserved); malformed input, non-retryable 4xx, oversize, or undecodable images return 422.", "facts": [ { "kind": "code", "literal": "429", "chunkId": "api/embed#image-input" }, { "kind": "code", "literal": "upstream_error", "chunkId": "api/embed#image-input" }, { "kind": "code", "literal": "503", "chunkId": "api/embed#image-input" }, { "kind": "code", "literal": "service_unavailable", "chunkId": "api/embed#image-input" }, { "kind": "code", "literal": "Retry-After", "chunkId": "api/embed#image-input" }, { "kind": "code", "literal": "422 validation_error", "chunkId": "api/embed#image-input" } ], "sources": [ { "chunkId": "api/embed#image-input", "url": "/docs/api/embed#image-input", "anchor": "image-input" } ], "mode": "source-primary", "terms": [ "image", "input", "local", "clip", "profile", "accepts", "http", "base64", "string", "fetching", "most", "rate", "limited", "hosts", "upstream", "error", "server", "errors", "service", "unavailable", "retry", "after", "preserved", "malformed", "retryable", "oversize", "undecodable", "images", "return", "validation", "fetches", "host", "returns", "upstreamerror", "serviceunavailable", "layer", "preserves", "response", "header", "both" ] }, { "id": "api/embed#lattice", "kind": "section", "title": "Embed", "heading": "Lattice", "group": "API", "url": "/docs/api/embed#lattice", "summary": "Lattice is a compact static CPU retriever for text where throughput and deployment size beat transformer-level quality; it is an explicit serving leg with no fallback. Generate an artifact with the upstream slicer, point the gateway at the model file via an env var, and match the declared dims — the recommended operating point is an int4-per-row 512-dimensional artifact whose vectors Layer writes as f32.", "facts": [ { "kind": "code", "literal": "uv run slicer slice \\\n --dim 512 \\\n --quant int4_row \\\n --output-dir /var/lib/hevlayer/lattice\nexport LAYER_LATTICE_MODEL_PATH=/var/lib/hevlayer/lattice/model.safetensors", "chunkId": "api/embed#lattice" }, { "kind": "code", "literal": "\"text\": {\n \"type\": \"string\",\n \"embed\": {\n \"model\": \"erikkaum/lattice-retrieval\",\n \"dims\": 512,\n \"serving\": { \"prefer\": \"lattice\" }\n }\n}", "chunkId": "api/embed#lattice" }, { "kind": "code", "literal": "model.safetensors", "chunkId": "api/embed#lattice" }, { "kind": "code", "literal": "tokenizer.json", "chunkId": "api/embed#lattice" }, { "kind": "code", "literal": "LAYER_LATTICE_MODEL_PATH", "chunkId": "api/embed#lattice" }, { "kind": "code", "literal": "erikkaum/lattice-retrieval", "chunkId": "api/embed#lattice" }, { "kind": "code", "literal": "embed.dims", "chunkId": "api/embed#lattice" }, { "kind": "code", "literal": "[512]f32", "chunkId": "api/embed#lattice" }, { "kind": "value", "literal": "github.com", "chunkId": "api/embed#lattice" } ], "sources": [ { "chunkId": "api/embed#lattice", "url": "/docs/api/embed#lattice", "anchor": "lattice" } ], "mode": "source-primary", "terms": [ "lattice", "compact", "static", "retriever", "text", "throughput", "deployment", "size", "beat", "transformer", "level", "quality", "explicit", "serving", "fallback", "generate", "artifact", "upstream", "slicer", "point", "gateway", "model", "file", "match", "declared", "dims", "recommended", "operating", "int4", "dimensional", "whose", "vectors", "layer", "writes", "slice", "quant", "output", "hevlayer", "export", "path" ] }, { "id": "api/embed#local-clip", "kind": "section", "title": "Embed", "heading": "Local CLIP", "group": "API", "url": "/docs/api/embed#local-clip", "summary": "prefer local on a CLIP-family model runs both towers in the gateway process on CPU: the image tower embeds attribute values at write time and the text tower resolves query Embed against the same vector column — no GPU, autoscaler pool, or store-native service. It fits query-time text embeds and small-to-medium image corpora; bulk backfills stay on autoscaler, and the legs compose per namespace.", "facts": [ { "kind": "code", "literal": "\"image_url\": {\"type\": \"string\", \"embed\": {\"model\": \"openai/clip-vit-base-patch32\", \"modality\": \"image\",\n \"serving\": {\"prefer\": \"local\"}}}", "chunkId": "api/embed#local-clip" }, { "kind": "code", "literal": "prefer: local", "chunkId": "api/embed#local-clip" }, { "kind": "code", "literal": "Embed", "chunkId": "api/embed#local-clip" }, { "kind": "code", "literal": "prefer: autoscaler", "chunkId": "api/embed#local-clip" } ], "sources": [ { "chunkId": "api/embed#local-clip", "url": "/docs/api/embed#local-clip", "anchor": "local-clip" } ], "mode": "source-primary", "terms": [ "local", "clip", "prefer", "family", "model", "runs", "both", "towers", "gateway", "process", "image", "tower", "embeds", "attribute", "values", "write", "time", "text", "resolves", "query", "embed", "against", "same", "vector", "column", "autoscaler", "pool", "store", "native", "service", "fits", "small", "medium", "corpora", "bulk", "backfills", "stay", "legs", "compose", "namespace" ] }, { "id": "api/embed#model-provisioning", "kind": "section", "title": "Embed", "heading": "Model provisioning", "group": "API", "url": "/docs/api/embed#model-provisioning", "summary": "The local CLIP leg requires an env var pointing at a directory with the checkpoint's four files; unset means the leg is unavailable (validation error, no silent fallback) and a bad directory stops the gateway at startup. On Kubernetes the Helm chart's opt-in localClip values download the model from S3 via init container, verify pinned SHA-256 checksums (mismatch fails the pod), and mount it read-only; Lattice and CLIP artifacts can serve side by side.", "facts": [ { "kind": "code", "literal": "LAYER_LOCAL_CLIP_MODEL_PATH", "chunkId": "api/embed#model-provisioning" }, { "kind": "code", "literal": "model.safetensors", "chunkId": "api/embed#model-provisioning" }, { "kind": "code", "literal": "tokenizer.json", "chunkId": "api/embed#model-provisioning" }, { "kind": "code", "literal": "config.json", "chunkId": "api/embed#model-provisioning" }, { "kind": "code", "literal": "preprocessor_config.json", "chunkId": "api/embed#model-provisioning" }, { "kind": "code", "literal": "gateway.localClip", "chunkId": "api/embed#model-provisioning" }, { "kind": "code", "literal": "gateway.localClip.modelPath", "chunkId": "api/embed#model-provisioning" }, { "kind": "code", "literal": "/var/lib/hevlayer/clip", "chunkId": "api/embed#model-provisioning" }, { "kind": "value", "literal": "SHA-256", "chunkId": "api/embed#model-provisioning" } ], "sources": [ { "chunkId": "api/embed#model-provisioning", "url": "/docs/api/embed#model-provisioning", "anchor": "model-provisioning" } ], "mode": "source-primary", "terms": [ "model", "provisioning", "local", "clip", "requires", "pointing", "directory", "checkpoint", "four", "files", "unset", "means", "unavailable", "validation", "error", "silent", "fallback", "stops", "gateway", "startup", "kubernetes", "helm", "chart", "localclip", "values", "download", "init", "container", "verify", "pinned", "checksums", "mismatch", "fails", "mount", "read", "only", "lattice", "artifacts", "serve", "side" ] }, { "id": "api/embed#performance-accounting", "kind": "section", "title": "Embed", "heading": "Performance accounting", "group": "API", "url": "/docs/api/embed#performance-accounting", "summary": "Write and query responses report embedding token and latency measurements under performance; queries omit the token count on a cache hit. Autoscaler provider measurements merge into the same object, and echoed work is exposed as Prometheus counters labeled by namespace, store kind, model, and serving mode.", "facts": [ { "kind": "code", "literal": "{\n \"rows\": [ /* ... */ ],\n \"performance\": {\n \"embedding_tokens\": 8,\n \"embedding_ms\": 42\n }\n}", "chunkId": "api/embed#performance-accounting" }, { "kind": "code", "literal": "performance", "chunkId": "api/embed#performance-accounting" }, { "kind": "code", "literal": "embedding_tokens", "chunkId": "api/embed#performance-accounting" }, { "kind": "code", "literal": "hevlayer_embed_tokens_total", "chunkId": "api/embed#performance-accounting" }, { "kind": "code", "literal": "hevlayer_embed_compute_seconds_total", "chunkId": "api/embed#performance-accounting" } ], "sources": [ { "chunkId": "api/embed#performance-accounting", "url": "/docs/api/embed#performance-accounting", "anchor": "performance-accounting" } ], "mode": "source-primary", "terms": [ "performance", "accounting", "write", "query", "responses", "report", "embedding", "token", "latency", "measurements", "under", "queries", "omit", "count", "cache", "autoscaler", "provider", "merge", "same", "object", "echoed", "work", "exposed", "prometheus", "counters", "labeled", "namespace", "store", "kind", "model", "serving", "mode", "rows", "tokens", "hevlayer", "embed", "total", "compute", "seconds", "embeddingtokens" ] }, { "id": "api/embed#query-with-embed", "kind": "section", "title": "Embed", "heading": "Query with Embed", "group": "API", "url": "/docs/api/embed#query-with-embed", "summary": "Embed is the query half of schema-attribute embedding: name the source attribute to infer the model from schema, or a derived vector attribute with an explicit model. Native mode forwards Embed upstream, autoscaler resolves through the provider then sends a plain ANN vector, local resolves in-process; query vectors are cached 60 seconds by default and a missing provider returns 503.", "facts": [ { "kind": "code", "literal": "// source attribute: infer the model from its schema\n\"rank_by\": [\"text\", \"ANN\", [\"Embed\", \"chest pain radiating to left arm\"]]\n\n// derived vector attribute: name the model explicitly\n\"rank_by\": [\"embed_text\", \"ANN\", [\"Embed\", \"chest pain radiating to left arm\", {\n \"model\": \"acme/clinical-retrieval-v3\"\n}]]", "chunkId": "api/embed#query-with-embed" }, { "kind": "code", "literal": "response = await client.query_namespace(\"clinical-notes\", {\n \"rank_by\": [\"text\", \"ANN\", [\"Embed\", \"chest pain radiating to left arm\"]],\n \"top_k\": 10,\n})\nprint(response.rows)", "chunkId": "api/embed#query-with-embed" }, { "kind": "code", "literal": "response, err := client.QueryNamespace(ctx, \"clinical-notes\", &hevlayer.QueryRequest{\n RankBy: []any{\"text\", \"ANN\", []any{\"Embed\", \"chest pain radiating to left arm\"}},\n TopK: 10,\n})", "chunkId": "api/embed#query-with-embed" }, { "kind": "code", "literal": "const response = await client.queryNamespace(\"clinical-notes\", {\n rank_by: [\"text\", \"ANN\", [\"Embed\", \"chest pain radiating to left arm\"]],\n top_k: 10,\n});", "chunkId": "api/embed#query-with-embed" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/clinical-notes/query\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"rank_by\": [\"text\", \"ANN\", [\"Embed\", \"chest pain radiating to left arm\"]],\n \"top_k\": 10\n }'", "chunkId": "api/embed#query-with-embed" }, { "kind": "code", "literal": "Embed", "chunkId": "api/embed#query-with-embed" }, { "kind": "code", "literal": "LAYER_EMBED_CACHE_TTL_MS", "chunkId": "api/embed#query-with-embed" }, { "kind": "code", "literal": "503 service_unavailable", "chunkId": "api/embed#query-with-embed" } ], "sources": [ { "chunkId": "api/embed#query-with-embed", "url": "/docs/api/embed#query-with-embed", "anchor": "query-with-embed" } ], "mode": "source-primary", "terms": [ "query", "embed", "half", "schema", "attribute", "embedding", "name", "source", "infer", "model", "derived", "vector", "explicit", "native", "mode", "forwards", "upstream", "autoscaler", "resolves", "through", "provider", "sends", "plain", "local", "process", "vectors", "cached", "seconds", "default", "missing", "returns", "rank", "text", "chest", "pain", "radiating", "left", "explicitly", "acme", "clinical" ] }, { "id": "api/embed#textimage-query", "kind": "section", "title": "Embed", "heading": "Text→image query", "group": "API", "url": "/docs/api/embed#textimage-query", "summary": "Querying an image column with a text Embed runs CLIP's text tower in the gateway process, returning ranked image rows with embedding measurements in performance; write responses report an image count instead of a token count. The lens demo renders this exact serving contract beside every result.", "facts": [ { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/photos/query\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"rank_by\": [\"image_url\", \"ANN\", [\"Embed\", \"sunset over water\"]],\n \"top_k\": 16,\n \"include_attributes\": [\"title\", \"image_url\"]\n }'", "chunkId": "api/embed#textimage-query" }, { "kind": "code", "literal": "{\n \"rows\": [\n { \"id\": \"commons-131\", \"$dist\": 0.412, \"title\": \"Sunset at Porto Covo\",\n \"image_url\": \"https://upload.wikimedia.org/…/640px-Porto_Covo.jpg\" }\n ],\n \"performance\": {\n \"embedding_tokens\": 5,\n \"embedding_ms\": 134 // CLIP text tower on the gateway CPU — no provider hop\n }\n}", "chunkId": "api/embed#textimage-query" }, { "kind": "code", "literal": "Embed", "chunkId": "api/embed#textimage-query" }, { "kind": "code", "literal": "embedding_images", "chunkId": "api/embed#textimage-query" }, { "kind": "code", "literal": "embedding_tokens", "chunkId": "api/embed#textimage-query" }, { "kind": "code", "literal": "prefer: local", "chunkId": "api/embed#textimage-query" }, { "kind": "code", "literal": "compute: gateway-in-process-cpu", "chunkId": "api/embed#textimage-query" }, { "kind": "code", "literal": "performance", "chunkId": "api/embed#textimage-query" }, { "kind": "value", "literal": "lens.hevlayer.com", "chunkId": "api/embed#textimage-query" }, { "kind": "value", "literal": "github.com", "chunkId": "api/embed#textimage-query" } ], "sources": [ { "chunkId": "api/embed#textimage-query", "url": "/docs/api/embed#textimage-query", "anchor": "textimage-query" } ], "mode": "source-primary", "terms": [ "text", "image", "query", "querying", "column", "embed", "runs", "clip", "tower", "gateway", "process", "returning", "ranked", "rows", "embedding", "measurements", "performance", "write", "responses", "report", "count", "instead", "token", "lens", "demo", "renders", "exact", "serving", "contract", "beside", "every", "result", "curl", "post", "layer", "namespaces", "photos", "authorization", "bearer", "content" ] }, { "id": "api/federated-query", "kind": "section", "title": "Federated query", "heading": null, "group": "API", "url": "/docs/api/federated-query", "summary": "A federated query runs one query across a set of namespaces via the namespace-less POST /v2/query and merges results into a single ranked list scoped by the key's entitlements. It has no upstream equivalent — the fan-out and merge are a Layer composition over per-namespace reads; for independent rankings, query namespaces separately.", "facts": [ { "kind": "code", "literal": "POST /v2/query", "chunkId": "api/federated-query" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/federated-query" } ], "sources": [ { "chunkId": "api/federated-query", "url": "/docs/api/federated-query", "anchor": null } ], "mode": "source-primary", "terms": [ "federated", "query", "runs", "across", "namespaces", "namespace", "less", "post", "merges", "results", "single", "ranked", "list", "scoped", "entitlements", "upstream", "equivalent", "merge", "layer", "composition", "reads", "independent", "rankings", "separately", "codetabs", "astro", "federate", "endpoint", "names", "path", "while", "takes", "body", "longer", "parameter", "turbopuffer", "multi", "ranking", "spread", "every" ] }, { "id": "api/federated-query#consistency", "kind": "section", "title": "Federated query", "heading": "Consistency", "group": "API", "url": "/docs/api/federated-query#consistency", "summary": "Each namespace has its own stable-read watermark and there is no single consistent cut across the set: the namespaces block reports per-namespace freshness and the stable-as-of header carries the minimum across reached namespaces. Layer does not manufacture a global watermark.", "facts": [ { "kind": "code", "literal": "namespaces", "chunkId": "api/federated-query#consistency" }, { "kind": "code", "literal": "stable_as_of", "chunkId": "api/federated-query#consistency" }, { "kind": "code", "literal": "x-layer-stable-as-of", "chunkId": "api/federated-query#consistency" } ], "sources": [ { "chunkId": "api/federated-query#consistency", "url": "/docs/api/federated-query#consistency", "anchor": "consistency" } ], "mode": "source-primary", "terms": [ "consistency", "namespace", "stable", "read", "watermark", "there", "single", "consistent", "across", "namespaces", "block", "reports", "freshness", "header", "carries", "minimum", "reached", "layer", "does", "manufacture", "global", "independent", "stableasof", "response", "most", "conservative", "answer" ] }, { "id": "api/federated-query#entitlements", "kind": "section", "title": "Federated query", "heading": "Entitlements", "group": "API", "url": "/docs/api/federated-query#entitlements", "summary": "Federated queries use store-derived auth: the upstream key must read each listed namespace, with read failures reported as partial unless strict. For minted keys, explicitly named namespaces outside the grant are a hard 403, while the wildcard set expands the upstream namespace list filtered through the key's namespace globs so the key itself defines the feed/library.", "facts": [ { "kind": "code", "literal": "namespaces", "chunkId": "api/federated-query#entitlements" }, { "kind": "code", "literal": "strict", "chunkId": "api/federated-query#entitlements" }, { "kind": "code", "literal": "vectorstore.", "chunkId": "api/federated-query#entitlements" }, { "kind": "code", "literal": "403", "chunkId": "api/federated-query#entitlements" }, { "kind": "code", "literal": "namespaces: [\"*\"]", "chunkId": "api/federated-query#entitlements" } ], "sources": [ { "chunkId": "api/federated-query#entitlements", "url": "/docs/api/federated-query#entitlements", "anchor": "entitlements" } ], "mode": "source-primary", "terms": [ "entitlements", "federated", "queries", "store", "derived", "auth", "upstream", "must", "read", "listed", "namespace", "failures", "reported", "partial", "unless", "strict", "minted", "keys", "explicitly", "named", "namespaces", "outside", "grant", "hard", "while", "wildcard", "expands", "list", "filtered", "through", "globs", "itself", "defines", "feed", "library", "vectorstore", "name", "uses", "same", "authentication" ] }, { "id": "api/federated-query#fan-out-and-fuse", "kind": "section", "title": "Federated query", "heading": "Fan-out and fuse", "group": "API", "url": "/docs/api/federated-query#fan-out-and-fuse", "summary": "Supply one ranking and a namespaces set; the gateway runs it against every namespace and returns one merged list with each row tagged by origin. rankby accepts the full single-namespace vocabulary including HybridText and Auto (which routes once for the whole fan-out), filters apply everywhere, and a wildcard or omitted namespaces expands the key's allowlist at request time.", "facts": [ { "kind": "code", "literal": "response = await client.query({\n \"namespaces\": [\"moment-pod-changelog\", \"moment-pod-latent-space\", \"moment-pod-no-priors\"],\n \"rank_by\": [\"text\", \"Auto\", \"evaluating RAG systems\"],\n \"top_k\": 12,\n \"filters\": [\"published_at\", \"Gte\", 1740000000],\n \"include_attributes\": [\"text\", \"show\", \"source_url\", \"start_sec\"],\n})", "chunkId": "api/federated-query#fan-out-and-fuse" }, { "kind": "code", "literal": "const response = await client.query({\n namespaces: [\"moment-pod-changelog\", \"moment-pod-latent-space\", \"moment-pod-no-priors\"],\n rank_by: [\"text\", \"Auto\", \"evaluating RAG systems\"],\n top_k: 12,\n filters: [\"published_at\", \"Gte\", 1740000000],\n include_attributes: [\"text\", \"show\", \"source_url\", \"start_sec\"],\n});", "chunkId": "api/federated-query#fan-out-and-fuse" }, { "kind": "code", "literal": "namespaces", "chunkId": "api/federated-query#fan-out-and-fuse" }, { "kind": "code", "literal": "rows", "chunkId": "api/federated-query#fan-out-and-fuse" }, { "kind": "code", "literal": "rank_by", "chunkId": "api/federated-query#fan-out-and-fuse" }, { "kind": "code", "literal": "HybridText", "chunkId": "api/federated-query#fan-out-and-fuse" }, { "kind": "code", "literal": "Auto", "chunkId": "api/federated-query#fan-out-and-fuse" }, { "kind": "code", "literal": "routing", "chunkId": "api/federated-query#fan-out-and-fuse" }, { "kind": "code", "literal": "filters", "chunkId": "api/federated-query#fan-out-and-fuse" }, { "kind": "code", "literal": "namespaces: [\"*\"]", "chunkId": "api/federated-query#fan-out-and-fuse" }, { "kind": "code", "literal": "[\"*\"]", "chunkId": "api/federated-query#fan-out-and-fuse" } ], "sources": [ { "chunkId": "api/federated-query#fan-out-and-fuse", "url": "/docs/api/federated-query#fan-out-and-fuse", "anchor": "fan-out-and-fuse" } ], "mode": "source-primary", "terms": [ "fuse", "supply", "ranking", "namespaces", "gateway", "runs", "against", "every", "namespace", "returns", "merged", "list", "tagged", "origin", "rankby", "accepts", "full", "single", "vocabulary", "including", "hybridtext", "auto", "routes", "once", "whole", "filters", "apply", "everywhere", "wildcard", "omitted", "expands", "allowlist", "request", "time", "response", "await", "client", "query", "moment", "changelog" ] }, { "id": "api/federated-query#filters", "kind": "section", "title": "Federated query", "heading": "Filters", "group": "API", "url": "/docs/api/federated-query#filters", "summary": "One filter expression is evaluated independently by every namespace, so it only behaves uniformly over a shared filterable schema; a namespace that cannot evaluate it is reported as a filter-schema-mismatch (a stable error code), not zero matches. Best-effort mode omits that namespace and marks the response partial; strict mode fails the request with 422.", "facts": [ { "kind": "code", "literal": "filters", "chunkId": "api/federated-query#filters" }, { "kind": "code", "literal": "filter_schema_mismatch", "chunkId": "api/federated-query#filters" }, { "kind": "code", "literal": "matched: 0", "chunkId": "api/federated-query#filters" }, { "kind": "code", "literal": "rows", "chunkId": "api/federated-query#filters" }, { "kind": "code", "literal": "errors", "chunkId": "api/federated-query#filters" }, { "kind": "code", "literal": "x-layer-partial: true", "chunkId": "api/federated-query#filters" }, { "kind": "code", "literal": "strict: true", "chunkId": "api/federated-query#filters" }, { "kind": "code", "literal": "422", "chunkId": "api/federated-query#filters" } ], "sources": [ { "chunkId": "api/federated-query#filters", "url": "/docs/api/federated-query#filters", "anchor": "filters" } ], "mode": "source-primary", "terms": [ "filters", "filter", "expression", "evaluated", "independently", "every", "namespace", "only", "behaves", "uniformly", "shared", "filterable", "schema", "cannot", "evaluate", "reported", "mismatch", "stable", "error", "code", "zero", "matches", "best", "effort", "mode", "omits", "marks", "response", "partial", "strict", "fails", "request", "matched", "rows", "errors", "layer", "true", "applied", "evaluates", "against" ] }, { "id": "api/federated-query#fusion-options", "kind": "section", "title": "Federated query", "heading": "Fusion options", "group": "API", "url": "/docs/api/federated-query#fusion-options", "summary": "Two fusion options: a per-namespace limit controlling rows each namespace contributes to the merge (default clamps to shallower than single-namespace per-leg limits since most namespaces contribute nothing), and a rank constant accepted for forward compatibility but currently inert.", "facts": [ { "kind": "code", "literal": "fusion.per_namespace_limit", "chunkId": "api/federated-query#fusion-options" }, { "kind": "code", "literal": "clamp(2 × top_k, 10, 100)", "chunkId": "api/federated-query#fusion-options" }, { "kind": "code", "literal": "per_leg_limit", "chunkId": "api/federated-query#fusion-options" }, { "kind": "code", "literal": "top_k", "chunkId": "api/federated-query#fusion-options" }, { "kind": "code", "literal": "fusion.rank_constant", "chunkId": "api/federated-query#fusion-options" }, { "kind": "code", "literal": "60", "chunkId": "api/federated-query#fusion-options" } ], "sources": [ { "chunkId": "api/federated-query#fusion-options", "url": "/docs/api/federated-query#fusion-options", "anchor": "fusion-options" } ], "mode": "source-primary", "terms": [ "fusion", "options", "namespace", "limit", "controlling", "rows", "contributes", "merge", "default", "clamps", "shallower", "single", "limits", "since", "most", "namespaces", "contribute", "nothing", "rank", "constant", "accepted", "forward", "compatibility", "currently", "inert", "clamp", "option", "meaning", "pernamespacelimit", "topk", "many", "returns", "perleglimit", "across", "wide", "final", "deep", "retrieval", "wasted", "integer" ] }, { "id": "api/federated-query#limits", "kind": "section", "title": "Federated query", "heading": "Limits", "group": "API", "url": "/docs/api/federated-query#limits", "summary": "Up to 512 namespaces per request (excess is a 422 naming the overage). Pagination is not supported — cursors are rejected because a fused cursor across independent namespaces cannot form the monotone bands pagination relies on.", "facts": [ { "kind": "code", "literal": "422", "chunkId": "api/federated-query#limits" }, { "kind": "code", "literal": "cursor", "chunkId": "api/federated-query#limits" } ], "sources": [ { "chunkId": "api/federated-query#limits", "url": "/docs/api/federated-query#limits", "anchor": "limits" } ], "mode": "source-primary", "terms": [ "limits", "namespaces", "request", "excess", "naming", "overage", "pagination", "supported", "cursors", "rejected", "because", "fused", "cursor", "across", "independent", "cannot", "form", "monotone", "bands", "relies", "limit", "value", "does" ] }, { "id": "api/federated-query#merge", "kind": "section", "title": "Federated query", "heading": "Merge", "group": "API", "url": "/docs/api/federated-query#merge", "summary": "Merging uses whatever quantity the route makes comparable: vector routes merge by $dist (exact global nearest-neighbor order) when namespaces share an embedding space, while text routes merge by rank-interleave since BM25/hybrid scores aren't comparable across corpora. The fused list orders on $rank, carries native $score only as provenance, and truncates to topk.", "facts": [ { "kind": "code", "literal": "ANN", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "$dist", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "merge.method", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "\"distance\"", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "BM25", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "HybridText", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "fused", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "hybrid_text", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "$score", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "$rank", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "id", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "\"rank-interleave\"", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "top_k", "chunkId": "api/federated-query#merge" }, { "kind": "code", "literal": "fusion.per_namespace_limit", "chunkId": "api/federated-query#merge" } ], "sources": [ { "chunkId": "api/federated-query#merge", "url": "/docs/api/federated-query#merge", "anchor": "merge" } ], "mode": "source-primary", "terms": [ "merge", "merging", "uses", "whatever", "quantity", "route", "makes", "comparable", "vector", "routes", "dist", "exact", "global", "nearest", "neighbor", "order", "namespaces", "share", "embedding", "space", "while", "text", "rank", "interleave", "since", "bm25", "hybrid", "scores", "aren", "across", "corpora", "fused", "list", "orders", "carries", "native", "score", "only", "provenance", "truncates" ] }, { "id": "api/federated-query#partial-failure", "kind": "section", "title": "Federated query", "heading": "Partial failure", "group": "API", "url": "/docs/api/federated-query#partial-failure", "summary": "The fan-out defaults to best-effort: successful namespaces return rows, failures land in an errors block, and the response is marked partial so a feed doesn't blank when one pod is down. Availability errors carry human-readable messages (match on namespace, not string) while filter-schema mismatches carry a stable code; strict mode makes any namespace error fail the whole request.", "facts": [ { "kind": "code", "literal": "{\n \"rows\": [ \"...\" ],\n \"errors\": [\n { \"namespace\": \"moment-pod-no-priors\", \"error\": \"Upstream error: namespace not found\" }\n ]\n}", "chunkId": "api/federated-query#partial-failure" }, { "kind": "code", "literal": "{\n \"rows\": [\n { \"id\": \"ep123#7\", \"$namespace\": \"moment-pod-latent-space\", \"$rank\": 1 }\n ],\n \"errors\": [\n {\n \"namespace\": \"moment-pod-no-priors\",\n \"code\": \"filter_schema_mismatch\",\n \"error\": \"filter schema mismatch\",\n \"detail\": \"filter attribute published_at is absent, not filterable, or has an incompatible type\"\n }\n ]\n}", "chunkId": "api/federated-query#partial-failure" }, { "kind": "code", "literal": "errors", "chunkId": "api/federated-query#partial-failure" }, { "kind": "code", "literal": "x-layer-partial: true", "chunkId": "api/federated-query#partial-failure" }, { "kind": "code", "literal": "error", "chunkId": "api/federated-query#partial-failure" }, { "kind": "code", "literal": "namespace", "chunkId": "api/federated-query#partial-failure" }, { "kind": "code", "literal": "code: \"filter_schema_mismatch\"", "chunkId": "api/federated-query#partial-failure" }, { "kind": "code", "literal": "detail", "chunkId": "api/federated-query#partial-failure" }, { "kind": "code", "literal": "\"strict\": true", "chunkId": "api/federated-query#partial-failure" } ], "sources": [ { "chunkId": "api/federated-query#partial-failure", "url": "/docs/api/federated-query#partial-failure", "anchor": "partial-failure" } ], "mode": "source-primary", "terms": [ "partial", "failure", "defaults", "best", "effort", "successful", "namespaces", "return", "rows", "failures", "land", "errors", "block", "response", "marked", "feed", "doesn", "blank", "down", "availability", "carry", "human", "readable", "messages", "match", "namespace", "string", "while", "filter", "schema", "mismatches", "stable", "code", "strict", "mode", "makes", "error", "fail", "whole", "request" ] }, { "id": "api/federated-query#response", "kind": "section", "title": "Federated query", "heading": "Response", "group": "API", "url": "/docs/api/federated-query#response", "summary": "The response is one fused list where each row carries its origin namespace and its within-namespace rank, plus a merge block naming the strategy and a namespaces block reporting each namespace's stable watermark and rows contributed to the merge. Routing and hybrid blocks echo exactly as in single-namespace queries since the route runs once for the whole fan-out.", "facts": [ { "kind": "code", "literal": "$namespace", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "$rank", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "merge", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "namespaces", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "$score", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "$dist", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "method", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "\"distance\"", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "\"rank-interleave\"", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "route", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "\"fused\"", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "stable_as_of", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "matched", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "per_namespace_limit", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "top_k", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "matched: 0", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "routing", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "hybrid", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "Auto", "chunkId": "api/federated-query#response" }, { "kind": "code", "literal": "HybridText", "chunkId": "api/federated-query#response" } ], "sources": [ { "chunkId": "api/federated-query#response", "url": "/docs/api/federated-query#response", "anchor": "response" } ], "mode": "source-primary", "terms": [ "response", "fused", "list", "carries", "origin", "namespace", "within", "rank", "plus", "merge", "block", "naming", "strategy", "namespaces", "reporting", "stable", "watermark", "rows", "contributed", "routing", "hybrid", "blocks", "echo", "exactly", "single", "queries", "since", "route", "runs", "once", "whole", "score", "dist", "method", "distance", "interleave", "matched", "limit", "auto", "hybridtext" ] }, { "id": "api/federated-query#validation", "kind": "section", "title": "Federated query", "heading": "Validation", "group": "API", "url": "/docs/api/federated-query#validation", "summary": "Validation table: out-of-grant namespaces are 403; upstream read failures are 200-with-errors or 502 under strict; filter schema mismatches are 200-with-errors or 422 under strict; empty sets, wildcard mixed with names, over-cap sets, missing embedding profiles under strict, cursors, and invalid rank expressions are 422.", "facts": [ { "kind": "code", "literal": "errors", "chunkId": "api/federated-query#validation" }, { "kind": "code", "literal": "strict: true", "chunkId": "api/federated-query#validation" }, { "kind": "code", "literal": "filters", "chunkId": "api/federated-query#validation" }, { "kind": "code", "literal": "errors[].code: \"filter_schema_mismatch\"", "chunkId": "api/federated-query#validation" }, { "kind": "code", "literal": "x-layer-partial: true", "chunkId": "api/federated-query#validation" }, { "kind": "code", "literal": "namespaces", "chunkId": "api/federated-query#validation" }, { "kind": "code", "literal": "namespaces: [\"*\"]", "chunkId": "api/federated-query#validation" }, { "kind": "code", "literal": "cursor", "chunkId": "api/federated-query#validation" }, { "kind": "code", "literal": "rank_by", "chunkId": "api/federated-query#validation" } ], "sources": [ { "chunkId": "api/federated-query#validation", "url": "/docs/api/federated-query#validation", "anchor": "validation" } ], "mode": "source-primary", "terms": [ "validation", "table", "grant", "namespaces", "upstream", "read", "failures", "errors", "under", "strict", "filter", "schema", "mismatches", "empty", "sets", "wildcard", "mixed", "names", "missing", "embedding", "profiles", "cursors", "invalid", "rank", "expressions", "true", "filters", "code", "mismatch", "layer", "partial", "cursor", "condition", "status", "named", "namespace", "outside", "minted", "fails", "cannot" ] }, { "id": "api/federated-query#vector-merge-requires-a-matching-embedding-space", "kind": "section", "title": "Federated query", "heading": "Vector merge requires a matching embedding space", "group": "API", "url": "/docs/api/federated-query#vector-merge-requires-a-matching-embedding-space", "summary": "Distance merge is only meaningful when every namespace embeds into the same geometry, declared via the embedding identity (model, dims, normalization) on each Index. Matching profiles yield distance merge; a missing or mismatched profile downgrades best-effort responses to rank-interleave with a downgrade reason, or fails 422 under strict. Text fan-outs are rank-interleave regardless.", "facts": [ { "kind": "code", "literal": "spec.embedding", "chunkId": "api/federated-query#vector-merge-requires-a-matching-embedding-space" }, { "kind": "code", "literal": "Index", "chunkId": "api/federated-query#vector-merge-requires-a-matching-embedding-space" }, { "kind": "code", "literal": "spec.backend.distanceMetric", "chunkId": "api/federated-query#vector-merge-requires-a-matching-embedding-space" }, { "kind": "code", "literal": "merge.method: \"distance\"", "chunkId": "api/federated-query#vector-merge-requires-a-matching-embedding-space" }, { "kind": "code", "literal": "merge.method: \"rank-interleave\"", "chunkId": "api/federated-query#vector-merge-requires-a-matching-embedding-space" }, { "kind": "code", "literal": "merge.downgraded_reason", "chunkId": "api/federated-query#vector-merge-requires-a-matching-embedding-space" }, { "kind": "code", "literal": "\"missing_embedding_profile\"", "chunkId": "api/federated-query#vector-merge-requires-a-matching-embedding-space" }, { "kind": "code", "literal": "\"embedding_profile_mismatch\"", "chunkId": "api/federated-query#vector-merge-requires-a-matching-embedding-space" }, { "kind": "code", "literal": "strict: true", "chunkId": "api/federated-query#vector-merge-requires-a-matching-embedding-space" }, { "kind": "code", "literal": "422", "chunkId": "api/federated-query#vector-merge-requires-a-matching-embedding-space" } ], "sources": [ { "chunkId": "api/federated-query#vector-merge-requires-a-matching-embedding-space", "url": "/docs/api/federated-query#vector-merge-requires-a-matching-embedding-space", "anchor": "vector-merge-requires-a-matching-embedding-space" } ], "mode": "source-primary", "terms": [ "vector", "merge", "requires", "matching", "embedding", "space", "distance", "only", "meaningful", "every", "namespace", "embeds", "same", "geometry", "declared", "identity", "model", "dims", "normalization", "index", "profiles", "yield", "missing", "mismatched", "profile", "downgrades", "best", "effort", "responses", "rank", "interleave", "downgrade", "reason", "fails", "under", "strict", "text", "outs", "regardless", "spec" ] }, { "id": "api/introduction", "kind": "section", "title": "Introduction", "heading": null, "group": "API", "url": "/docs/api/introduction", "summary": "Layer speaks the upstream store's native wire protocol, adding what it needs through x-layer- headers, so pointing an existing client at the gateway just works and gains stable reads, the document cache, and enhanced search without changing requests.", "facts": [ { "kind": "code", "literal": "x-layer-*", "chunkId": "api/introduction" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/introduction" }, { "kind": "value", "literal": "StoreSwitch.astro", "chunkId": "api/introduction" }, { "kind": "value", "literal": "StoreNote.astro", "chunkId": "api/introduction" }, { "kind": "value", "literal": "Upstream.astro", "chunkId": "api/introduction" }, { "kind": "value", "literal": "FeatureGate.astro", "chunkId": "api/introduction" } ], "sources": [ { "chunkId": "api/introduction", "url": "/docs/api/introduction", "anchor": null } ], "mode": "source-primary", "terms": [ "layer", "speaks", "upstream", "store", "native", "wire", "protocol", "adding", "needs", "through", "headers", "pointing", "existing", "client", "gateway", "just", "works", "gains", "stable", "reads", "document", "cache", "enhanced", "search", "without", "changing", "requests", "codetabs", "astro", "storeswitch", "storenote", "featuregate", "adds", "turbopuffer", "compatible", "point", "fields", "features", "already", "send" ] }, { "id": "api/introduction#authentication", "kind": "section", "title": "Introduction", "heading": "Authentication", "group": "API", "url": "/docs/api/introduction#authentication", "summary": "Every request carries a bearer token of two kinds: the store key (the upstream turbopuffer credential, accepted as admin — the drop-in default) or a minted key scoped to namespaces crossed with read/write. Routes are classified read, write, or admin; out-of-scope requests answer 403 with the reason, and connection env vars plus an optional direct-to-turbopuffer fallback pair are documented.", "facts": [ { "kind": "code", "literal": "Authorization: Bearer ", "chunkId": "api/introduction#authentication" }, { "kind": "code", "literal": "VectorStore", "chunkId": "api/introduction#authentication" }, { "kind": "code", "literal": "read", "chunkId": "api/introduction#authentication" }, { "kind": "code", "literal": "write", "chunkId": "api/introduction#authentication" }, { "kind": "code", "literal": "admin", "chunkId": "api/introduction#authentication" }, { "kind": "code", "literal": "LAYER_GATEWAY_URL", "chunkId": "api/introduction#authentication" }, { "kind": "code", "literal": "LAYER_GATEWAY_API_KEY", "chunkId": "api/introduction#authentication" }, { "kind": "code", "literal": "deriveFromStore", "chunkId": "api/introduction#authentication" }, { "kind": "code", "literal": "keys", "chunkId": "api/introduction#authentication" }, { "kind": "code", "literal": "TURBOPUFFER_API_KEY", "chunkId": "api/introduction#authentication" }, { "kind": "code", "literal": "TURBOPUFFER_API_URL", "chunkId": "api/introduction#authentication" }, { "kind": "code", "literal": "https://aws-us-east-1.turbopuffer.com", "chunkId": "api/introduction#authentication" } ], "sources": [ { "chunkId": "api/introduction#authentication", "url": "/docs/api/introduction#authentication", "anchor": "authentication" } ], "mode": "source-primary", "terms": [ "authentication", "every", "request", "carries", "bearer", "token", "kinds", "store", "upstream", "turbopuffer", "credential", "accepted", "admin", "drop", "default", "minted", "scoped", "namespaces", "crossed", "read", "write", "routes", "classified", "scope", "requests", "answer", "reason", "connection", "vars", "plus", "optional", "direct", "fallback", "pair", "documented", "authorization", "vectorstore", "layer", "gateway", "derivefromstore" ] }, { "id": "api/introduction#cache-warm-hint--get-v1namespacesnshint_cache_warm", "kind": "section", "title": "Introduction", "heading": "Cache warm hint — GET /v1/namespaces/{ns}/hint_cache_warm", "group": "API", "url": "/docs/api/introduction#cache-warm-hint--get-v1namespacesnshint_cache_warm", "summary": "The cache-warm hint is a raw upstream passthrough with no query parameters; supplying any warm option forwards the hint upstream and additionally runs Layer-side warm steps (document-cache backfill job and snapshot mirror), each independently toggleable.", "facts": [ { "kind": "code", "literal": "GET /v1/namespaces/{ns}/hint_cache_warm", "chunkId": "api/introduction#cache-warm-hint--get-v1namespacesnshint_cache_warm" }, { "kind": "value", "literal": "turbopuffer.com", "chunkId": "api/introduction#cache-warm-hint--get-v1namespacesnshint_cache_warm" } ], "sources": [ { "chunkId": "api/introduction#cache-warm-hint--get-v1namespacesnshint_cache_warm", "url": "/docs/api/introduction#cache-warm-hint--get-v1namespacesnshint_cache_warm", "anchor": "cache-warm-hint--get-v1namespacesnshint_cache_warm" } ], "mode": "source-primary", "terms": [ "cache", "warm", "hint", "namespaces", "upstream", "passthrough", "query", "parameters", "supplying", "option", "forwards", "additionally", "runs", "layer", "side", "steps", "document", "backfill", "snapshot", "mirror", "independently", "toggleable", "turbopuffer", "hintcachewarm", "contract", "response", "returned", "verbatim", "supplied", "aerospike", "origin", "plus", "latest", "body", "step", "request", "page" ] }, { "id": "api/introduction#compatibility-posture", "kind": "section", "title": "Introduction", "heading": "Compatibility posture", "group": "API", "url": "/docs/api/introduction#compatibility-posture", "summary": "Layer is a drop-in for existing turbopuffer clients: routes the upstream doesn't implement live under /v2/ without shadowing upstream behavior, and unproxied routes return 404 rather than being silently re-routed upstream.", "facts": [ { "kind": "code", "literal": "/v2/", "chunkId": "api/introduction#compatibility-posture" }, { "kind": "value", "literal": "turbopuffer.com", "chunkId": "api/introduction#compatibility-posture" } ], "sources": [ { "chunkId": "api/introduction#compatibility-posture", "url": "/docs/api/introduction#compatibility-posture", "anchor": "compatibility-posture" } ], "mode": "source-primary", "terms": [ "compatibility", "posture", "layer", "drop", "existing", "turbopuffer", "clients", "routes", "upstream", "doesn", "implement", "live", "under", "without", "shadowing", "behavior", "unproxied", "return", "rather", "being", "silently", "routed", "does", "namespaced", "shadow", "request", "route", "proxy", "returns", "might", "handle", "differently" ] }, { "id": "api/introduction#cross-cutting-conventions", "kind": "section", "title": "Introduction", "heading": "Cross-cutting conventions", "group": "API", "url": "/docs/api/introduction#cross-cutting-conventions", "summary": "Conventions applying to every proxied endpoint: hevlayer-prefixed attributes are reserved (writes rejected; the upserted-at watermark is server-stamped), backing-engine failures are hard 5xx while Aerospike cache failures are soft, and the x-layer-cache header distinguishes hit, miss, and miss-on-error.", "facts": [ { "kind": "code", "literal": "_hevlayer_*", "chunkId": "api/introduction#cross-cutting-conventions" }, { "kind": "code", "literal": "_hevlayer_", "chunkId": "api/introduction#cross-cutting-conventions" }, { "kind": "code", "literal": "_hevlayer_upserted_at", "chunkId": "api/introduction#cross-cutting-conventions" }, { "kind": "code", "literal": "x-layer-cache", "chunkId": "api/introduction#cross-cutting-conventions" }, { "kind": "code", "literal": "hit", "chunkId": "api/introduction#cross-cutting-conventions" }, { "kind": "code", "literal": "miss", "chunkId": "api/introduction#cross-cutting-conventions" }, { "kind": "code", "literal": "miss-on-error", "chunkId": "api/introduction#cross-cutting-conventions" }, { "kind": "code", "literal": "x-layer-next-cursor", "chunkId": "api/introduction#cross-cutting-conventions" } ], "sources": [ { "chunkId": "api/introduction#cross-cutting-conventions", "url": "/docs/api/introduction#cross-cutting-conventions", "anchor": "cross-cutting-conventions" } ], "mode": "source-primary", "terms": [ "cross", "cutting", "conventions", "applying", "every", "proxied", "endpoint", "hevlayer", "prefixed", "attributes", "reserved", "writes", "rejected", "upserted", "watermark", "server", "stamped", "backing", "engine", "failures", "hard", "while", "aerospike", "cache", "soft", "layer", "header", "distinguishes", "miss", "error", "next", "cursor", "these", "apply", "proxies", "whether", "route", "upstream", "compatible", "only" ] }, { "id": "api/introduction#enhancements-to-upstream-routes", "kind": "section", "title": "Introduction", "heading": "Enhancements to upstream routes", "group": "API", "url": "/docs/api/introduction#enhancements-to-upstream-routes", "summary": "Index of the wire-compatible upstream routes; each linked section describes only what Layer overlays on top of the upstream contract.", "facts": [], "sources": [ { "chunkId": "api/introduction#enhancements-to-upstream-routes", "url": "/docs/api/introduction#enhancements-to-upstream-routes", "anchor": "enhancements-to-upstream-routes" } ], "mode": "source-primary", "terms": [ "enhancements", "upstream", "routes", "index", "wire", "compatible", "linked", "section", "describes", "only", "layer", "overlays", "contract", "below", "turbopuffer", "body" ] }, { "id": "api/introduction#gateway-failures", "kind": "section", "title": "Introduction", "heading": "Gateway failures", "group": "API", "url": "/docs/api/introduction#gateway-failures", "summary": "If the gateway is unreachable, the Python, Go, and TypeScript SDKs return the original connection error; they do not retry directly against a backing store because the server chooses the store per namespace and the client surface stays backend-neutral.", "facts": [ { "kind": "code", "literal": "VectorStore", "chunkId": "api/introduction#gateway-failures" } ], "sources": [ { "chunkId": "api/introduction#gateway-failures", "url": "/docs/api/introduction#gateway-failures", "anchor": "gateway-failures" } ], "mode": "source-primary", "terms": [ "gateway", "failures", "unreachable", "python", "typescript", "sdks", "return", "original", "connection", "error", "retry", "directly", "against", "backing", "store", "because", "server", "chooses", "namespace", "client", "surface", "stays", "backend", "neutral", "vectorstore", "talk", "layer", "returned" ] }, { "id": "api/introduction#install", "kind": "section", "title": "Introduction", "heading": "Install", "group": "API", "url": "/docs/api/introduction#install", "summary": "Four ways to call Layer — Python, Go, and TypeScript clients (all generated from the gateway's OpenAPI spec) plus plain REST — with install commands and client construction pointing at the gateway URL and API key. Every endpoint page shows the four surfaces side by side.", "facts": [ { "kind": "code", "literal": "pip install hevlayer # Python 3.11+\ngo get github.com/hev/layer-go # Go 1.22+\nnpm install hevlayer # Node 18+", "chunkId": "api/introduction#install" }, { "kind": "code", "literal": "import os\n\nfrom hevlayer import AsyncHevlayer\n\nclient = AsyncHevlayer(\n base_url=os.environ[\"LAYER_GATEWAY_URL\"],\n api_key=os.environ[\"LAYER_GATEWAY_API_KEY\"],\n)", "chunkId": "api/introduction#install" }, { "kind": "code", "literal": "import (\n \"os\"\n\n hevlayer \"github.com/hev/layer-go\"\n)\n\nclient := hevlayer.NewClient(\n hevlayer.WithBaseURL(os.Getenv(\"LAYER_GATEWAY_URL\")),\n hevlayer.WithAPIKey(os.Getenv(\"LAYER_GATEWAY_API_KEY\")),\n)", "chunkId": "api/introduction#install" }, { "kind": "code", "literal": "import { Hevlayer } from \"hevlayer\";\n\nconst client = new Hevlayer({\n baseUrl: process.env.LAYER_GATEWAY_URL,\n apiKey: process.env.LAYER_GATEWAY_API_KEY,\n});", "chunkId": "api/introduction#install" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/introduction#install" }, { "kind": "code", "literal": "apps/layer-gateway/openapi.yaml", "chunkId": "api/introduction#install" }, { "kind": "code", "literal": "client", "chunkId": "api/introduction#install" }, { "kind": "code", "literal": "ctx context.Context", "chunkId": "api/introduction#install" } ], "sources": [ { "chunkId": "api/introduction#install", "url": "/docs/api/introduction#install", "anchor": "install" } ], "mode": "source-primary", "terms": [ "install", "four", "ways", "call", "layer", "python", "typescript", "clients", "generated", "gateway", "openapi", "spec", "plus", "plain", "rest", "commands", "client", "construction", "pointing", "every", "endpoint", "page", "shows", "surfaces", "side", "hevlayer", "github", "node", "import", "asynchevlayer", "base", "environ", "newclient", "withbaseurl", "getenv", "withapikey", "const", "baseurl", "process", "apikey" ] }, { "id": "api/introduction#metadata--get-v2namespacesnsmetadata", "kind": "section", "title": "Introduction", "heading": "Metadata — GET /v2/namespaces/{ns}/metadata", "group": "API", "url": "/docs/api/introduction#metadata--get-v2namespacesnsmetadata", "summary": "Namespace metadata is proxied upstream verbatim (schema, row count, index status, timestamps) then enriched with a layer block carrying the stable watermark and stability flag.", "facts": [ { "kind": "code", "literal": "GET /v2/namespaces/{ns}/metadata", "chunkId": "api/introduction#metadata--get-v2namespacesnsmetadata" }, { "kind": "code", "literal": "layer", "chunkId": "api/introduction#metadata--get-v2namespacesnsmetadata" }, { "kind": "code", "literal": "stable_as_of", "chunkId": "api/introduction#metadata--get-v2namespacesnsmetadata" }, { "kind": "code", "literal": "is_stable", "chunkId": "api/introduction#metadata--get-v2namespacesnsmetadata" }, { "kind": "value", "literal": "turbopuffer.com", "chunkId": "api/introduction#metadata--get-v2namespacesnsmetadata" } ], "sources": [ { "chunkId": "api/introduction#metadata--get-v2namespacesnsmetadata", "url": "/docs/api/introduction#metadata--get-v2namespacesnsmetadata", "anchor": "metadata--get-v2namespacesnsmetadata" } ], "mode": "source-primary", "terms": [ "metadata", "namespaces", "namespace", "proxied", "upstream", "verbatim", "schema", "count", "index", "status", "timestamps", "enriched", "layer", "block", "carrying", "stable", "watermark", "stability", "flag", "turbopuffer", "contract", "containing", "stableasof", "isstable", "page" ] }, { "id": "api/introduction#query--post-v2namespacesnsquery", "kind": "section", "title": "Introduction", "heading": "Query — POST /v2/namespaces/{ns}/query", "group": "API", "url": "/docs/api/introduction#query--post-v2namespacesnsquery", "summary": "The query route follows the upstream contract and adds stable reads via an injected watermark predicate while the index is updating, a one-shot 429 retry with the filter forced on, and the stable-as-of response header for correlating freshness.", "facts": [ { "kind": "code", "literal": "POST /v2/namespaces/{ns}/query", "chunkId": "api/introduction#query--post-v2namespacesnsquery" }, { "kind": "code", "literal": "_hevlayer_upserted_at <= watermark", "chunkId": "api/introduction#query--post-v2namespacesnsquery" }, { "kind": "code", "literal": "updating", "chunkId": "api/introduction#query--post-v2namespacesnsquery" }, { "kind": "code", "literal": "x-layer-stable-as-of", "chunkId": "api/introduction#query--post-v2namespacesnsquery" }, { "kind": "value", "literal": "turbopuffer.com", "chunkId": "api/introduction#query--post-v2namespacesnsquery" } ], "sources": [ { "chunkId": "api/introduction#query--post-v2namespacesnsquery", "url": "/docs/api/introduction#query--post-v2namespacesnsquery", "anchor": "query--post-v2namespacesnsquery" } ], "mode": "source-primary", "terms": [ "query", "post", "namespaces", "route", "follows", "upstream", "contract", "adds", "stable", "reads", "injected", "watermark", "predicate", "while", "index", "updating", "shot", "retry", "filter", "forced", "response", "header", "correlating", "freshness", "hevlayer", "upserted", "layer", "turbopuffer", "vector", "queries", "request", "shape", "ranking", "filters", "attribute", "selection", "hevlayerupsertedat", "race", "write", "storm" ] }, { "id": "api/introduction#write--post-v2namespacesns", "kind": "section", "title": "Introduction", "heading": "Write — POST /v2/namespaces/{ns}", "group": "API", "url": "/docs/api/introduction#write--post-v2namespacesns", "summary": "The write route follows the upstream contract for upserts, deletes, and patches, adding a best-effort document-cache mirror before explicit-id writes and a server-stamped upserted-at watermark powering the query path's consistency; reserved hevlayer attributes are rejected.", "facts": [ { "kind": "code", "literal": "POST /v2/namespaces/{ns}", "chunkId": "api/introduction#write--post-v2namespacesns" }, { "kind": "code", "literal": "patch_rows", "chunkId": "api/introduction#write--post-v2namespacesns" }, { "kind": "code", "literal": "_hevlayer_upserted_at", "chunkId": "api/introduction#write--post-v2namespacesns" }, { "kind": "code", "literal": "_hevlayer_*", "chunkId": "api/introduction#write--post-v2namespacesns" }, { "kind": "value", "literal": "turbopuffer.com", "chunkId": "api/introduction#write--post-v2namespacesns" } ], "sources": [ { "chunkId": "api/introduction#write--post-v2namespacesns", "url": "/docs/api/introduction#write--post-v2namespacesns", "anchor": "write--post-v2namespacesns" } ], "mode": "source-primary", "terms": [ "write", "post", "namespaces", "route", "follows", "upstream", "contract", "upserts", "deletes", "patches", "adding", "best", "effort", "document", "cache", "mirror", "before", "explicit", "writes", "server", "stamped", "upserted", "watermark", "powering", "query", "path", "consistency", "reserved", "hevlayer", "attributes", "rejected", "patch", "rows", "turbopuffer", "upsert", "delete", "patchrows", "aerospike", "hevlayerupsertedat", "every" ] }, { "id": "api/keys", "kind": "section", "title": "API keys", "heading": null, "group": "API", "url": "/docs/api/keys", "summary": "Layer mints its own API keys whose entitlements each name a VectorStore, Warehouse, or Layer itself, carrying scopes and claims per target. Minting is optional — the default store credential is accepted as an admin bearer, and admin scope is what calls the key routes.", "facts": [ { "kind": "code", "literal": "VectorStore", "chunkId": "api/keys" }, { "kind": "code", "literal": "Warehouse", "chunkId": "api/keys" }, { "kind": "code", "literal": "ApiKey", "chunkId": "api/keys" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/keys" } ], "sources": [ { "chunkId": "api/keys", "url": "/docs/api/keys", "anchor": null } ], "mode": "source-primary", "terms": [ "layer", "mints", "keys", "whose", "entitlements", "name", "vectorstore", "warehouse", "itself", "carrying", "scopes", "claims", "target", "minting", "optional", "default", "store", "credential", "accepted", "admin", "bearer", "scope", "calls", "routes", "apikey", "codetabs", "astro", "mint", "verify", "revoke", "rest", "open", "stores", "warehouses", "opens", "declared", "resource", "entitlement", "names", "carries" ] }, { "id": "api/keys#authenticate", "kind": "section", "title": "API keys", "heading": "Authenticate", "group": "API", "url": "/docs/api/keys#authenticate", "summary": "The authenticate verb exchanges a raw token for a stable keyId plus the full entitlements map so external systems make their own authorization decisions — Layer never interprets claim strings. Invalid, revoked, and expired tokens all answer 401 indistinguishably in constant time, and the route is rate-limited and unauthenticated.", "facts": [ { "kind": "code", "literal": "identity = await client.authenticate_key({\"token\": presented})\nclaims = identity.entitlements[\"warehouse.prod-snowflake\"].claims", "chunkId": "api/keys#authenticate" }, { "kind": "code", "literal": "identity, err := client.AuthenticateKey(ctx, &hevlayer.AuthenticateKeyRequest{\n\tToken: presented,\n})\nclaims := identity.Entitlements[\"warehouse.prod-snowflake\"].Claims", "chunkId": "api/keys#authenticate" }, { "kind": "code", "literal": "const identity = await client.authenticateKey({ token: presented });\nconst claims = identity.entitlements[\"warehouse.prod-snowflake\"].claims;", "chunkId": "api/keys#authenticate" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/keys/authenticate\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"token\": \"hvl_iqGFsDD2PNkyhCqr59jjvKuKL47vqXMz\"}'", "chunkId": "api/keys#authenticate" }, { "kind": "code", "literal": "keyId", "chunkId": "api/keys#authenticate" }, { "kind": "code", "literal": "200", "chunkId": "api/keys#authenticate" }, { "kind": "code", "literal": "{keyId, name, owner, entitlements, expiresAt}", "chunkId": "api/keys#authenticate" }, { "kind": "code", "literal": "401", "chunkId": "api/keys#authenticate" } ], "sources": [ { "chunkId": "api/keys#authenticate", "url": "/docs/api/keys#authenticate", "anchor": "authenticate" } ], "mode": "source-primary", "terms": [ "authenticate", "verb", "exchanges", "token", "stable", "keyid", "plus", "full", "entitlements", "external", "systems", "make", "their", "authorization", "decisions", "layer", "never", "interprets", "claim", "strings", "invalid", "revoked", "expired", "tokens", "answer", "indistinguishably", "constant", "time", "route", "rate", "limited", "unauthenticated", "identity", "await", "client", "presented", "claims", "warehouse", "prod", "snowflake" ] }, { "id": "api/keys#cli", "kind": "section", "title": "API keys", "heading": "CLI", "group": "API", "url": "/docs/api/keys#cli", "summary": "The same key operations from the layer CLI: mint with entitlement, namespace, and claim flags, list, revoke, and (rarely, after retention) remove. Minting prints the token once alone on stdout for piping; metadata goes to stderr.", "facts": [ { "kind": "code", "literal": "layer keys mint cohort-reader --owner acme \\\n --entitle vectorstore.prod-turbopuffer=read \\\n --namespaces \"cohort-*\" \\\n --claim warehouse.prod-snowflake=\"notes:cohort:*:read\"\nlayer keys ls\nlayer keys revoke cohort-reader\n# Rare cleanup after the retention period:\nlayer keys rm cohort-reader", "chunkId": "api/keys#cli" }, { "kind": "code", "literal": "layer keys mint", "chunkId": "api/keys#cli" } ], "sources": [ { "chunkId": "api/keys#cli", "url": "/docs/api/keys#cli", "anchor": "cli" } ], "mode": "source-primary", "terms": [ "same", "operations", "layer", "mint", "entitlement", "namespace", "claim", "flags", "list", "revoke", "rarely", "after", "retention", "remove", "minting", "prints", "token", "once", "alone", "stdout", "piping", "metadata", "goes", "stderr", "keys", "cohort", "reader", "owner", "acme", "entitle", "vectorstore", "prod", "turbopuffer", "read", "namespaces", "warehouse", "snowflake", "notes", "rare", "cleanup" ] }, { "id": "api/keys#key-model", "kind": "section", "title": "API keys", "heading": "Key model", "group": "API", "url": "/docs/api/keys#key-model", "summary": "Raw tokens are returned once at mint; Layer stores only one-way hashes, so a lost token is revoked and re-minted. Every key is an ApiKey cluster resource (kubectl is an equal authoring surface and audit trail); revoke is the default end-of-life keeping the audit record, and hard delete is a rare cleanup allowed only after revocation.", "facts": [ { "kind": "code", "literal": "hvl_iqGFsDD2PNkyhCqr59jjvKuKL47vqXMz", "chunkId": "api/keys#key-model" }, { "kind": "code", "literal": "ApiKey", "chunkId": "api/keys#key-model" }, { "kind": "code", "literal": "is the audit trail, and", "chunkId": "api/keys#key-model" }, { "kind": "code", "literal": "Revoked", "chunkId": "api/keys#key-model" } ], "sources": [ { "chunkId": "api/keys#key-model", "url": "/docs/api/keys#key-model", "anchor": "key-model" } ], "mode": "source-primary", "terms": [ "model", "tokens", "returned", "once", "mint", "layer", "stores", "only", "hashes", "lost", "token", "revoked", "minted", "every", "apikey", "cluster", "resource", "kubectl", "equal", "authoring", "surface", "audit", "trail", "revoke", "default", "life", "keeping", "record", "hard", "delete", "rare", "cleanup", "allowed", "after", "revocation", "iqgfsdd2pnkyhcqr59jjvkukl47vqxmz", "look", "like", "hvliqgfsdd2pnkyhcqr59jjvkukl47vqxmz", "response" ] }, { "id": "api/keys#kubectl", "kind": "section", "title": "API keys", "heading": "kubectl", "group": "API", "url": "/docs/api/keys#kubectl", "summary": "Applying an ApiKey CRD with no credential has the operator mint the token into a Secret referenced by the resource status. Both surfaces round-trip one schema — the CRD YAML and the REST key object are two spellings of the same thing.", "facts": [ { "kind": "code", "literal": "kubectl apply -f key.yaml\nkubectl get apikeys -n layer\nkubectl get secret apikey-cohort-reader -n layer -o jsonpath='{.data.token}' | base64 -d", "chunkId": "api/keys#kubectl" }, { "kind": "code", "literal": "ApiKey", "chunkId": "api/keys#kubectl" }, { "kind": "code", "literal": "status.secretRef", "chunkId": "api/keys#kubectl" }, { "kind": "code", "literal": "and", "chunkId": "api/keys#kubectl" }, { "kind": "flag", "literal": "-o", "chunkId": "api/keys#kubectl" } ], "sources": [ { "chunkId": "api/keys#kubectl", "url": "/docs/api/keys#kubectl", "anchor": "kubectl" } ], "mode": "source-primary", "terms": [ "kubectl", "applying", "apikey", "credential", "operator", "mint", "token", "secret", "referenced", "resource", "status", "both", "surfaces", "round", "trip", "schema", "yaml", "rest", "object", "spellings", "same", "thing", "apply", "apikeys", "layer", "cohort", "reader", "jsonpath", "data", "base64", "secretref", "other", "authoring", "surface", "mints", "named", "through", "keys", "keyid", "page" ] }, { "id": "api/keys#list-and-get", "kind": "section", "title": "API keys", "heading": "List and get", "group": "API", "url": "/docs/api/keys#list-and-get", "summary": "List and get return key metadata only — keyId, name, owner, entitlements, phase, timestamps — never tokens or recoverable hashes. The last-seen timestamp advances at most once per five minutes per key.", "facts": [ { "kind": "code", "literal": "keys = await client.list_keys()\nkey = await client.get_key(\"0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9\")", "chunkId": "api/keys#list-and-get" }, { "kind": "code", "literal": "keys, err := client.ListKeys(ctx, nil)\nkey, err := client.GetKey(ctx, \"0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9\")", "chunkId": "api/keys#list-and-get" }, { "kind": "code", "literal": "const keys = await client.listKeys();\nconst key = await client.getKey(\"0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9\");", "chunkId": "api/keys#list-and-get" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/keys\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/keys#list-and-get" }, { "kind": "code", "literal": "lastSeenAt", "chunkId": "api/keys#list-and-get" } ], "sources": [ { "chunkId": "api/keys#list-and-get", "url": "/docs/api/keys#list-and-get", "anchor": "list-and-get" } ], "mode": "source-primary", "terms": [ "list", "return", "metadata", "only", "keyid", "name", "owner", "entitlements", "phase", "timestamps", "never", "tokens", "recoverable", "hashes", "last", "seen", "timestamp", "advances", "most", "once", "five", "minutes", "keys", "await", "client", "0a1b2c3d", "4e5f", "6071", "8293", "a4b5c6d7e8f9", "listkeys", "getkey", "const", "curl", "layer", "gateway", "authorization", "bearer", "lastseenat", "layergatewayurl" ] }, { "id": "api/keys#mint", "kind": "section", "title": "API keys", "heading": "Mint", "group": "API", "url": "/docs/api/keys#mint", "summary": "Minting takes a unique name, optional owner/description, an entitlements map keyed by target prefix (vectorstore, warehouse, or layer), and an expiry defaulting to 365 days; the 201 response is the only place the raw token appears. Entitlements whose target doesn't exist yet are accepted and surface as a missing-target condition, and an admin key is minted with the layer target at admin scope.", "facts": [ { "kind": "code", "literal": "const key = await client.mintKey({\n name: \"cohort-reader\",\n owner: \"acme\",\n entitlements: {\n \"vectorstore.prod-turbopuffer\": {\n scopes: [\"read\"],\n namespaces: [\"cohort-*\"],\n },\n \"warehouse.prod-snowflake\": {\n claims: [\"notes:cohort:*:read\"],\n },\n },\n expiresAfter: \"365d\",\n});\nconsole.log(key.token); // shown once, never again", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "201 Created", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "name", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "ApiKey", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "owner", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "description", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "entitlements", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "vectorstore.", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "warehouse.", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "layer", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "expiresAfter", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "never", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "365d", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "expiresAt", "chunkId": "api/keys#mint" }, { "kind": "code", "literal": "EntitlementTargetMissing", "chunkId": "api/keys#mint" } ], "sources": [ { "chunkId": "api/keys#mint", "url": "/docs/api/keys#mint", "anchor": "mint" } ], "mode": "source-primary", "terms": [ "mint", "minting", "takes", "unique", "name", "optional", "owner", "description", "entitlements", "keyed", "target", "prefix", "vectorstore", "warehouse", "layer", "expiry", "defaulting", "days", "response", "only", "place", "token", "appears", "whose", "doesn", "exist", "accepted", "surface", "missing", "condition", "admin", "minted", "scope", "const", "await", "client", "mintkey", "cohort", "reader", "acme" ] }, { "id": "api/keys#revoke-by-default", "kind": "section", "title": "API keys", "heading": "Revoke by default", "group": "API", "url": "/docs/api/keys#revoke-by-default", "summary": "Revocation propagates within seconds and rotation is mint-new, deploy, revoke-old — no in-place rotation. Revoked keys stay listable indefinitely (Layer never garbage-collects them; set external retention), and hard delete is accepted only on a Revoked key, permanently removing the resource and audit history.", "facts": [ { "kind": "code", "literal": "await client.revoke_key(\"0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9\")", "chunkId": "api/keys#revoke-by-default" }, { "kind": "code", "literal": "_, err := client.RevokeKey(ctx, \"0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9\")", "chunkId": "api/keys#revoke-by-default" }, { "kind": "code", "literal": "await client.revokeKey(\"0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9\");", "chunkId": "api/keys#revoke-by-default" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/keys/$KEY_ID/revoke\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/keys#revoke-by-default" }, { "kind": "code", "literal": "curl -X DELETE \"$LAYER_GATEWAY_URL/v2/keys/$KEY_ID\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/keys#revoke-by-default" }, { "kind": "code", "literal": "?includeRevoked", "chunkId": "api/keys#revoke-by-default" }, { "kind": "code", "literal": "Revoked", "chunkId": "api/keys#revoke-by-default" }, { "kind": "code", "literal": "Active", "chunkId": "api/keys#revoke-by-default" }, { "kind": "code", "literal": "Pending", "chunkId": "api/keys#revoke-by-default" }, { "kind": "code", "literal": "Expired", "chunkId": "api/keys#revoke-by-default" }, { "kind": "code", "literal": "409 Conflict", "chunkId": "api/keys#revoke-by-default" }, { "kind": "code", "literal": "ApiKey", "chunkId": "api/keys#revoke-by-default" } ], "sources": [ { "chunkId": "api/keys#revoke-by-default", "url": "/docs/api/keys#revoke-by-default", "anchor": "revoke-by-default" } ], "mode": "source-primary", "terms": [ "revoke", "default", "revocation", "propagates", "within", "seconds", "rotation", "mint", "deploy", "place", "revoked", "keys", "stay", "listable", "indefinitely", "layer", "never", "garbage", "collects", "external", "retention", "hard", "delete", "accepted", "only", "permanently", "removing", "resource", "audit", "history", "await", "client", "0a1b2c3d", "4e5f", "6071", "8293", "a4b5c6d7e8f9", "revokekey", "curl", "post" ] }, { "id": "api/keys#routes", "kind": "section", "title": "API keys", "heading": "Routes", "group": "API", "url": "/docs/api/keys#routes", "summary": "Route table for /v2/keys: mint (POST, admin, only response with the raw token), list and per-key get (admin, metadata only), revoke (admin, idempotent), hard delete (admin, revoked keys only), and authenticate (unauthenticated by construction — the token is the credential).", "facts": [ { "kind": "code", "literal": "/v2/keys", "chunkId": "api/keys#routes" }, { "kind": "code", "literal": "?includeRevoked", "chunkId": "api/keys#routes" }, { "kind": "code", "literal": "/v2/keys/{keyId}", "chunkId": "api/keys#routes" }, { "kind": "code", "literal": "/v2/keys/{keyId}/revoke", "chunkId": "api/keys#routes" }, { "kind": "code", "literal": "Revoked", "chunkId": "api/keys#routes" }, { "kind": "code", "literal": "/v2/keys/authenticate", "chunkId": "api/keys#routes" }, { "kind": "code", "literal": "layer", "chunkId": "api/keys#routes" }, { "kind": "code", "literal": "admin", "chunkId": "api/keys#routes" }, { "kind": "code", "literal": "authenticate", "chunkId": "api/keys#routes" } ], "sources": [ { "chunkId": "api/keys#routes", "url": "/docs/api/keys#routes", "anchor": "routes" } ], "mode": "source-primary", "terms": [ "routes", "route", "table", "keys", "mint", "post", "admin", "only", "response", "token", "list", "metadata", "revoke", "idempotent", "hard", "delete", "revoked", "authenticate", "unauthenticated", "construction", "credential", "includerevoked", "keyid", "layer", "method", "auth", "behavior", "contains", "never", "material", "adds", "expired", "record", "stays", "permanently", "remove", "audit", "none", "exchange", "identity" ] }, { "id": "api/keys#using-a-minted-key", "kind": "section", "title": "API keys", "heading": "Using a minted key", "group": "API", "url": "/docs/api/keys#using-a-minted-key", "summary": "A minted key works wherever its entitlements reach: vectorstore entitlements open data-plane routes inside namespace globs, out-of-grant and insufficient-scope requests answer 403 with the reason named, and claims-only keys authenticate but open no Layer route. Minted keys are gateway-only — the SDK direct-to-upstream fall-through fails fast for them.", "facts": [ { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces/cohort-7/query\" \\\n -X POST \\\n -H \"Authorization: Bearer hvl_iqGFsDD2PNkyhCqr59jjvKuKL47vqXMz\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"rank_by\": [\"text\", \"BM25\", \"acme\"], \"top_k\": 10}'", "chunkId": "api/keys#using-a-minted-key" }, { "kind": "code", "literal": "{\"error\": \"namespace not in key grant\", \"namespace\": \"orders\"}", "chunkId": "api/keys#using-a-minted-key" }, { "kind": "code", "literal": "{\"error\": \"insufficient API key scope\", \"required_scope\": \"admin\"}", "chunkId": "api/keys#using-a-minted-key" }, { "kind": "code", "literal": "vectorstore.", "chunkId": "api/keys#using-a-minted-key" } ], "sources": [ { "chunkId": "api/keys#using-a-minted-key", "url": "/docs/api/keys#using-a-minted-key", "anchor": "using-a-minted-key" } ], "mode": "source-primary", "terms": [ "minted", "works", "wherever", "entitlements", "reach", "vectorstore", "open", "data", "plane", "routes", "inside", "namespace", "globs", "grant", "insufficient", "scope", "requests", "answer", "reason", "named", "claims", "only", "keys", "authenticate", "layer", "route", "gateway", "direct", "upstream", "fall", "through", "fails", "fast", "curl", "namespaces", "cohort", "query", "post", "authorization", "bearer" ] }, { "id": "api/license", "kind": "section", "title": "License", "heading": null, "group": "API", "url": "/docs/api/license", "summary": "GET /v2/license returns the gateway's local license projection for operators and dashboards: it never phones home, deriving state from the configured license key plus a codified grace cushion.", "facts": [ { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/license\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/license" }, { "kind": "code", "literal": "GET /v2/license", "chunkId": "api/license" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/license" } ], "sources": [ { "chunkId": "api/license", "url": "/docs/api/license", "anchor": null } ], "mode": "source-primary", "terms": [ "license", "returns", "gateway", "local", "projection", "operators", "dashboards", "never", "phones", "home", "deriving", "state", "configured", "plus", "codified", "grace", "cushion", "curl", "layer", "authorization", "bearer", "codetabs", "astro", "read", "operator", "dashboard", "oracle", "derives", "layergatewayurl", "layergatewayapikey" ] }, { "id": "api/license#client-call", "kind": "section", "title": "License", "heading": "Client Call", "group": "API", "url": "/docs/api/license#client-call", "summary": "Reading license state from the Python, Go, and TypeScript clients or cURL returns a state object exposing the gateway's licensed state.", "facts": [ { "kind": "code", "literal": "state = await client.get_license()\nprint(state.gateway.state)", "chunkId": "api/license#client-call" }, { "kind": "code", "literal": "state, err := client.GetLicense(ctx)\nif err != nil {\n return err\n}\nfmt.Println(state.Gateway.State)", "chunkId": "api/license#client-call" }, { "kind": "code", "literal": "const state = await client.getLicense();\nconsole.log(state.gateway.state);", "chunkId": "api/license#client-call" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/license\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/license#client-call" } ], "sources": [ { "chunkId": "api/license#client-call", "url": "/docs/api/license#client-call", "anchor": "client-call" } ], "mode": "source-primary", "terms": [ "client", "call", "reading", "license", "state", "python", "typescript", "clients", "curl", "returns", "object", "exposing", "gateway", "licensed", "await", "print", "getlicense", "return", "println", "const", "console", "layer", "authorization", "bearer", "layergatewayurl", "layergatewayapikey" ] }, { "id": "api/license#fields", "kind": "section", "title": "License", "heading": "Fields", "group": "API", "url": "/docs/api/license#fields", "summary": "Field reference: valid, a floor-state marker and verifier reason for missing/invalid keys, and for valid keys the subject, tier, enabled feature strings (absence denies the feature), numeric limits (missing means unlimited), and expiry; the gateway block always reports licensed/grace/floor with seconds-to-deadline and grace-remaining counters. Phase 1 covers the gateway surface only.", "facts": [ { "kind": "code", "literal": "valid", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "state", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "reason", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "missing", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "sub", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "tier", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "trial", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "design-partner", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "features", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "limits", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "exp", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "gateway.state", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "licensed", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "grace", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "floor", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "gateway.seconds_to_deadline", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "exp + grace", "chunkId": "api/license#fields" }, { "kind": "code", "literal": "gateway.grace_seconds_remaining", "chunkId": "api/license#fields" } ], "sources": [ { "chunkId": "api/license#fields", "url": "/docs/api/license#fields", "anchor": "fields" } ], "mode": "source-primary", "terms": [ "fields", "field", "reference", "valid", "floor", "state", "marker", "verifier", "reason", "missing", "invalid", "keys", "subject", "tier", "enabled", "feature", "strings", "absence", "denies", "numeric", "limits", "means", "unlimited", "expiry", "gateway", "block", "always", "reports", "licensed", "grace", "seconds", "deadline", "remaining", "counters", "phase", "covers", "surface", "only", "trial", "design" ] }, { "id": "api/license#response", "kind": "section", "title": "License", "heading": "Response", "group": "API", "url": "/docs/api/license#response", "summary": "A valid key returns its claims plus the gateway state; an expired key inside the grace window stays valid with a grace countdown; missing, invalid, or fully degraded licenses return invalid with a floor state and a reason distinguishing verifier failure from a missing key.", "facts": [ { "kind": "code", "literal": "{\n \"valid\": true,\n \"sub\": \"acme-corp\",\n \"tier\": \"design-partner\",\n \"features\": [\"transform-runtime\", \"agents\", \"rbac\", \"warehouses\", \"doc-cache\", \"history\", \"cost\"],\n \"limits\": {\"namespaces\": 50, \"udf_workers\": 20},\n \"exp\": \"2026-12-14T00:00:00Z\",\n \"gateway\": {\n \"state\": \"licensed\",\n \"seconds_to_deadline\": 1209600,\n \"grace_seconds_remaining\": 0\n }\n}", "chunkId": "api/license#response" }, { "kind": "code", "literal": "{\n \"valid\": false,\n \"state\": \"floor\",\n \"reason\": \"missing\",\n \"gateway\": {\n \"state\": \"floor\",\n \"seconds_to_deadline\": 0,\n \"grace_seconds_remaining\": 0\n }\n}", "chunkId": "api/license#response" }, { "kind": "code", "literal": "valid: true", "chunkId": "api/license#response" }, { "kind": "code", "literal": "gateway.state", "chunkId": "api/license#response" }, { "kind": "code", "literal": "grace", "chunkId": "api/license#response" }, { "kind": "code", "literal": "valid", "chunkId": "api/license#response" }, { "kind": "code", "literal": "true", "chunkId": "api/license#response" }, { "kind": "code", "literal": "grace_seconds_remaining", "chunkId": "api/license#response" }, { "kind": "code", "literal": "valid: false", "chunkId": "api/license#response" }, { "kind": "code", "literal": "reason", "chunkId": "api/license#response" }, { "kind": "code", "literal": "reason: \"missing\"", "chunkId": "api/license#response" } ], "sources": [ { "chunkId": "api/license#response", "url": "/docs/api/license#response", "anchor": "response" } ], "mode": "source-primary", "terms": [ "response", "valid", "returns", "claims", "plus", "gateway", "state", "expired", "inside", "grace", "window", "stays", "countdown", "missing", "invalid", "fully", "degraded", "licenses", "return", "floor", "reason", "distinguishing", "verifier", "failure", "true", "acme", "corp", "tier", "design", "partner", "features", "transform", "runtime", "agents", "rbac", "warehouses", "cache", "history", "cost", "limits" ] }, { "id": "api/license#state-effects", "kind": "section", "title": "License", "heading": "State Effects", "group": "API", "url": "/docs/api/license#state-effects", "summary": "Effects by state: licensed gates routes on the features list; grace keeps gated routes working while tagging responses with a grace header; floor makes gated routes return 402 license-required. CE routes (query, point read, write, scan, metadata, snapshot, backend routing) always work regardless of state.", "facts": [ { "kind": "code", "literal": "licensed", "chunkId": "api/license#state-effects" }, { "kind": "code", "literal": "features", "chunkId": "api/license#state-effects" }, { "kind": "code", "literal": "grace", "chunkId": "api/license#state-effects" }, { "kind": "code", "literal": "x-hevlayer-license-grace: true", "chunkId": "api/license#state-effects" }, { "kind": "code", "literal": "floor", "chunkId": "api/license#state-effects" }, { "kind": "code", "literal": "402", "chunkId": "api/license#state-effects" }, { "kind": "code", "literal": "error: \"license_required\"", "chunkId": "api/license#state-effects" } ], "sources": [ { "chunkId": "api/license#state-effects", "url": "/docs/api/license#state-effects", "anchor": "state-effects" } ], "mode": "source-primary", "terms": [ "state", "effects", "licensed", "gates", "routes", "features", "list", "grace", "keeps", "gated", "working", "while", "tagging", "responses", "header", "floor", "makes", "return", "license", "required", "query", "point", "read", "write", "scan", "metadata", "snapshot", "backend", "routing", "always", "work", "regardless", "hevlayer", "true", "error", "gateway", "route", "behavior", "feature", "includes" ] }, { "id": "api/namespace-metadata", "kind": "section", "title": "Namespace metadata", "heading": null, "group": "API", "url": "/docs/api/namespace-metadata", "summary": "Namespace metadata is proxied verbatim from upstream — schema, row counts, index status, timestamps — with Layer adding a single enrichment sub-object on top.", "facts": [ { "kind": "code", "literal": "/v2/namespaces/{ns}/metadata", "chunkId": "api/namespace-metadata" }, { "kind": "value", "literal": "Upstream.astro", "chunkId": "api/namespace-metadata" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/namespace-metadata" }, { "kind": "value", "literal": "turbopuffer.com", "chunkId": "api/namespace-metadata" } ], "sources": [ { "chunkId": "api/namespace-metadata", "url": "/docs/api/namespace-metadata", "anchor": null } ], "mode": "source-primary", "terms": [ "namespace", "metadata", "proxied", "verbatim", "upstream", "schema", "counts", "index", "status", "timestamps", "layer", "adding", "single", "enrichment", "object", "namespaces", "astro", "codetabs", "turbopuffer", "read", "enriched", "freshness", "signals", "payload", "endpoint", "follow", "contract", "adds" ] }, { "id": "api/namespace-metadata#list-namespaces", "kind": "section", "title": "Namespace metadata", "heading": "List namespaces", "group": "API", "url": "/docs/api/namespace-metadata#list-namespaces", "summary": "GET /v2/namespaces is a Layer-only augmented listing that pages the upstream namespace list and enriches each row with stability, cache state, and last-write signals — the endpoint the dashboard inventory reads. Per-row metadata failures degrade to an error field rather than dropping the row, responses come from a short-TTL cache to survive dashboard polling, and listing does not register namespaces with the consistency watcher.", "facts": [ { "kind": "code", "literal": "namespaces = await client.list_namespaces(prefix=\"prod\", page_size=100)", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "namespaces, err := client.ListNamespaces(ctx, &hevlayer.ListNamespacesParams{\n Prefix: \"prod\",\n PageSize: 100,\n})", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "const namespaces = await client.listNamespaces({\n prefix: \"prod\",\n pageSize: 100,\n});", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces?prefix=prod&page_size=100\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "{\n \"namespaces\": [\n {\n \"name\": \"products\",\n \"row_count\": 12500,\n \"size_bytes\": 48800000,\n \"stable_as_of_ms\": 1715600400000,\n \"is_stable\": true,\n \"index\": { \"status\": \"up-to-date\" },\n \"cache_state\": {\"state\": \"warm\", \"warm_inflight\": false},\n \"last_write_ms\": 1715600399000,\n \"shadow\": false,\n \"labels\": {}\n }\n ],\n \"next_cursor\": \"...\"\n}", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "GET /v2/namespaces", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "is_stable", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "index.status", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "\"up-to-date\"", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "\"updating\"", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "stable_as_of_ms", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "indexed", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "index_lag_rows", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "GET /v2/namespaces/{namespace}/metadata", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "index", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "index.unindexed_bytes", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "updating", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "prefix", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "cursor", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "next_cursor", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "page_size", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "metadata_error", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "NAMESPACE_LIST_CACHE_TTL_MS", "chunkId": "api/namespace-metadata#list-namespaces" }, { "kind": "code", "literal": "10000", "chunkId": "api/namespace-metadata#list-namespaces" } ], "sources": [ { "chunkId": "api/namespace-metadata#list-namespaces", "url": "/docs/api/namespace-metadata#list-namespaces", "anchor": "list-namespaces" } ], "mode": "source-primary", "terms": [ "list", "namespaces", "layer", "only", "augmented", "listing", "pages", "upstream", "namespace", "enriches", "stability", "cache", "state", "last", "write", "signals", "endpoint", "dashboard", "inventory", "reads", "metadata", "failures", "degrade", "error", "field", "rather", "dropping", "responses", "come", "short", "survive", "polling", "does", "register", "consistency", "watcher", "await", "client", "prefix", "prod" ] }, { "id": "api/namespace-metadata#request", "kind": "section", "title": "Namespace metadata", "heading": "Request", "group": "API", "url": "/docs/api/namespace-metadata#request", "summary": "A single-namespace metadata read returns the upstream payload verbatim plus a layer block with the stable watermark, stability flag, indexed flag, and index-lag row count.", "facts": [ { "kind": "code", "literal": "metadata = await client.get_namespace_metadata(\"products\")", "chunkId": "api/namespace-metadata#request" }, { "kind": "code", "literal": "metadata, err := client.GetNamespaceMetadata(ctx, \"products\")", "chunkId": "api/namespace-metadata#request" }, { "kind": "code", "literal": "const metadata = await client.getNamespaceMetadata(\"products\");", "chunkId": "api/namespace-metadata#request" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces/products/metadata\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/namespace-metadata#request" } ], "sources": [ { "chunkId": "api/namespace-metadata#request", "url": "/docs/api/namespace-metadata#request", "anchor": "request" } ], "mode": "source-primary", "terms": [ "request", "single", "namespace", "metadata", "read", "returns", "upstream", "payload", "verbatim", "plus", "layer", "block", "stable", "watermark", "stability", "flag", "indexed", "index", "count", "await", "client", "products", "getnamespacemetadata", "const", "curl", "gateway", "namespaces", "authorization", "bearer", "layergatewayurl", "layergatewayapikey", "proxied", "turbopuffer", "schema", "approxrowcount", "12500", "approxlogicalbytes", "48800000", "createdat", "2026" ] }, { "id": "api/namespace-metadata#the-layer-block", "kind": "section", "title": "Namespace metadata", "heading": "The layer block", "group": "API", "url": "/docs/api/namespace-metadata#the-layer-block", "summary": "The layer block's fields answer two different questions: isstable/stableasof report whether the upstream index has caught up on rows it has seen (driving read-after-write and the per-query filter-skip decision), while indexed/indexlagrows report whether every row's vector is present and queryable (what a bulk load or pipeline needs to know it finished), reconciled from the latest snapshot so it advances on snapshot cadence. A namespace can be stable yet not fully indexed; missing namespaces return upstream's 404.", "facts": [ { "kind": "code", "literal": "layer", "chunkId": "api/namespace-metadata#the-layer-block" }, { "kind": "code", "literal": "stable_as_of", "chunkId": "api/namespace-metadata#the-layer-block" }, { "kind": "code", "literal": "is_stable", "chunkId": "api/namespace-metadata#the-layer-block" }, { "kind": "code", "literal": "index.status == \"up-to-date\"", "chunkId": "api/namespace-metadata#the-layer-block" }, { "kind": "code", "literal": "indexed", "chunkId": "api/namespace-metadata#the-layer-block" }, { "kind": "code", "literal": "index_lag_rows", "chunkId": "api/namespace-metadata#the-layer-block" }, { "kind": "code", "literal": "CONSISTENCY_STABLE_POLL_INTERVAL_MS", "chunkId": "api/namespace-metadata#the-layer-block" }, { "kind": "code", "literal": "CONSISTENCY_POLL_INTERVAL_MS", "chunkId": "api/namespace-metadata#the-layer-block" }, { "kind": "code", "literal": "is_stable: true", "chunkId": "api/namespace-metadata#the-layer-block" }, { "kind": "code", "literal": "indexed: false", "chunkId": "api/namespace-metadata#the-layer-block" } ], "sources": [ { "chunkId": "api/namespace-metadata#the-layer-block", "url": "/docs/api/namespace-metadata#the-layer-block", "anchor": "the-layer-block" } ], "mode": "source-primary", "terms": [ "layer", "block", "fields", "answer", "different", "questions", "isstable", "stableasof", "report", "whether", "upstream", "index", "caught", "rows", "seen", "driving", "read", "after", "write", "query", "filter", "skip", "decision", "while", "indexed", "indexlagrows", "every", "vector", "present", "queryable", "bulk", "load", "pipeline", "needs", "know", "finished", "reconciled", "latest", "snapshot", "advances" ] }, { "id": "api/pipelines", "kind": "section", "title": "Pipelines", "heading": null, "group": "API", "url": "/docs/api/pipelines", "summary": "The pipeline API organizes indexing code into a typical two-stage layout — extraction and chunking on CPU, embedding on GPU — with a best-practice walkthrough that generalizes to N stages.", "facts": [ { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/pipelines" } ], "sources": [ { "chunkId": "api/pipelines", "url": "/docs/api/pipelines", "anchor": null } ], "mode": "source-primary", "terms": [ "pipeline", "organizes", "indexing", "code", "typical", "stage", "layout", "extraction", "chunking", "embedding", "best", "practice", "walkthrough", "generalizes", "stages", "codetabs", "astro", "organize", "extract", "chunk", "embed", "trigger", "runs", "wait", "completion", "keeps", "need", "index", "data", "simple", "organized", "followed", "guide", "walks", "through", "concepts", "expand" ] }, { "id": "api/pipelines#deploy", "kind": "section", "title": "Pipelines", "heading": "Deploy", "group": "API", "url": "/docs/api/pipelines#deploy", "summary": "Build the two worker images yourself (Layer does not build images), push them, and kubectl-apply the pipeline resources; the operator creates one Deployment per resource plus the embed pool's KEDA object. Apply order doesn't matter because the app creates the gateway pipeline before enqueueing, and the CRD types install with the Helm chart.", "facts": [ { "kind": "code", "literal": "kubectl apply -f pipelines/", "chunkId": "api/pipelines#deploy" } ], "sources": [ { "chunkId": "api/pipelines#deploy", "url": "/docs/api/pipelines#deploy", "anchor": "deploy" } ], "mode": "source-primary", "terms": [ "deploy", "build", "worker", "images", "yourself", "layer", "does", "push", "kubectl", "apply", "pipeline", "resources", "operator", "creates", "deployment", "resource", "plus", "embed", "pool", "keda", "object", "order", "doesn", "matter", "because", "gateway", "before", "enqueueing", "types", "install", "helm", "chart", "pipelines", "workers", "yaml", "references", "registry", "cluster", "pull", "here" ] }, { "id": "api/pipelines#document-lifecycle", "kind": "section", "title": "Pipelines", "heading": "Document lifecycle", "group": "API", "url": "/docs/api/pipelines#document-lifecycle", "summary": "Documents move from pending (chunks stored, awaiting embedding) to indexed (vectors written to the configured VectorStore); embedding is a claim stage held only while leased, recovering to pending on lease expiry. Re-staging a document idempotently resets it to pending with new chunks — the reprocessing path after source changes.", "facts": [ { "kind": "code", "literal": "put chunks put vectors\n (new doc) ──────────► pending ──────────────► indexed\n ▲\n │ re-stage (idempotent)", "chunkId": "api/pipelines#document-lifecycle" }, { "kind": "code", "literal": "embedding", "chunkId": "api/pipelines#document-lifecycle" }, { "kind": "code", "literal": "pending", "chunkId": "api/pipelines#document-lifecycle" } ], "sources": [ { "chunkId": "api/pipelines#document-lifecycle", "url": "/docs/api/pipelines#document-lifecycle", "anchor": "document-lifecycle" } ], "mode": "source-primary", "terms": [ "document", "lifecycle", "documents", "move", "pending", "chunks", "stored", "awaiting", "embedding", "indexed", "vectors", "written", "configured", "vectorstore", "claim", "stage", "held", "only", "while", "leased", "recovering", "lease", "expiry", "staging", "idempotently", "resets", "reprocessing", "path", "after", "source", "changes", "idempotent", "waiting", "namespace", "worker", "recover", "expires", "reprocess", "data" ] }, { "id": "api/pipelines#embed", "kind": "section", "title": "Pipelines", "heading": "Embed", "group": "API", "url": "/docs/api/pipelines#embed", "summary": "The GPU worker loops claiming pending documents with a lease, reads their chunks back, embeds, and writes vectors — which upserts to the configured VectorStore and marks the document indexed; a crashed worker loses nothing. Multivector namespaces on a search-engine store send a bag of vectors per entry, with the first inner vector cached for nearest-to-id lookup. Full worker code shown in Python, Go, and TypeScript.", "facts": [ { "kind": "code", "literal": "indexed", "chunkId": "api/pipelines#embed" }, { "kind": "code", "literal": "kind: search", "chunkId": "api/pipelines#embed" }, { "kind": "code", "literal": "vectors: [[...], [...]]", "chunkId": "api/pipelines#embed" }, { "kind": "code", "literal": "vector: [...]", "chunkId": "api/pipelines#embed" }, { "kind": "code", "literal": "nearest_to_id", "chunkId": "api/pipelines#embed" } ], "sources": [ { "chunkId": "api/pipelines#embed", "url": "/docs/api/pipelines#embed", "anchor": "embed" } ], "mode": "source-primary", "terms": [ "embed", "worker", "loops", "claiming", "pending", "documents", "lease", "reads", "their", "chunks", "back", "embeds", "writes", "vectors", "upserts", "configured", "vectorstore", "marks", "document", "indexed", "crashed", "loses", "nothing", "multivector", "namespaces", "search", "engine", "store", "send", "entry", "first", "inner", "vector", "cached", "nearest", "lookup", "full", "code", "shown", "python" ] }, { "id": "api/pipelines#extract-and-chunk", "kind": "section", "title": "Pipelines", "heading": "Extract and chunk", "group": "API", "url": "/docs/api/pipelines#extract-and-chunk", "summary": "The CPU worker reads the source (an SQS queue in the example), splits text into chunks, and stages them durably (S3, cached in the document cache), marking each document pending. The worker hardcodes nothing — the operator injects the pipeline id, gateway URL, and source reference as environment variables.", "facts": [ { "kind": "code", "literal": "pending", "chunkId": "api/pipelines#extract-and-chunk" }, { "kind": "code", "literal": "spec.sourceRef", "chunkId": "api/pipelines#extract-and-chunk" }, { "kind": "code", "literal": "sourceRef", "chunkId": "api/pipelines#extract-and-chunk" }, { "kind": "code", "literal": "pipelines/extract-chunk.yaml", "chunkId": "api/pipelines#extract-and-chunk" } ], "sources": [ { "chunkId": "api/pipelines#extract-and-chunk", "url": "/docs/api/pipelines#extract-and-chunk", "anchor": "extract-and-chunk" } ], "mode": "source-primary", "terms": [ "extract", "chunk", "worker", "reads", "source", "queue", "example", "splits", "text", "chunks", "stages", "durably", "cached", "document", "cache", "marking", "pending", "hardcodes", "nothing", "operator", "injects", "pipeline", "gateway", "reference", "environment", "variables", "spec", "sourceref", "pipelines", "yaml", "staging", "stores", "marks", "page", "below", "comes", "declared", "extractchunk", "hevlayer", "import" ] }, { "id": "api/pipelines#failure-model", "kind": "section", "title": "Pipelines", "heading": "Failure model", "group": "API", "url": "/docs/api/pipelines#failure-model", "summary": "VectorStore write failures are hard (502; the document stays claimed for re-claim); Aerospike cache failures don't block chunk reads when S3 backing exists; PostgreSQL connectivity failures return 500 and should be retried with backoff. Lease expiry is handled server-side, so crashed workers' documents recover on the next claim sweep.", "facts": [ { "kind": "code", "literal": "embedding", "chunkId": "api/pipelines#failure-model" } ], "sources": [ { "chunkId": "api/pipelines#failure-model", "url": "/docs/api/pipelines#failure-model", "anchor": "failure-model" } ], "mode": "source-primary", "terms": [ "failure", "model", "vectorstore", "write", "failures", "hard", "document", "stays", "claimed", "claim", "aerospike", "cache", "block", "chunk", "reads", "backing", "exists", "postgresql", "connectivity", "return", "should", "retried", "backoff", "lease", "expiry", "handled", "server", "side", "crashed", "workers", "documents", "recover", "next", "sweep", "embedding", "vectors", "route", "returns", "present", "stop" ] }, { "id": "api/pipelines#file-tree", "kind": "section", "title": "Pipelines", "heading": "File tree", "group": "API", "url": "/docs/api/pipelines#file-tree", "summary": "Reference layout for an indexer: two Pipeline YAML resources (CPU extract-chunk and GPU embed stages sharing one pipeline id and queue), the two worker scripts, and an app exposing a trigger-a-run endpoint. YAML declares images, pools, and scaling; every SDK call shown is also a plain REST endpoint.", "facts": [ { "kind": "code", "literal": "indexer/\n├── pipelines/\n│ ├── extract-chunk.yaml # CPU stage — Pipeline resource\n│ └── embed.yaml # GPU stage — Pipeline resource\n├── extract_chunk.py # read the source, stage chunks\n├── embed.py # claim pending docs, write vectors\n└── app.py # REST API: trigger a run, wait for completion", "chunkId": "api/pipelines#file-tree" }, { "kind": "code", "literal": "pipelineId: products", "chunkId": "api/pipelines#file-tree" } ], "sources": [ { "chunkId": "api/pipelines#file-tree", "url": "/docs/api/pipelines#file-tree", "anchor": "file-tree" } ], "mode": "source-primary", "terms": [ "file", "tree", "reference", "layout", "indexer", "pipeline", "yaml", "resources", "extract", "chunk", "embed", "stages", "sharing", "queue", "worker", "scripts", "exposing", "trigger", "endpoint", "declares", "images", "pools", "scaling", "every", "call", "shown", "also", "plain", "rest", "pipelines", "stage", "resource", "read", "source", "chunks", "claim", "pending", "docs", "write", "vectors" ] }, { "id": "api/pipelines#trigger-a-run", "kind": "section", "title": "Pipelines", "heading": "Trigger a run", "group": "API", "url": "/docs/api/pipelines#trigger-a-run", "summary": "A pipeline run ensures the pipeline exists, sends the batch to its source queue, waits for completion, and returns the resulting snapshot. Missing-document staging deduplicates within each pipeline and commits large calls in segments, allowing workers and autoscaling to observe earlier queue depth while later segments are still being added.", "facts": [ { "kind": "code", "literal": "POST /index-runs", "chunkId": "api/pipelines#trigger-a-run" }, { "kind": "code", "literal": "create_missing", "chunkId": "api/pipelines#trigger-a-run" } ], "sources": [ { "chunkId": "api/pipelines#trigger-a-run", "url": "/docs/api/pipelines#trigger-a-run", "anchor": "trigger-a-run" } ], "mode": "source-primary", "terms": [ "trigger", "pipeline", "ensures", "exists", "sends", "batch", "source", "queue", "waits", "completion", "returns", "resulting", "snapshot", "missing", "document", "staging", "deduplicates", "within", "commits", "large", "calls", "segments", "allowing", "workers", "autoscaling", "observe", "earlier", "depth", "while", "later", "still", "being", "added", "post", "index", "runs", "create", "exposes", "rest", "system" ] }, { "id": "api/pipelines#wait-for-completion", "kind": "section", "title": "Pipelines", "heading": "Wait for completion", "group": "API", "url": "/docs/api/pipelines#wait-for-completion", "summary": "A run completes in two steps: poll pipeline status until the pending count reaches zero (the same signal KEDA scales on), then poll namespace history until a snapshot lands past the run's start watermark and flip the application to that SHA. Documents whose durable chunks can't be loaded within the retry budget move to failed with a dead-letter reason.", "facts": [ { "kind": "code", "literal": "pending_count", "chunkId": "api/pipelines#wait-for-completion" }, { "kind": "code", "literal": "status", "chunkId": "api/pipelines#wait-for-completion" }, { "kind": "code", "literal": "waiting_on_upstream", "chunkId": "api/pipelines#wait-for-completion" }, { "kind": "code", "literal": "failed", "chunkId": "api/pipelines#wait-for-completion" }, { "kind": "code", "literal": "failed_reasons", "chunkId": "api/pipelines#wait-for-completion" }, { "kind": "code", "literal": "{\"chunks_unavailable\": 2}", "chunkId": "api/pipelines#wait-for-completion" } ], "sources": [ { "chunkId": "api/pipelines#wait-for-completion", "url": "/docs/api/pipelines#wait-for-completion", "anchor": "wait-for-completion" } ], "mode": "source-primary", "terms": [ "wait", "completion", "completes", "steps", "poll", "pipeline", "status", "until", "pending", "count", "reaches", "zero", "same", "signal", "keda", "scales", "namespace", "history", "snapshot", "lands", "past", "start", "watermark", "flip", "application", "documents", "whose", "durable", "chunks", "loaded", "within", "retry", "budget", "move", "failed", "dead", "letter", "reason", "waiting", "upstream" ] }, { "id": "api/query", "kind": "section", "title": "Query & Fetch", "heading": null, "group": "API", "url": "/docs/api/query", "summary": "Layer's query API adds stable reads, the document cache, and search history around the upstream contract, reporting Layer metadata in x-layer- response headers; the native query body is documented upstream and these sections cover only the overlay.", "facts": [ { "kind": "code", "literal": "x-layer-*", "chunkId": "api/query" }, { "kind": "code", "literal": "vector", "chunkId": "api/query" }, { "kind": "code", "literal": "rank_by", "chunkId": "api/query" }, { "kind": "code", "literal": "filters", "chunkId": "api/query" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/query" }, { "kind": "value", "literal": "StoreSwitch.astro", "chunkId": "api/query" }, { "kind": "value", "literal": "StoreNote.astro", "chunkId": "api/query" }, { "kind": "value", "literal": "Upstream.astro", "chunkId": "api/query" }, { "kind": "value", "literal": "FeatureGate.astro", "chunkId": "api/query" }, { "kind": "value", "literal": "turbopuffer.com", "chunkId": "api/query" } ], "sources": [ { "chunkId": "api/query", "url": "/docs/api/query", "anchor": null } ], "mode": "source-primary", "terms": [ "layer", "query", "adds", "stable", "reads", "document", "cache", "search", "history", "around", "upstream", "contract", "reporting", "metadata", "response", "headers", "native", "body", "documented", "these", "sections", "cover", "only", "overlay", "vector", "rank", "filters", "codetabs", "astro", "storeswitch", "storenote", "featuregate", "turbopuffer", "similarity", "cached", "fetch", "reports", "backed", "runs", "namespace" ] }, { "id": "api/query#batch-fetch", "kind": "section", "title": "Query & Fetch", "heading": "Batch fetch", "group": "API", "url": "/docs/api/query#batch-fetch", "summary": "Batch fetch returns found documents and missing ids inline instead of a partial 404, preserving request order — which makes it a convenient way to reassemble a pipeline's chunks back into their original document by requesting chunk ids in sequence.", "facts": [ { "kind": "code", "literal": "batch = await client.fetch_documents(\"products\", {\n \"ids\": [\"asin-1\", \"asin-2\", \"asin-3\"],\n \"include_attributes\": [\"title\"],\n})", "chunkId": "api/query#batch-fetch" }, { "kind": "code", "literal": "batch, err := client.FetchDocuments(ctx, \"products\", &hevlayer.FetchDocumentsRequest{\n Ids: []string{\"asin-1\", \"asin-2\", \"asin-3\"},\n IncludeAttributes: []string{\"title\"},\n})", "chunkId": "api/query#batch-fetch" }, { "kind": "code", "literal": "const batch = await client.fetchDocuments(\"products\", {\n ids: [\"asin-1\", \"asin-2\", \"asin-3\"],\n include_attributes: [\"title\"],\n});", "chunkId": "api/query#batch-fetch" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/documents\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"ids\": [\"asin-1\", \"asin-2\", \"asin-3\"],\n \"include_attributes\": [\"title\"]\n }'", "chunkId": "api/query#batch-fetch" }, { "kind": "code", "literal": "{\n \"documents\": [\n {\"id\": \"asin-1\", \"attributes\": {\"title\": \"...\"}},\n {\"id\": \"asin-3\", \"attributes\": {\"title\": \"...\"}}\n ],\n \"missing\": [\"asin-2\"]\n}", "chunkId": "api/query#batch-fetch" }, { "kind": "code", "literal": "documents", "chunkId": "api/query#batch-fetch" }, { "kind": "code", "literal": "missing", "chunkId": "api/query#batch-fetch" } ], "sources": [ { "chunkId": "api/query#batch-fetch", "url": "/docs/api/query#batch-fetch", "anchor": "batch-fetch" } ], "mode": "source-primary", "terms": [ "batch", "fetch", "returns", "found", "documents", "missing", "inline", "instead", "partial", "preserving", "request", "order", "makes", "convenient", "reassemble", "pipeline", "chunks", "back", "their", "original", "document", "requesting", "chunk", "sequence", "await", "client", "products", "asin", "include", "attributes", "title", "fetchdocuments", "hevlayer", "fetchdocumentsrequest", "string", "includeattributes", "const", "curl", "post", "layer" ] }, { "id": "api/query#batch-query", "kind": "section", "title": "Query & Fetch", "heading": "Batch query", "group": "API", "url": "/docs/api/query#batch-query", "summary": "A queries array runs several independent rankings in one round trip, returning a parallel results array with every leg held on the same stable cut. Batches take 2–16 legs, reject cursors, and pass upstream rerank bodies through unchanged; use nearest-to-id instead when many seeds should collapse into one ranking, and federated query to fan one query across namespaces.", "facts": [ { "kind": "code", "literal": "batch = await client.batch_query_namespace(\"products\", {\n \"queries\": [\n {\"rank_by\": [\"vector\", \"ANN\", [0.1, 0.2, 0.3]], \"top_k\": 10},\n {\"rank_by\": [\"title\", \"BM25\", \"wireless earbuds\"], \"top_k\": 10},\n ],\n})\n# batch.results[0].rows ranked by vector; batch.results[1].rows by text", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "batch, err := client.BatchQueryNamespace(ctx, \"products\",\n &hevlayer.BatchQueryRequest{\n Queries: []hevlayer.TurbopufferQueryRequest{\n {\"rank_by\": []any{\"vector\", \"ANN\", []float64{0.1, 0.2, 0.3}}, \"top_k\": 10},\n {\"rank_by\": []any{\"title\", \"BM25\", \"wireless earbuds\"}, \"top_k\": 10},\n },\n })", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "const batch = await client.batchQueryNamespace(\"products\", {\n queries: [\n { rank_by: [\"vector\", \"ANN\", [0.1, 0.2, 0.3]], top_k: 10 },\n { rank_by: [\"title\", \"BM25\", \"wireless earbuds\"], top_k: 10 },\n ],\n});\n// batch.results[0].rows ranked by vector; batch.results[1].rows by text", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/query\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"queries\": [\n {\"rank_by\": [\"vector\", \"ANN\", [0.1, 0.2, 0.3]], \"top_k\": 10},\n {\"rank_by\": [\"title\", \"BM25\", \"wireless earbuds\"], \"top_k\": 10}\n ]\n }'", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "nearest_to_id", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "queries", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "results", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "{ \"results\": [{ \"rows\": ... }] }", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "batch_query_namespace", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "rerank_by", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "x-layer-stable-as-of", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "rank_by", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "vector", "chunkId": "api/query#batch-query" }, { "kind": "code", "literal": "cursor", "chunkId": "api/query#batch-query" } ], "sources": [ { "chunkId": "api/query#batch-query", "url": "/docs/api/query#batch-query", "anchor": "batch-query" } ], "mode": "source-primary", "terms": [ "batch", "query", "queries", "array", "runs", "several", "independent", "rankings", "round", "trip", "returning", "parallel", "results", "every", "held", "same", "stable", "batches", "take", "legs", "reject", "cursors", "pass", "upstream", "rerank", "bodies", "through", "unchanged", "nearest", "instead", "many", "seeds", "should", "collapse", "ranking", "federated", "across", "namespaces", "await", "client" ] }, { "id": "api/query#behavior-matrix", "kind": "section", "title": "Query & Fetch", "heading": "Behavior matrix", "group": "API", "url": "/docs/api/query#behavior-matrix", "summary": "Cache-state matrix for fetch: hits serve from cache; misses with the document upstream serve from upstream and backfill; upstream-absent yields 404 (single) or inline missing (batch); cache unavailability serves upstream with a miss-on-error marker.", "facts": [ { "kind": "code", "literal": "missing", "chunkId": "api/query#behavior-matrix" }, { "kind": "code", "literal": "miss-on-error", "chunkId": "api/query#behavior-matrix" } ], "sources": [ { "chunkId": "api/query#behavior-matrix", "url": "/docs/api/query#behavior-matrix", "anchor": "behavior-matrix" } ], "mode": "source-primary", "terms": [ "behavior", "matrix", "cache", "state", "fetch", "hits", "serve", "misses", "document", "upstream", "backfill", "absent", "yields", "single", "inline", "missing", "batch", "unavailability", "serves", "miss", "error", "marker", "present", "unavailable" ] }, { "id": "api/query#counting-matches", "kind": "section", "title": "Query & Fetch", "heading": "Counting matches", "group": "API", "url": "/docs/api/query#counting-matches", "summary": "Counting rows matching a full-text or vector query uses scan count mode with the fts or ann selector on the shared scans endpoint — fts is exact, ann is an approximate radius scan, and both honor the exhaustive flag and count deadline.", "facts": [ { "kind": "code", "literal": "fts", "chunkId": "api/query#counting-matches" }, { "kind": "code", "literal": "ann", "chunkId": "api/query#counting-matches" }, { "kind": "code", "literal": "/scans", "chunkId": "api/query#counting-matches" }, { "kind": "code", "literal": "approximate", "chunkId": "api/query#counting-matches" }, { "kind": "code", "literal": "exhaustive", "chunkId": "api/query#counting-matches" } ], "sources": [ { "chunkId": "api/query#counting-matches", "url": "/docs/api/query#counting-matches", "anchor": "counting-matches" } ], "mode": "source-primary", "terms": [ "counting", "matches", "rows", "matching", "full", "text", "vector", "query", "uses", "scan", "count", "mode", "selector", "shared", "scans", "endpoint", "exact", "approximate", "radius", "both", "honor", "exhaustive", "flag", "deadline", "many", "match", "ranked", "counts", "share", "single", "filter", "flagged" ] }, { "id": "api/query#fetch", "kind": "section", "title": "Query & Fetch", "heading": "Fetch", "group": "API", "url": "/docs/api/query#fetch", "summary": "Fetch is a Layer-only endpoint with no upstream equivalent: the document cache is checked first, and on miss or error the gateway falls through to the backing store and backfills the cache best-effort.", "facts": [], "sources": [ { "chunkId": "api/query#fetch", "url": "/docs/api/query#fetch", "anchor": "fetch" } ], "mode": "source-primary", "terms": [ "fetch", "layer", "only", "endpoint", "upstream", "equivalent", "document", "cache", "checked", "first", "miss", "error", "gateway", "falls", "through", "backing", "store", "backfills", "best", "effort" ] }, { "id": "api/query#hybrid-text-fusion", "kind": "section", "title": "Query & Fetch", "heading": "Hybrid text fusion", "group": "API", "url": "/docs/api/query#hybrid-text-fusion", "summary": "HybridText is a Layer-only rankby spelling that tokenizes the input with turbopuffer's own alyze tokenizer, expands it into a BM25 leg plus one fuzzy leg per token, and RRF-fuses the legs into one typo-tolerant ranking — one expression in, no new endpoint. The ranked field must be declared with both full-text and fuzzy indexing; an optional options object tunes fuzziness, rank constant, per-leg depth, and shard fan-out, and a flag adds per-leg attribution to each row.", "facts": [ { "kind": "code", "literal": "response = await client.query_namespace(\"support-tickets\", {\n \"rank_by\": [\"content\", \"HybridText\", \"conection timout kubernets\"],\n \"top_k\": 10,\n \"filters\": [\"tenant\", \"Eq\", \"t-42\"],\n \"include_attributes\": [\"content\", \"title\"],\n})", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "response, err := client.QueryNamespace(ctx, \"support-tickets\", &hevlayer.QueryRequest{\n RankBy: []any{\"content\", \"HybridText\", \"conection timout kubernets\"},\n TopK: 10,\n Filters: []any{\"tenant\", \"Eq\", \"t-42\"},\n IncludeAttributes: []string{\"content\", \"title\"},\n})", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "const response = await client.queryNamespace(\"support-tickets\", {\n rank_by: [\"content\", \"HybridText\", \"conection timout kubernets\"],\n top_k: 10,\n filters: [\"tenant\", \"Eq\", \"t-42\"],\n include_attributes: [\"content\", \"title\"],\n});", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/support-tickets/query\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"rank_by\": [\"content\", \"HybridText\", \"conection timout kubernets\"],\n \"top_k\": 10,\n \"filters\": [\"tenant\", \"Eq\", \"t-42\"],\n \"include_attributes\": [\"content\", \"title\"]\n }'", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "[\"content\", \"HybridText\", \"conection timout kubernets\", {\n \"fuzziness\": \"auto\",\n \"rank_constant\": 60,\n \"per_leg_limit\": null\n}]", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "HybridText", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "rank_by", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "alyze", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "{\"type\": \"string\", \"full_text_search\": true, \"fuzzy\": true}", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "fuzziness", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "\"auto\"", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "rank_constant", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "60", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "per_leg_limit", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "clamp(5 × top_k, 50, 200)", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "threads", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "Index.spec.scan.threads", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "include_leg_breakdown: true", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "$fused.legs", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "rank", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "score", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "null", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "bm25", "chunkId": "api/query#hybrid-text-fusion" }, { "kind": "code", "literal": "fuzzy:", "chunkId": "api/query#hybrid-text-fusion" } ], "sources": [ { "chunkId": "api/query#hybrid-text-fusion", "url": "/docs/api/query#hybrid-text-fusion", "anchor": "hybrid-text-fusion" } ], "mode": "source-primary", "terms": [ "hybrid", "text", "fusion", "hybridtext", "layer", "only", "rankby", "spelling", "tokenizes", "input", "turbopuffer", "alyze", "tokenizer", "expands", "bm25", "plus", "fuzzy", "token", "fuses", "legs", "typo", "tolerant", "ranking", "expression", "endpoint", "ranked", "field", "must", "declared", "both", "full", "indexing", "optional", "options", "object", "tunes", "fuzziness", "rank", "constant", "depth" ] }, { "id": "api/query#options", "kind": "section", "title": "Query & Fetch", "heading": "Options", "group": "API", "url": "/docs/api/query#options", "summary": "The Auto expression's options: route forces a strategy instead of applying the policy (for re-issue after deferral or A/B comparison), vector supplies a numeric vector or inline Embed (with a field selector for the embedding profile, resolved only for semantic/fused), and fuzziness forwards to hybrid expansion on the text-bearing routes.", "facts": [ { "kind": "code", "literal": "route", "chunkId": "api/query#options" }, { "kind": "code", "literal": "\"auto\"", "chunkId": "api/query#options" }, { "kind": "code", "literal": "\"hybrid_text\"", "chunkId": "api/query#options" }, { "kind": "code", "literal": "\"semantic\"", "chunkId": "api/query#options" }, { "kind": "code", "literal": "\"fused\"", "chunkId": "api/query#options" }, { "kind": "code", "literal": "vector", "chunkId": "api/query#options" }, { "kind": "code", "literal": "[\"Embed\", input, {field?, model?}?]", "chunkId": "api/query#options" }, { "kind": "code", "literal": "field", "chunkId": "api/query#options" }, { "kind": "code", "literal": "Embed", "chunkId": "api/query#options" }, { "kind": "code", "literal": "semantic", "chunkId": "api/query#options" }, { "kind": "code", "literal": "fused", "chunkId": "api/query#options" }, { "kind": "code", "literal": "embed_", "chunkId": "api/query#options" }, { "kind": "code", "literal": "model", "chunkId": "api/query#options" }, { "kind": "code", "literal": "fuzziness", "chunkId": "api/query#options" }, { "kind": "code", "literal": "HybridText", "chunkId": "api/query#options" }, { "kind": "code", "literal": "hybrid_text", "chunkId": "api/query#options" }, { "kind": "code", "literal": "hybrid", "chunkId": "api/query#options" }, { "kind": "code", "literal": "routing", "chunkId": "api/query#options" }, { "kind": "code", "literal": "include_leg_breakdown: true", "chunkId": "api/query#options" }, { "kind": "code", "literal": "$fused.legs", "chunkId": "api/query#options" } ], "sources": [ { "chunkId": "api/query#options", "url": "/docs/api/query#options", "anchor": "options" } ], "mode": "source-primary", "terms": [ "options", "auto", "expression", "route", "forces", "strategy", "instead", "applying", "policy", "issue", "after", "deferral", "comparison", "vector", "supplies", "numeric", "inline", "embed", "field", "selector", "embedding", "profile", "resolved", "only", "semantic", "fused", "fuzziness", "forwards", "hybrid", "expansion", "text", "bearing", "routes", "input", "model", "attr", "hybridtext", "routing", "include", "breakdown" ] }, { "id": "api/query#query-by-id", "kind": "section", "title": "Query & Fetch", "heading": "Query by id", "group": "API", "url": "/docs/api/query#query-by-id", "summary": "nearest-to-id ranks by stored document vectors instead of a raw query vector: the gateway resolves each seed id's vector (cache first, store on miss), averages them into an unweighted centroid, and ranks nearest neighbors — one id for \"more like this\", several for \"more like these\". Unresolvable ids give a 404 naming them; empty or conflicting vector inputs give 422.", "facts": [ { "kind": "code", "literal": "response = await client.query_namespace(\"products\", {\n \"nearest_to_id\": [\"asin-B08N5WRWNW\", \"asin-B07PXGQC1Q\"],\n \"top_k\": 10,\n \"include_attributes\": [\"title\", \"category\"],\n})", "chunkId": "api/query#query-by-id" }, { "kind": "code", "literal": "response, err := client.QueryNamespace(ctx, \"products\", &hevlayer.QueryRequest{\n NearestToID: []string{\"asin-B08N5WRWNW\", \"asin-B07PXGQC1Q\"},\n TopK: 10,\n IncludeAttributes: []string{\"title\", \"category\"},\n})", "chunkId": "api/query#query-by-id" }, { "kind": "code", "literal": "const response = await client.queryNamespace(\"products\", {\n nearest_to_id: [\"asin-B08N5WRWNW\", \"asin-B07PXGQC1Q\"],\n top_k: 10,\n include_attributes: [\"title\", \"category\"],\n});", "chunkId": "api/query#query-by-id" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/query\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"nearest_to_id\": [\"asin-B08N5WRWNW\", \"asin-B07PXGQC1Q\"],\n \"top_k\": 10,\n \"include_attributes\": [\"title\", \"category\"]\n }'", "chunkId": "api/query#query-by-id" }, { "kind": "code", "literal": "nearest_to_id", "chunkId": "api/query#query-by-id" }, { "kind": "code", "literal": "vector", "chunkId": "api/query#query-by-id" } ], "sources": [ { "chunkId": "api/query#query-by-id", "url": "/docs/api/query#query-by-id", "anchor": "query-by-id" } ], "mode": "source-primary", "terms": [ "query", "nearest", "ranks", "stored", "document", "vectors", "instead", "vector", "gateway", "resolves", "seed", "cache", "first", "store", "miss", "averages", "unweighted", "centroid", "neighbors", "more", "like", "several", "these", "unresolvable", "give", "naming", "empty", "conflicting", "inputs", "response", "await", "client", "namespace", "products", "asin", "b08n5wrwnw", "b07pxgqc1q", "include", "attributes", "title" ] }, { "id": "api/query#query-routing", "kind": "section", "title": "Query & Fetch", "heading": "Query routing", "group": "API", "url": "/docs/api/query#query-routing", "summary": "Auto is a Layer-only rankby spelling that picks hybrid-text, semantic, or fused per query from the shape of the input alone, so the branch doesn't live in application code. An inline Embed resolves only after a vector-needing route is selected; without a vector source, such routes return the routing decision instead of results so the app can embed and re-issue, while short keyword traffic executes immediately and never pays for an embedding.", "facts": [ { "kind": "code", "literal": "response = await client.query_namespace(\"support-tickets\", {\n \"rank_by\": [\"title\", \"Auto\", user_input, {\n \"vector\": [\"Embed\", user_input, {\"field\": \"content\"}],\n }],\n \"top_k\": 10,\n \"filters\": [\"tenant\", \"Eq\", \"t-42\"],\n})", "chunkId": "api/query#query-routing" }, { "kind": "code", "literal": "response, err := client.QueryNamespace(ctx, \"support-tickets\", &hevlayer.QueryRequest{\n RankBy: []any{\"title\", \"Auto\", userInput, map[string]any{\n \"vector\": []any{\"Embed\", userInput, map[string]any{\"field\": \"content\"}},\n }},\n TopK: 10,\n Filters: []any{\"tenant\", \"Eq\", \"t-42\"},\n})", "chunkId": "api/query#query-routing" }, { "kind": "code", "literal": "const response = await client.queryNamespace(\"support-tickets\", {\n rank_by: [\"title\", \"Auto\", userInput, {\n vector: [\"Embed\", userInput, { field: \"content\" }],\n }],\n top_k: 10,\n filters: [\"tenant\", \"Eq\", \"t-42\"],\n});", "chunkId": "api/query#query-routing" }, { "kind": "code", "literal": "\"timout\"", "chunkId": "api/query#query-routing" }, { "kind": "code", "literal": "Auto", "chunkId": "api/query#query-routing" }, { "kind": "code", "literal": "rank_by", "chunkId": "api/query#query-routing" }, { "kind": "code", "literal": "Embed", "chunkId": "api/query#query-routing" }, { "kind": "code", "literal": "semantic", "chunkId": "api/query#query-routing" }, { "kind": "code", "literal": "fused", "chunkId": "api/query#query-routing" }, { "kind": "code", "literal": "hybrid_text", "chunkId": "api/query#query-routing" } ], "sources": [ { "chunkId": "api/query#query-routing", "url": "/docs/api/query#query-routing", "anchor": "query-routing" } ], "mode": "source-primary", "terms": [ "query", "routing", "auto", "layer", "only", "rankby", "spelling", "picks", "hybrid", "text", "semantic", "fused", "shape", "input", "alone", "branch", "doesn", "live", "application", "code", "inline", "embed", "resolves", "after", "vector", "needing", "route", "selected", "without", "source", "such", "routes", "return", "decision", "instead", "results", "issue", "while", "short", "keyword" ] }, { "id": "api/query#rank-expressions", "kind": "section", "title": "Query & Fetch", "heading": "Rank expressions", "group": "API", "url": "/docs/api/query#rank-expressions", "summary": "Passing rankby with topk selects an explicit ranking operator handled with the same cache, history, and stable-read behavior as vector queries; native upstream bodies omitting topk stay pass-through, and rankby is mutually exclusive with vector and nearest-to-id.", "facts": [ { "kind": "code", "literal": "rank_by", "chunkId": "api/query#rank-expressions" }, { "kind": "code", "literal": "top_k", "chunkId": "api/query#rank-expressions" }, { "kind": "code", "literal": "vector", "chunkId": "api/query#rank-expressions" }, { "kind": "code", "literal": "nearest_to_id", "chunkId": "api/query#rank-expressions" } ], "sources": [ { "chunkId": "api/query#rank-expressions", "url": "/docs/api/query#rank-expressions", "anchor": "rank-expressions" } ], "mode": "source-primary", "terms": [ "rank", "expressions", "passing", "rankby", "topk", "selects", "explicit", "ranking", "operator", "handled", "same", "cache", "history", "stable", "read", "behavior", "vector", "queries", "native", "upstream", "bodies", "omitting", "stay", "pass", "through", "mutually", "exclusive", "nearest", "need", "instead", "level", "nearesttoid", "shape", "layer", "handles", "portable", "subset", "query", "omit", "remain" ] }, { "id": "api/query#response", "kind": "section", "title": "Query & Fetch", "heading": "Response", "group": "API", "url": "/docs/api/query#response", "summary": "HybridText responses carry the RRF-fused rows plus a hybrid block echoing the effective expansion — tokens, tokens dropped by the cap, fuzziness, rank constant, leg count, and per-leg depth — so defaults are never invisible; optional per-leg attribution and a surfaced flag appear when enabled or when the fallback fired. RRF scores are comparable within a response only, and non-HybridText requests keep upstream-shaped responses byte-for-byte.", "facts": [ { "kind": "code", "literal": "{\n \"rows\": [\n {\n \"id\": \"ticket-4117\",\n \"$score\": 0.0639,\n \"content\": \"...\",\n \"title\": \"Connection timeout on Kubernetes ingress\"\n }\n ],\n \"hybrid\": {\n \"tokens\": [\"conection\", \"timout\", \"kubernets\"],\n \"tokens_dropped\": 0,\n \"fuzziness\": \"auto\",\n \"rank_constant\": 60,\n \"legs\": 4,\n \"per_leg_limit\": 50\n },\n \"next_cursor\": null\n}", "chunkId": "api/query#response" }, { "kind": "code", "literal": "hybrid", "chunkId": "api/query#response" }, { "kind": "code", "literal": "$score", "chunkId": "api/query#response" }, { "kind": "code", "literal": "$fused.legs", "chunkId": "api/query#response" }, { "kind": "code", "literal": "include_leg_breakdown: true", "chunkId": "api/query#response" }, { "kind": "code", "literal": "leg", "chunkId": "api/query#response" }, { "kind": "code", "literal": "rank", "chunkId": "api/query#response" }, { "kind": "code", "literal": "score", "chunkId": "api/query#response" }, { "kind": "code", "literal": "tokens", "chunkId": "api/query#response" }, { "kind": "code", "literal": "tokens_dropped", "chunkId": "api/query#response" }, { "kind": "code", "literal": "legs", "chunkId": "api/query#response" }, { "kind": "code", "literal": "surfaced", "chunkId": "api/query#response" }, { "kind": "code", "literal": "true", "chunkId": "api/query#response" }, { "kind": "code", "literal": "next_cursor", "chunkId": "api/query#response" }, { "kind": "code", "literal": "null", "chunkId": "api/query#response" }, { "kind": "code", "literal": "x-layer-next-cursor", "chunkId": "api/query#response" }, { "kind": "code", "literal": "cursor", "chunkId": "api/query#response" }, { "kind": "code", "literal": "HybridText", "chunkId": "api/query#response" }, { "kind": "code", "literal": "threads", "chunkId": "api/query#response" }, { "kind": "code", "literal": "rerank_by", "chunkId": "api/query#response" } ], "sources": [ { "chunkId": "api/query#response", "url": "/docs/api/query#response", "anchor": "response" } ], "mode": "source-primary", "terms": [ "response", "hybridtext", "responses", "carry", "fused", "rows", "plus", "hybrid", "block", "echoing", "effective", "expansion", "tokens", "dropped", "fuzziness", "rank", "constant", "count", "depth", "defaults", "never", "invisible", "optional", "attribution", "surfaced", "flag", "appear", "enabled", "fallback", "fired", "scores", "comparable", "within", "only", "requests", "keep", "upstream", "shaped", "byte", "ticket" ] }, { "id": "api/query#response-1", "kind": "section", "title": "Query & Fetch", "heading": "Response", "group": "API", "url": "/docs/api/query#response-1", "summary": "Every Auto response carries a routing block naming the chosen route, the policy version (or forced), the token count read, and whether the route executed — false meaning a deferral where the app should embed and re-issue. Routed queries keep single-strategy semantics: one consistency cut, all-or-nothing legs, one search-history entry, and inline-Embed measurements merged into performance.", "facts": [ { "kind": "code", "literal": "{\n \"rows\": [{\"id\": \"ticket-4117\", \"$score\": 0.0639, \"title\": \"...\"}],\n \"routing\": {\n \"route\": \"hybrid_text\",\n \"policy\": \"v1\",\n \"tokens\": 1,\n \"executed\": true\n },\n \"hybrid\": {\"tokens\": [\"timout\"], \"tokens_dropped\": 0, \"fuzziness\": \"auto\", \"rank_constant\": 60, \"legs\": 2, \"per_leg_limit\": 50}\n}", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "Auto", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "routing", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "route", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "policy", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "\"forced\"", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "tokens", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "executed", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "false", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "rows", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "Embed", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "embedding_tokens", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "embedding_ms", "chunkId": "api/query#response-1" }, { "kind": "code", "literal": "performance", "chunkId": "api/query#response-1" } ], "sources": [ { "chunkId": "api/query#response-1", "url": "/docs/api/query#response-1", "anchor": "response-1" } ], "mode": "source-primary", "terms": [ "response", "every", "auto", "carries", "routing", "block", "naming", "chosen", "route", "policy", "version", "forced", "token", "count", "read", "whether", "executed", "false", "meaning", "deferral", "should", "embed", "issue", "routed", "queries", "keep", "single", "strategy", "semantics", "consistency", "nothing", "legs", "search", "history", "entry", "inline", "measurements", "merged", "performance", "rows" ] }, { "id": "api/query#routing-policy", "kind": "section", "title": "Query & Fetch", "heading": "Routing policy", "group": "API", "url": "/docs/api/query#routing-policy", "summary": "The v1 policy routes on token count under the hybrid tokenizer policy: two or fewer tokens go hybrid-text, eight or more go semantic, three to seven go fused. Vector availability gates execution, never the choice, and the policy version travels in the echo and search history so threshold changes are visible.", "facts": [ { "kind": "code", "literal": "hybrid_text", "chunkId": "api/query#routing-policy" }, { "kind": "code", "literal": "semantic", "chunkId": "api/query#routing-policy" }, { "kind": "code", "literal": "fused", "chunkId": "api/query#routing-policy" }, { "kind": "code", "literal": "Embed", "chunkId": "api/query#routing-policy" }, { "kind": "code", "literal": "vector", "chunkId": "api/query#routing-policy" }, { "kind": "code", "literal": "\"policy\": \"v1\"", "chunkId": "api/query#routing-policy" } ], "sources": [ { "chunkId": "api/query#routing-policy", "url": "/docs/api/query#routing-policy", "anchor": "routing-policy" } ], "mode": "source-primary", "terms": [ "routing", "policy", "routes", "token", "count", "under", "hybrid", "tokenizer", "fewer", "tokens", "text", "eight", "more", "semantic", "three", "seven", "fused", "vector", "availability", "gates", "execution", "never", "choice", "version", "travels", "echo", "search", "history", "threshold", "changes", "visible", "embed", "reads", "input", "same", "fusion", "route", "runs", "hybridtext", "expansion" ] }, { "id": "api/query#semantics", "kind": "section", "title": "Query & Fetch", "heading": "Semantics", "group": "API", "url": "/docs/api/query#semantics", "summary": "Fusion semantics: RRF uses the effective leg order (BM25, per-token fuzzy, then semantic on fused routes), every leg sees one consistency cut with filters replicated, any leg failure fails the whole request, and the query logs to search history as a single entry that replays the whole expansion as a unit.", "facts": [ { "kind": "code", "literal": "include_leg_breakdown: true", "chunkId": "api/query#semantics" }, { "kind": "code", "literal": "filters", "chunkId": "api/query#semantics" }, { "kind": "code", "literal": "x-layer-stable-as-of", "chunkId": "api/query#semantics" }, { "kind": "code", "literal": "HybridText", "chunkId": "api/query#semantics" } ], "sources": [ { "chunkId": "api/query#semantics", "url": "/docs/api/query#semantics", "anchor": "semantics" } ], "mode": "source-primary", "terms": [ "semantics", "fusion", "uses", "effective", "order", "bm25", "token", "fuzzy", "semantic", "fused", "routes", "every", "sees", "consistency", "filters", "replicated", "failure", "fails", "whole", "request", "query", "logs", "search", "history", "single", "entry", "replays", "expansion", "unit", "include", "breakdown", "true", "layer", "stable", "hybridtext", "first", "routed", "queries", "includelegbreakdown", "require" ] }, { "id": "api/query#single-fetch", "kind": "section", "title": "Query & Fetch", "heading": "Single fetch", "group": "API", "url": "/docs/api/query#single-fetch", "summary": "Single fetch returns a document by id with an x-layer-cache header distinguishing hit, backfilled miss, and miss-on-error; a document missing from both cache and upstream is a 404.", "facts": [ { "kind": "code", "literal": "doc = await client.fetch_document(\n \"products\",\n \"asin-B08N5WRWNW\",\n include_attributes=[\"title\", \"category\"],\n)", "chunkId": "api/query#single-fetch" }, { "kind": "code", "literal": "doc, err := client.FetchDocument(ctx, \"products\", \"asin-B08N5WRWNW\",\n &hevlayer.FetchDocumentParams{\n IncludeAttributes: []string{\"title\", \"category\"},\n })", "chunkId": "api/query#single-fetch" }, { "kind": "code", "literal": "const doc = await client.fetchDocument(\"products\", \"asin-B08N5WRWNW\", {\n includeAttributes: [\"title\", \"category\"],\n});", "chunkId": "api/query#single-fetch" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces/products/documents/asin-B08N5WRWNW?include_attributes=title,category\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/query#single-fetch" }, { "kind": "code", "literal": "x-layer-cache: hit", "chunkId": "api/query#single-fetch" }, { "kind": "code", "literal": "x-layer-cache: miss", "chunkId": "api/query#single-fetch" }, { "kind": "code", "literal": "x-layer-cache: miss-on-error", "chunkId": "api/query#single-fetch" } ], "sources": [ { "chunkId": "api/query#single-fetch", "url": "/docs/api/query#single-fetch", "anchor": "single-fetch" } ], "mode": "source-primary", "terms": [ "single", "fetch", "returns", "document", "layer", "cache", "header", "distinguishing", "backfilled", "miss", "error", "missing", "both", "upstream", "await", "client", "products", "asin", "b08n5wrwnw", "include", "attributes", "title", "category", "fetchdocument", "hevlayer", "fetchdocumentparams", "includeattributes", "string", "const", "curl", "gateway", "namespaces", "documents", "authorization", "bearer", "layergatewayurl", "layergatewayapikey", "outcome", "status", "cached" ] }, { "id": "api/query#stable-reads", "kind": "section", "title": "Query & Fetch", "heading": "Stable reads", "group": "API", "url": "/docs/api/query#stable-reads", "summary": "Queries default to stable reads: a control loop polls each namespace's index status and records a watermark, and while the index is updating the gateway injects a hidden upserted-at predicate so reads never see partially-indexed rows, retrying once with the filter forced on after a 429. Responses carry the stable-as-of header and a top-level next-cursor; explicit temporal cuts via as-of or between compose with filters and Layer rank expressions, and watcher cadences are tunable per gateway.", "facts": [ { "kind": "code", "literal": "HTTP/1.1 200 OK\nx-layer-stable-as-of: 1715600400000\n\n{\"rows\":[{\"id\":\"asin-B08N5WRWNW\",\"$dist\":0.42,\"title\":\"...\"}]}", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "x-layer-stable-as-of", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "consistency=eventual", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "index.status", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "poll_start - safety_margin", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "Updating", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "_hevlayer_upserted_at <= watermark", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "Stable", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "Unknown", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "next_cursor", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "null", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "x-layer-next-cursor", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "cursor", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "HybridText", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "Auto", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "as_of", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "between", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "as_of: 1747300000123", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "_hevlayer_upserted_at <= 1747300000123", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "between: [lo, hi]", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "lo < _hevlayer_upserted_at <= hi", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "filters", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "nearest_to_id", "chunkId": "api/query#stable-reads" }, { "kind": "code", "literal": "consistency", "chunkId": "api/query#stable-reads" } ], "sources": [ { "chunkId": "api/query#stable-reads", "url": "/docs/api/query#stable-reads", "anchor": "stable-reads" } ], "mode": "source-primary", "terms": [ "stable", "reads", "queries", "default", "control", "loop", "polls", "namespace", "index", "status", "records", "watermark", "while", "updating", "gateway", "injects", "hidden", "upserted", "predicate", "never", "partially", "indexed", "rows", "retrying", "once", "filter", "forced", "after", "responses", "carry", "header", "level", "next", "cursor", "explicit", "temporal", "cuts", "between", "compose", "filters" ] }, { "id": "api/query#surfacing-fallback", "kind": "section", "title": "Query & Fetch", "heading": "Surfacing fallback", "group": "API", "url": "/docs/api/query#surfacing-fallback", "summary": "When a fully-misspelled query's primary expansion fuses to zero rows (BM25 drops non-matching tokens), Layer re-runs one fuzzy leg per token reordered by edit distance and fuses those instead, so typo-heavy queries still surface near matches. The response then carries a surfaced flag in the hybrid block; working queries never reach this path.", "facts": [ { "kind": "code", "literal": "\"surfaced\": true", "chunkId": "api/query#surfacing-fallback" }, { "kind": "code", "literal": "hybrid", "chunkId": "api/query#surfacing-fallback" }, { "kind": "code", "literal": "legs", "chunkId": "api/query#surfacing-fallback" }, { "kind": "code", "literal": "surfaced", "chunkId": "api/query#surfacing-fallback" } ], "sources": [ { "chunkId": "api/query#surfacing-fallback", "url": "/docs/api/query#surfacing-fallback", "anchor": "surfacing-fallback" } ], "mode": "source-primary", "terms": [ "surfacing", "fallback", "fully", "misspelled", "query", "primary", "expansion", "fuses", "zero", "rows", "bm25", "drops", "matching", "tokens", "layer", "runs", "fuzzy", "token", "reordered", "edit", "distance", "those", "instead", "typo", "heavy", "queries", "still", "surface", "near", "matches", "response", "carries", "surfaced", "flag", "hybrid", "block", "working", "never", "reach", "path" ] }, { "id": "api/query#tokenization", "kind": "section", "title": "Query & Fetch", "heading": "Tokenization", "group": "API", "url": "/docs/api/query#tokenization", "summary": "The fixed tokenizer policy: split on Unicode word boundaries and lowercase via alyze, drop tokens under two characters, dedupe, and cap at fifteen tokens (counted in tokens-dropped when cut). No stemming, stopwords, or language detection; the input must yield at least one token.", "facts": [ { "kind": "code", "literal": "alyze", "chunkId": "api/query#tokenization" }, { "kind": "code", "literal": "word_v4", "chunkId": "api/query#tokenization" }, { "kind": "code", "literal": "tokens_dropped", "chunkId": "api/query#tokenization" } ], "sources": [ { "chunkId": "api/query#tokenization", "url": "/docs/api/query#tokenization", "anchor": "tokenization" } ], "mode": "source-primary", "terms": [ "tokenization", "fixed", "tokenizer", "policy", "split", "unicode", "word", "boundaries", "lowercase", "alyze", "drop", "tokens", "under", "characters", "dedupe", "fifteen", "counted", "dropped", "stemming", "stopwords", "language", "detection", "input", "must", "yield", "least", "token", "string", "becomes", "documented", "code", "behind", "turbopuffer", "production", "wordv4", "punctuation", "only", "never", "survive", "shorter" ] }, { "id": "api/query#validation", "kind": "section", "title": "Query & Fetch", "heading": "Validation", "group": "API", "url": "/docs/api/query#validation", "summary": "HybridText validation, all 422: zero tokens under the policy, HybridText nested inside a batch queries array, and out-of-range fuzziness, rank constant, per-leg limit, or threads.", "facts": [ { "kind": "code", "literal": "422", "chunkId": "api/query#validation" }, { "kind": "code", "literal": "HybridText", "chunkId": "api/query#validation" }, { "kind": "code", "literal": "queries", "chunkId": "api/query#validation" }, { "kind": "code", "literal": "fuzziness", "chunkId": "api/query#validation" }, { "kind": "code", "literal": "\"auto\" \\| 0 \\| 1 \\| 2", "chunkId": "api/query#validation" }, { "kind": "code", "literal": "rank_constant", "chunkId": "api/query#validation" }, { "kind": "code", "literal": "per_leg_limit", "chunkId": "api/query#validation" }, { "kind": "code", "literal": "threads", "chunkId": "api/query#validation" } ], "sources": [ { "chunkId": "api/query#validation", "url": "/docs/api/query#validation", "anchor": "validation" } ], "mode": "source-primary", "terms": [ "validation", "hybridtext", "zero", "tokens", "under", "policy", "nested", "inside", "batch", "queries", "array", "range", "fuzziness", "rank", "constant", "limit", "threads", "auto", "return", "condition", "input", "yields", "nothing", "expand", "expansion", "already", "deep", "construction", "rankconstant", "perleglimit", "gateway", "pick", "between", "hybrid", "text", "semantic", "retrieval", "query", "routing" ] }, { "id": "api/query#validation-1", "kind": "section", "title": "Query & Fetch", "heading": "Validation", "group": "API", "url": "/docs/api/query#validation-1", "summary": "Auto validation, all 422: forcing semantic or fused without a vector (forcing asserts you have one; only auto-routing defers), zero tokens, vector dimensionality mismatch, and Auto inside a batch queries array.", "facts": [ { "kind": "code", "literal": "422", "chunkId": "api/query#validation-1" }, { "kind": "code", "literal": "\"semantic\"", "chunkId": "api/query#validation-1" }, { "kind": "code", "literal": "\"fused\"", "chunkId": "api/query#validation-1" }, { "kind": "code", "literal": "vector", "chunkId": "api/query#validation-1" }, { "kind": "code", "literal": "Auto", "chunkId": "api/query#validation-1" }, { "kind": "code", "literal": "queries", "chunkId": "api/query#validation-1" } ], "sources": [ { "chunkId": "api/query#validation-1", "url": "/docs/api/query#validation-1", "anchor": "validation-1" } ], "mode": "source-primary", "terms": [ "validation", "auto", "forcing", "semantic", "fused", "without", "vector", "asserts", "only", "routing", "defers", "zero", "tokens", "dimensionality", "mismatch", "inside", "batch", "queries", "array", "return", "condition", "forced", "input", "yields", "under", "policy", "nothing", "route", "same", "check", "plain", "query", "inherited", "hybrid", "text", "fusion" ] }, { "id": "api/response-headers", "kind": "section", "title": "Response Headers", "heading": null, "group": "API", "url": "/docs/api/response-headers", "summary": "Layer keeps compatible read bodies upstream-shaped and returns its metadata in headers: the stable-as-of watermark, pagination cursor, cache status, warnings, license-grace marker, and W3C trace context. SDKs also expose these as fields on parsed response objects.", "facts": [ { "kind": "code", "literal": "x-layer-stable-as-of", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "x-layer-next-cursor", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "x-layer-cache", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "hit", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "miss", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "miss-on-error", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "x-layer-warning", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "vector_attribute_dropped", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "x-hevlayer-license-grace", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "true", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "traceparent", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "query_namespace", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "rows", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "stable_as_of", "chunkId": "api/response-headers" }, { "kind": "code", "literal": "next_cursor", "chunkId": "api/response-headers" } ], "sources": [ { "chunkId": "api/response-headers", "url": "/docs/api/response-headers", "anchor": null } ], "mode": "source-primary", "terms": [ "layer", "keeps", "compatible", "read", "bodies", "upstream", "shaped", "returns", "metadata", "headers", "stable", "watermark", "pagination", "cursor", "cache", "status", "warnings", "license", "grace", "marker", "trace", "context", "sdks", "also", "expose", "these", "fields", "parsed", "response", "objects", "next", "miss", "error", "warning", "vector", "attribute", "dropped", "hevlayer", "true", "traceparent" ] }, { "id": "api/scans", "kind": "section", "title": "Scan", "heading": null, "group": "API", "url": "/docs/api/scans", "summary": "A scan is on-demand row selection over a namespace by one of four selectors — filter, full-text (fts), hybrid-text, or ANN radius — returning IDs or distinct values as asynchronous jobs, or a count synchronously. At most one ranked selector per request (filters AND onto it), origin scatter/gather activates only after shard backfill completes, and uses include bulk exports, consistency checks, counts, and field-value discovery.", "facts": [ { "kind": "code", "literal": "mode: ids", "chunkId": "api/scans" }, { "kind": "code", "literal": "mode: count", "chunkId": "api/scans" }, { "kind": "code", "literal": "mode: values", "chunkId": "api/scans" }, { "kind": "code", "literal": "filters", "chunkId": "api/scans" }, { "kind": "code", "literal": "fts", "chunkId": "api/scans" }, { "kind": "code", "literal": "hybrid_text", "chunkId": "api/scans" }, { "kind": "code", "literal": "ann", "chunkId": "api/scans" }, { "kind": "code", "literal": "radius", "chunkId": "api/scans" }, { "kind": "code", "literal": "threads", "chunkId": "api/scans" }, { "kind": "code", "literal": "Index.spec.scan.threads", "chunkId": "api/scans" }, { "kind": "code", "literal": "POST /v2/namespaces/{ns}/init", "chunkId": "api/scans" }, { "kind": "code", "literal": "layer.shard_lag_rows", "chunkId": "api/scans" }, { "kind": "code", "literal": "_hevlayer_shard", "chunkId": "api/scans" }, { "kind": "code", "literal": "422", "chunkId": "api/scans" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/scans" } ], "sources": [ { "chunkId": "api/scans", "url": "/docs/api/scans", "anchor": null } ], "mode": "source-primary", "terms": [ "scan", "demand", "selection", "namespace", "four", "selectors", "filter", "full", "text", "hybrid", "radius", "returning", "distinct", "values", "asynchronous", "jobs", "count", "synchronously", "most", "ranked", "selector", "request", "filters", "onto", "origin", "scatter", "gather", "activates", "only", "after", "shard", "backfill", "completes", "uses", "include", "bulk", "exports", "consistency", "checks", "counts" ] }, { "id": "api/scans#auto-mode-policy", "kind": "section", "title": "Scan", "heading": "Auto-Mode Policy", "group": "API", "url": "/docs/api/scans#auto-mode-policy", "summary": "Auto source ties cache freshness to the stable-read watermark via a per-namespace cache-warmed-through marker: an empty cache runs origin and stamps it, a fresh cache serves directly, and a stale cache serves while starting a background origin warm. Cache-served scans add a warmed-through predicate so the result is a stable warmed view.", "facts": [ { "kind": "code", "literal": "cache_warmed_through", "chunkId": "api/scans#auto-mode-policy" }, { "kind": "code", "literal": "cache_warmed_through >= watermark", "chunkId": "api/scans#auto-mode-policy" }, { "kind": "code", "literal": "cache_warmed_through < watermark", "chunkId": "api/scans#auto-mode-policy" }, { "kind": "code", "literal": "_hevlayer_upserted_at <= cache_warmed_through", "chunkId": "api/scans#auto-mode-policy" } ], "sources": [ { "chunkId": "api/scans#auto-mode-policy", "url": "/docs/api/scans#auto-mode-policy", "anchor": "auto-mode-policy" } ], "mode": "source-primary", "terms": [ "auto", "mode", "policy", "source", "ties", "cache", "freshness", "stable", "read", "watermark", "namespace", "warmed", "through", "marker", "empty", "runs", "origin", "stamps", "fresh", "serves", "directly", "stale", "while", "starting", "background", "warm", "served", "scans", "predicate", "result", "view", "hevlayer", "upserted", "same", "consistency", "reads", "gateway", "tracks", "cachewarmedthrough", "observed" ] }, { "id": "api/scans#bounding-ranked-scans", "kind": "section", "title": "Scan", "heading": "Bounding ranked scans", "group": "API", "url": "/docs/api/scans#bounding-ranked-scans", "summary": "Ranked selectors fan out one capped upstream query per shard; threads bounds width while exhaustive and the timeout bound depth. Non-exhaustive scans report saturated shards as lower bounds with a bounded flag; exhaustive BM25 recurses via score-band pagination, ANN counts in-radius rows gateway-side, and bounded (shard saturation) is independent of approximate (ANN recall fuzz).", "facts": [ { "kind": "code", "literal": "top_k = 10_000", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "threads", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "exhaustive", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "timeout_seconds", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "exhaustive: false", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "bounded: true", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "shards_saturated > 0", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "exhaustive: true", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "$score < last", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "id", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "$dist", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "$dist <= radius", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "bounded", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "approximate", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": ">=", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "ann", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "bounded: false", "chunkId": "api/scans#bounding-ranked-scans" }, { "kind": "code", "literal": "approximate: true", "chunkId": "api/scans#bounding-ranked-scans" } ], "sources": [ { "chunkId": "api/scans#bounding-ranked-scans", "url": "/docs/api/scans#bounding-ranked-scans", "anchor": "bounding-ranked-scans" } ], "mode": "source-primary", "terms": [ "bounding", "ranked", "scans", "selectors", "capped", "upstream", "query", "shard", "threads", "bounds", "width", "while", "exhaustive", "timeout", "bound", "depth", "report", "saturated", "shards", "lower", "bounded", "flag", "bm25", "recurses", "score", "band", "pagination", "counts", "radius", "rows", "gateway", "side", "saturation", "independent", "approximate", "recall", "fuzz", "seconds", "false", "true" ] }, { "id": "api/scans#count-mode", "kind": "section", "title": "Scan", "heading": "Count Mode", "group": "API", "url": "/docs/api/scans#count-mode", "summary": "Count mode returns synchronously with the serving source, snapshot sha and watermark when snapshot-served, or boundedness/saturation/thread detail when live. Snapshot eligibility requires a single leaf Eq or In filter on a snapshotted field (else fall-through under auto, 412 under explicit snapshot), and temporal selectors force live evaluation.", "facts": [ { "kind": "code", "literal": "count = await client.create_scan(\"products\", {\n \"mode\": \"count\",\n \"source\": \"auto\",\n \"filters\": [\"category\", \"Eq\", \"Electronics\"],\n \"threads\": 8,\n \"timeout_seconds\": 30,\n})", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "count, err := client.CreateScan(ctx, \"products\", &hevlayer.CreateScanRequest{\n Mode: \"count\",\n Source: \"auto\",\n Filters: []interface{}{\"category\", \"Eq\", \"Electronics\"},\n Threads: 8,\n TimeoutSeconds: 30,\n})", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "const count = await client.createScan(\"products\", {\n mode: \"count\",\n source: \"auto\",\n filters: [\"category\", \"Eq\", \"Electronics\"],\n threads: 8,\n timeout_seconds: 30,\n});", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/scans\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"mode\": \"count\",\n \"source\": \"auto\",\n \"filters\": [\"category\", \"Eq\", \"Electronics\"],\n \"threads\": 8,\n \"timeout_seconds\": 30\n }'", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "{\n \"count\": 4210,\n \"served_by\": \"snapshot\",\n \"snapshot_sha\": \"3f9e8b21\",\n \"watermark_ms\": 1747300000123,\n \"elapsed_ms\": 3\n}", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "{\n \"count\": 4210,\n \"served_by\": \"origin\",\n \"bounded\": false,\n \"timed_out\": false,\n \"shards_saturated\": 0,\n \"shards_total\": 1,\n \"threads\": 1,\n \"elapsed_ms\": 42\n}", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "watermark_ms", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "x-layer-stable-as-of", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "auto", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "snapshot", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "cache", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "origin", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "Eq", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "In", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "fields[]", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "And", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "Or", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "Not", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "412 precondition_failed", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "source: snapshot", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "as_of", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "_hevlayer_upserted_at <= as_of", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "between: [lo, hi]", "chunkId": "api/scans#count-mode" }, { "kind": "code", "literal": "lo < _hevlayer_upserted_at <= hi", "chunkId": "api/scans#count-mode" } ], "sources": [ { "chunkId": "api/scans#count-mode", "url": "/docs/api/scans#count-mode", "anchor": "count-mode" } ], "mode": "source-primary", "terms": [ "count", "mode", "returns", "synchronously", "serving", "source", "snapshot", "watermark", "served", "boundedness", "saturation", "thread", "detail", "live", "eligibility", "requires", "single", "leaf", "filter", "snapshotted", "field", "else", "fall", "through", "under", "auto", "explicit", "temporal", "selectors", "force", "evaluation", "await", "client", "create", "scan", "products", "filters", "category", "electronics", "threads" ] }, { "id": "api/scans#fan-out-width", "kind": "section", "title": "Scan", "heading": "Fan-out width", "group": "API", "url": "/docs/api/scans#fan-out-width", "summary": "threads means concurrent upstream requests per scan, not OS threads, resolved from the request, then the Index's scan setting, then the default of 8, clamped to active shards and the server cap of 32 and echoed on origin responses. Snapshot and cache reads don't fan out and ignore it.", "facts": [ { "kind": "code", "literal": "threads", "chunkId": "api/scans#fan-out-width" }, { "kind": "code", "literal": "spec.scan.threads", "chunkId": "api/scans#fan-out-width" }, { "kind": "code", "literal": "Index", "chunkId": "api/scans#fan-out-width" }, { "kind": "code", "literal": "32", "chunkId": "api/scans#fan-out-width" } ], "sources": [ { "chunkId": "api/scans#fan-out-width", "url": "/docs/api/scans#fan-out-width", "anchor": "fan-out-width" } ], "mode": "source-primary", "terms": [ "width", "threads", "means", "concurrent", "upstream", "requests", "scan", "resolved", "request", "index", "setting", "default", "clamped", "active", "shards", "server", "echoed", "origin", "responses", "snapshot", "cache", "reads", "ignore", "spec", "scans", "shard", "sets", "maximum", "number", "those", "single", "flight", "once", "operating", "system", "gateway", "async", "resolution", "order", "namespace" ] }, { "id": "api/scans#filters", "kind": "section", "title": "Scan", "heading": "Filters", "group": "API", "url": "/docs/api/scans#filters", "summary": "Scans accept the same filter array as query: pushed to turbopuffer on origin scans, evaluated against cached attributes on cache scans with a supported operator subset. Auto falls back to origin for filters the cache can't evaluate; explicit cache source fails rather than returning partial results.", "facts": [ { "kind": "code", "literal": "Eq", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "NotEq", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "Gt", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "Gte", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "Lt", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "Lte", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "In", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "NotIn", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "And", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "Or", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "Not", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "auto", "chunkId": "api/scans#filters" }, { "kind": "code", "literal": "source: cache", "chunkId": "api/scans#filters" } ], "sources": [ { "chunkId": "api/scans#filters", "url": "/docs/api/scans#filters", "anchor": "filters" } ], "mode": "source-primary", "terms": [ "filters", "scans", "accept", "same", "filter", "array", "query", "pushed", "turbopuffer", "origin", "evaluated", "against", "cached", "attributes", "cache", "supported", "operator", "subset", "auto", "falls", "back", "evaluate", "explicit", "source", "fails", "rather", "returning", "partial", "results", "noteq", "notin", "gateway", "evaluates", "document", "operators", "sees", "cannot", "uses", "unsupported" ] }, { "id": "api/scans#full-text-count", "kind": "section", "title": "Scan", "heading": "Full-text count", "group": "API", "url": "/docs/api/scans#full-text-count", "summary": "The fts selector counts rows matching a BM25 query exactly, always running origin scatter/gather (source must be omitted, auto, or origin), with filters ANDed on as an extra constraint.", "facts": [ { "kind": "code", "literal": "count = await client.create_scan(\"products\", {\n \"mode\": \"count\",\n \"fts\": {\"field\": \"title\", \"query\": \"wireless headphones\"},\n \"filters\": [\"category\", \"Eq\", \"Electronics\"],\n \"exhaustive\": True,\n})", "chunkId": "api/scans#full-text-count" }, { "kind": "code", "literal": "count, err := client.CreateScan(ctx, \"products\", &hevlayer.CreateScanRequest{\n Mode: \"count\",\n Fts: &hevlayer.FtsScan{Field: \"title\", Query: \"wireless headphones\"},\n Filters: []interface{}{\"category\", \"Eq\", \"Electronics\"},\n Exhaustive: true,\n})", "chunkId": "api/scans#full-text-count" }, { "kind": "code", "literal": "const count = await client.createScan(\"products\", {\n mode: \"count\",\n fts: { field: \"title\", query: \"wireless headphones\" },\n filters: [\"category\", \"Eq\", \"Electronics\"],\n exhaustive: true,\n});", "chunkId": "api/scans#full-text-count" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/scans\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"mode\": \"count\",\n \"fts\": {\"field\": \"title\", \"query\": \"wireless headphones\"},\n \"filters\": [\"category\", \"Eq\", \"Electronics\"],\n \"exhaustive\": true\n }'", "chunkId": "api/scans#full-text-count" }, { "kind": "code", "literal": "fts", "chunkId": "api/scans#full-text-count" }, { "kind": "code", "literal": "source", "chunkId": "api/scans#full-text-count" }, { "kind": "code", "literal": "auto", "chunkId": "api/scans#full-text-count" }, { "kind": "code", "literal": "origin", "chunkId": "api/scans#full-text-count" }, { "kind": "code", "literal": "filters", "chunkId": "api/scans#full-text-count" } ], "sources": [ { "chunkId": "api/scans#full-text-count", "url": "/docs/api/scans#full-text-count", "anchor": "full-text-count" } ], "mode": "source-primary", "terms": [ "full", "text", "count", "selector", "counts", "rows", "matching", "bm25", "query", "exactly", "always", "running", "origin", "scatter", "gather", "source", "must", "omitted", "auto", "filters", "anded", "extra", "constraint", "await", "client", "create", "scan", "products", "mode", "field", "title", "wireless", "headphones", "category", "electronics", "exhaustive", "true", "createscan", "hevlayer", "createscanrequest" ] }, { "id": "api/scans#high-cardinality", "kind": "section", "title": "Scan", "heading": "High cardinality", "group": "API", "url": "/docs/api/scans#high-cardinality", "summary": "Values scans are the enumeration path for fields snapshot histograms skip at their 10,000-distinct-value cap; a values job caps its own listing at 1,000,000 distinct values in gateway memory. Crossing the cap completes rather than fails — counts stay exact, the listing truncates deterministically to the top values, and truncated, bounded, and approximate are three independent flags.", "facts": [ { "kind": "code", "literal": "truncated: true", "chunkId": "api/scans#high-cardinality" }, { "kind": "code", "literal": "truncated", "chunkId": "api/scans#high-cardinality" }, { "kind": "code", "literal": "bounded", "chunkId": "api/scans#high-cardinality" }, { "kind": "code", "literal": "approximate", "chunkId": "api/scans#high-cardinality" }, { "kind": "code", "literal": "top_k", "chunkId": "api/scans#high-cardinality" } ], "sources": [ { "chunkId": "api/scans#high-cardinality", "url": "/docs/api/scans#high-cardinality", "anchor": "high-cardinality" } ], "mode": "source-primary", "terms": [ "high", "cardinality", "values", "scans", "enumeration", "path", "fields", "snapshot", "histograms", "skip", "their", "distinct", "value", "caps", "listing", "gateway", "memory", "crossing", "completes", "rather", "fails", "counts", "stay", "exact", "truncates", "deterministically", "truncated", "bounded", "approximate", "three", "independent", "flags", "true", "facet", "field", "beyond", "exactly", "those", "accumulates", "histogram" ] }, { "id": "api/scans#hybrid-text-count", "kind": "section", "title": "Scan", "heading": "Hybrid text count", "group": "API", "url": "/docs/api/scans#hybrid-text-count", "summary": "The hybrid-text selector counts the deduped union of the BM25 leg, per-token fuzzy legs, and per-token surfacing legs — a superset of the hybridtext query route, which only adds surfacing legs when primary legs return nothing — so it can count more rows than the route returns. Use it for a generous live count beside hybridtext or auto results; plain fts counts exact BM25 only.", "facts": [ { "kind": "code", "literal": "count = await client.create_scan(\"products\", {\n \"mode\": \"count\",\n \"hybrid_text\": {\"field\": \"title\", \"query\": \"wireles headphones\"},\n \"filters\": [\"category\", \"Eq\", \"Electronics\"],\n})", "chunkId": "api/scans#hybrid-text-count" }, { "kind": "code", "literal": "count, err := client.CreateScan(ctx, \"products\", &hevlayer.CreateScanRequest{\n Mode: \"count\",\n HybridText: &hevlayer.HybridTextScan{Field: \"title\", Query: \"wireles headphones\"},\n Filters: []interface{}{\"category\", \"Eq\", \"Electronics\"},\n})", "chunkId": "api/scans#hybrid-text-count" }, { "kind": "code", "literal": "const count = await client.createScan(\"products\", {\n mode: \"count\",\n hybrid_text: { field: \"title\", query: \"wireles headphones\" },\n filters: [\"category\", \"Eq\", \"Electronics\"],\n});", "chunkId": "api/scans#hybrid-text-count" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/scans\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"mode\": \"count\",\n \"hybrid_text\": {\"field\": \"title\", \"query\": \"wireles headphones\"},\n \"filters\": [\"category\", \"Eq\", \"Electronics\"]\n }'", "chunkId": "api/scans#hybrid-text-count" }, { "kind": "code", "literal": "HybridText", "chunkId": "api/scans#hybrid-text-count" }, { "kind": "code", "literal": "hybrid_text", "chunkId": "api/scans#hybrid-text-count" }, { "kind": "code", "literal": "query", "chunkId": "api/scans#hybrid-text-count" }, { "kind": "code", "literal": "auto", "chunkId": "api/scans#hybrid-text-count" }, { "kind": "code", "literal": "fts", "chunkId": "api/scans#hybrid-text-count" } ], "sources": [ { "chunkId": "api/scans#hybrid-text-count", "url": "/docs/api/scans#hybrid-text-count", "anchor": "hybrid-text-count" } ], "mode": "source-primary", "terms": [ "hybrid", "text", "count", "selector", "counts", "deduped", "union", "bm25", "token", "fuzzy", "legs", "surfacing", "superset", "hybridtext", "query", "route", "only", "adds", "primary", "return", "nothing", "more", "rows", "returns", "generous", "live", "beside", "auto", "results", "plain", "exact", "await", "client", "create", "scan", "products", "mode", "field", "title", "wireles" ] }, { "id": "api/scans#id-mode", "kind": "section", "title": "Scan", "heading": "ID Mode", "group": "API", "url": "/docs/api/scans#id-mode", "summary": "ID mode (the default) creates an asynchronous 202 job with progress and effective-source fields; read IDs from the results route after completion with limit/offset pagination. Valid sources are auto, cache, and origin, and the Python and TypeScript clients ship helpers that create and poll to completion.", "facts": [ { "kind": "code", "literal": "job = await client.create_scan(\"products\", {\n \"source\": \"auto\",\n \"mode\": \"ids\",\n \"filters\": [\"category\", \"Eq\", \"Electronics\"],\n \"threads\": 8,\n \"page_size\": 1000,\n})", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "job, err := client.CreateScan(ctx, \"products\", &hevlayer.CreateScanRequest{\n Source: \"auto\",\n Mode: \"ids\",\n Filters: []interface{}{\"category\", \"Eq\", \"Electronics\"},\n Threads: 8,\n PageSize: 1000,\n})", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "const job = await client.createScan(\"products\", {\n source: \"auto\",\n mode: \"ids\",\n filters: [\"category\", \"Eq\", \"Electronics\"],\n threads: 8,\n page_size: 1000,\n});", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/scans\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"source\": \"auto\",\n \"mode\": \"ids\",\n \"filters\": [\"category\", \"Eq\", \"Electronics\"],\n \"threads\": 8,\n \"page_size\": 1000\n }'", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "{\n \"id\": \"scan-uuid\",\n \"namespace\": \"products\",\n \"source\": \"auto\",\n \"effective_source\": \"origin\",\n \"status\": \"running\",\n \"progress\": 0,\n \"documents_scanned\": 0,\n \"threads\": 8,\n \"created_at\": \"2026-05-26T10:00:00Z\"\n}", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "results = await client.get_scan_results(\"products\", job.id, limit=1000, offset=0)", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "results, err := client.GetScanResults(ctx, \"products\", scanID,\n &hevlayer.GetScanResultsParams{Limit: 1000, Offset: 0})", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "const results = await client.getScanResults(\"products\", job.id, {\n limit: 1000,\n offset: 0,\n});", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces/products/scans/scan-uuid/results?limit=1000&offset=0\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "{\n \"ids\": [\"doc-1\", \"doc-2\"],\n \"total\": 2\n}", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "mode", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "ids", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "auto", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "cache", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "origin", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "scan(...)", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "GetScan", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "status", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "completed", "chunkId": "api/scans#id-mode" }, { "kind": "code", "literal": "202 Accepted", "chunkId": "api/scans#id-mode" } ], "sources": [ { "chunkId": "api/scans#id-mode", "url": "/docs/api/scans#id-mode", "anchor": "id-mode" } ], "mode": "source-primary", "terms": [ "mode", "default", "creates", "asynchronous", "progress", "effective", "source", "fields", "read", "results", "route", "after", "completion", "limit", "offset", "pagination", "valid", "sources", "auto", "cache", "origin", "python", "typescript", "clients", "ship", "helpers", "create", "poll", "await", "client", "scan", "products", "filters", "category", "electronics", "threads", "page", "size", "1000", "createscan" ] }, { "id": "api/scans#operational-notes", "kind": "section", "title": "Scan", "heading": "Operational notes", "group": "API", "url": "/docs/api/scans#operational-notes", "summary": "ID and values scan state is in-memory and resets on gateway restart; count scans have a 30s default and 300s max deadline; values jobs cap at a million distinct values with a truncation flag; origin fan-out defaults to 8; snapshot-served counts are exact at the snapshot watermark.", "facts": [ { "kind": "code", "literal": "truncated: true", "chunkId": "api/scans#operational-notes" }, { "kind": "code", "literal": "Index.spec.scan.threads", "chunkId": "api/scans#operational-notes" }, { "kind": "code", "literal": "watermark_ms", "chunkId": "api/scans#operational-notes" } ], "sources": [ { "chunkId": "api/scans#operational-notes", "url": "/docs/api/scans#operational-notes", "anchor": "operational-notes" } ], "mode": "source-primary", "terms": [ "operational", "notes", "values", "scan", "state", "memory", "resets", "gateway", "restart", "count", "scans", "default", "300s", "deadline", "jobs", "million", "distinct", "truncation", "flag", "origin", "defaults", "snapshot", "served", "counts", "exact", "watermark", "truncated", "true", "index", "spec", "threads", "ephemeral", "maximum", "crossed", "listing", "keeps", "concurrent", "upstream", "requests", "unless" ] }, { "id": "api/scans#precomputed-serving", "kind": "section", "title": "Scan", "heading": "Precomputed serving", "group": "API", "url": "/docs/api/scans#precomputed-serving", "summary": "An unfiltered values scan on a snapshotted field is answered straight from the snapshot's facet histogram — the job completes during the create call with snapshot provenance. Skipped or absent fields and scans carrying any selector fall through under auto and fail 412 under explicit snapshot source.", "facts": [ { "kind": "code", "literal": "filters", "chunkId": "api/scans#precomputed-serving" }, { "kind": "code", "literal": "fields[]", "chunkId": "api/scans#precomputed-serving" }, { "kind": "code", "literal": "202", "chunkId": "api/scans#precomputed-serving" }, { "kind": "code", "literal": "status: completed", "chunkId": "api/scans#precomputed-serving" }, { "kind": "code", "literal": "effective_source: snapshot", "chunkId": "api/scans#precomputed-serving" }, { "kind": "code", "literal": "snapshot_sha", "chunkId": "api/scans#precomputed-serving" }, { "kind": "code", "literal": "watermark_ms", "chunkId": "api/scans#precomputed-serving" }, { "kind": "code", "literal": "fields_skipped[]", "chunkId": "api/scans#precomputed-serving" }, { "kind": "code", "literal": "auto", "chunkId": "api/scans#precomputed-serving" }, { "kind": "code", "literal": "412 precondition_failed", "chunkId": "api/scans#precomputed-serving" }, { "kind": "code", "literal": "source: snapshot", "chunkId": "api/scans#precomputed-serving" } ], "sources": [ { "chunkId": "api/scans#precomputed-serving", "url": "/docs/api/scans#precomputed-serving", "anchor": "precomputed-serving" } ], "mode": "source-primary", "terms": [ "precomputed", "serving", "unfiltered", "values", "scan", "snapshotted", "field", "answered", "straight", "snapshot", "facet", "histogram", "completes", "during", "create", "call", "provenance", "skipped", "absent", "fields", "scans", "carrying", "selector", "fall", "through", "under", "auto", "fail", "explicit", "source", "filters", "status", "completed", "effective", "watermark", "precondition", "failed", "ranked", "present", "latest" ] }, { "id": "api/scans#radius-count", "kind": "section", "title": "Scan", "heading": "Radius count", "group": "API", "url": "/docs/api/scans#radius-count", "summary": "The ann selector counts rows within a required finite radius of a query vector, always via origin scatter/gather; the gateway applies the radius bound to returned distances rather than pushing it upstream. The count is flagged approximate because ANN recall makes ball membership fuzzy, independent of shard saturation.", "facts": [ { "kind": "code", "literal": "count = await client.create_scan(\"products\", {\n \"mode\": \"count\",\n \"ann\": {\"field\": \"vector\", \"vector\": [0.12, -0.3, 0.88], \"radius\": 0.25},\n})", "chunkId": "api/scans#radius-count" }, { "kind": "code", "literal": "count, err := client.CreateScan(ctx, \"products\", &hevlayer.CreateScanRequest{\n Mode: \"count\",\n Ann: &hevlayer.AnnScan{Field: \"vector\", Vector: []float64{0.12, -0.3, 0.88}, Radius: 0.25},\n})", "chunkId": "api/scans#radius-count" }, { "kind": "code", "literal": "const count = await client.createScan(\"products\", {\n mode: \"count\",\n ann: { field: \"vector\", vector: [0.12, -0.3, 0.88], radius: 0.25 },\n});", "chunkId": "api/scans#radius-count" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/scans\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"mode\": \"count\",\n \"ann\": {\"field\": \"vector\", \"vector\": [0.12, -0.3, 0.88], \"radius\": 0.25}\n }'", "chunkId": "api/scans#radius-count" }, { "kind": "code", "literal": "{\n \"count\": 980,\n \"served_by\": \"origin\",\n \"approximate\": true,\n \"bounded\": false,\n \"timed_out\": false,\n \"shards_saturated\": 0,\n \"shards_total\": 1,\n \"threads\": 1,\n \"elapsed_ms\": 51\n}", "chunkId": "api/scans#radius-count" }, { "kind": "code", "literal": "radius", "chunkId": "api/scans#radius-count" }, { "kind": "code", "literal": "ann", "chunkId": "api/scans#radius-count" }, { "kind": "code", "literal": "field", "chunkId": "api/scans#radius-count" }, { "kind": "code", "literal": "vector", "chunkId": "api/scans#radius-count" }, { "kind": "code", "literal": "fts", "chunkId": "api/scans#radius-count" }, { "kind": "code", "literal": "approximate: true", "chunkId": "api/scans#radius-count" }, { "kind": "code", "literal": "$dist", "chunkId": "api/scans#radius-count" } ], "sources": [ { "chunkId": "api/scans#radius-count", "url": "/docs/api/scans#radius-count", "anchor": "radius-count" } ], "mode": "source-primary", "terms": [ "radius", "count", "selector", "counts", "rows", "within", "required", "finite", "query", "vector", "always", "origin", "scatter", "gather", "gateway", "applies", "bound", "returned", "distances", "rather", "pushing", "upstream", "flagged", "approximate", "because", "recall", "makes", "ball", "membership", "fuzzy", "independent", "shard", "saturation", "await", "client", "create", "scan", "products", "mode", "field" ] }, { "id": "api/scans#routes", "kind": "section", "title": "Scan", "heading": "Routes", "group": "API", "url": "/docs/api/scans#routes", "summary": "Scan routes under /v2/namespaces/{ns}/scans: POST creates an ID/values job or returns a count, GET lists jobs, per-id GET reads one job, a results route reads completed output, and DELETE drops the in-memory job.", "facts": [ { "kind": "code", "literal": "POST /v2/namespaces/{ns}/scans", "chunkId": "api/scans#routes" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/scans", "chunkId": "api/scans#routes" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/scans/{id}", "chunkId": "api/scans#routes" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/scans/{id}/results", "chunkId": "api/scans#routes" }, { "kind": "code", "literal": "DELETE /v2/namespaces/{ns}/scans/{id}", "chunkId": "api/scans#routes" } ], "sources": [ { "chunkId": "api/scans#routes", "url": "/docs/api/scans#routes", "anchor": "routes" } ], "mode": "source-primary", "terms": [ "routes", "scan", "under", "namespaces", "scans", "post", "creates", "values", "returns", "count", "lists", "jobs", "reads", "results", "route", "completed", "output", "delete", "drops", "memory", "method", "behavior", "create", "return", "list", "namespace", "read", "drop" ] }, { "id": "api/scans#sources", "kind": "section", "title": "Scan", "heading": "Sources", "group": "API", "url": "/docs/api/scans#sources", "summary": "Source-by-mode table for the filter selector: auto picks cache-when-fresh or snapshot-first depending on mode, snapshot requires eligibility, cache uses the Aerospike document cache only, and origin runs a paginated upstream scan. Ranked selectors (fts, hybridtext, ann) have no snapshot or cache evaluator — they always run origin, and explicit snapshot or cache sources return 422.", "facts": [ { "kind": "code", "literal": "auto", "chunkId": "api/scans#sources" }, { "kind": "code", "literal": "snapshot", "chunkId": "api/scans#sources" }, { "kind": "code", "literal": "Eq", "chunkId": "api/scans#sources" }, { "kind": "code", "literal": "In", "chunkId": "api/scans#sources" }, { "kind": "code", "literal": "fields[]", "chunkId": "api/scans#sources" }, { "kind": "code", "literal": "cache", "chunkId": "api/scans#sources" }, { "kind": "code", "literal": "origin", "chunkId": "api/scans#sources" }, { "kind": "code", "literal": "fts", "chunkId": "api/scans#sources" }, { "kind": "code", "literal": "hybrid_text", "chunkId": "api/scans#sources" }, { "kind": "code", "literal": "ann", "chunkId": "api/scans#sources" }, { "kind": "code", "literal": "422", "chunkId": "api/scans#sources" } ], "sources": [ { "chunkId": "api/scans#sources", "url": "/docs/api/scans#sources", "anchor": "sources" } ], "mode": "source-primary", "terms": [ "sources", "source", "mode", "table", "filter", "selector", "auto", "picks", "cache", "fresh", "snapshot", "first", "depending", "requires", "eligibility", "uses", "aerospike", "document", "only", "origin", "runs", "paginated", "upstream", "scan", "ranked", "selectors", "hybridtext", "evaluator", "always", "explicit", "return", "fields", "hybrid", "text", "count", "values", "enough", "otherwise", "eligible", "supported" ] }, { "id": "api/scans#values-mode", "kind": "section", "title": "Scan", "heading": "Values Mode", "group": "API", "url": "/docs/api/scans#values-mode", "summary": "A values scan enumerates the distinct values of one attribute field with per-value document counts — for discovering a field's value set rather than confirming known values. The field is required (scalar string/integer or string array; vectors rejected), the job is asynchronous like ID mode, and results use the same v/n vocabulary as snapshot histograms with deterministic ordering; ranked-scan saturation makes counts lower bounds.", "facts": [ { "kind": "code", "literal": "job = await client.create_scan(\"products\", {\n \"mode\": \"values\",\n \"field\": \"category\",\n \"source\": \"auto\",\n \"filters\": [\"in_stock\", \"Eq\", True],\n})", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "job, err := client.CreateScan(ctx, \"products\", &hevlayer.CreateScanRequest{\n Mode: \"values\",\n Field: \"category\",\n Source: \"auto\",\n Filters: []interface{}{\"in_stock\", \"Eq\", true},\n})", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "const job = await client.createScan(\"products\", {\n mode: \"values\",\n field: \"category\",\n source: \"auto\",\n filters: [\"in_stock\", \"Eq\", true],\n});", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/scans\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"mode\": \"values\",\n \"field\": \"category\",\n \"source\": \"auto\",\n \"filters\": [\"in_stock\", \"Eq\", true]\n }'", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "{\n \"id\": \"scan-uuid\",\n \"namespace\": \"products\",\n \"mode\": \"values\",\n \"field\": \"category\",\n \"source\": \"auto\",\n \"effective_source\": \"origin\",\n \"status\": \"running\",\n \"progress\": 0,\n \"documents_scanned\": 0,\n \"threads\": 8,\n \"created_at\": \"2026-05-26T10:00:00Z\"\n}", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "{\n \"values\": [\n {\"v\": \"electronics\", \"n\": 4210},\n {\"v\": \"books\", \"n\": 1240}\n ],\n \"total\": 2,\n \"truncated\": false\n}", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "field", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "mode: values", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "422", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "202 Accepted", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "scan(...)", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "status", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "completed", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "limit", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "offset", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": "bounded: true", "chunkId": "api/scans#values-mode" }, { "kind": "code", "literal": ">=", "chunkId": "api/scans#values-mode" } ], "sources": [ { "chunkId": "api/scans#values-mode", "url": "/docs/api/scans#values-mode", "anchor": "values-mode" } ], "mode": "source-primary", "terms": [ "values", "mode", "scan", "enumerates", "distinct", "attribute", "field", "value", "document", "counts", "discovering", "rather", "confirming", "known", "required", "scalar", "string", "integer", "array", "vectors", "rejected", "asynchronous", "like", "results", "same", "vocabulary", "snapshot", "histograms", "deterministic", "ordering", "ranked", "saturation", "makes", "lower", "bounds", "await", "client", "create", "products", "category" ] }, { "id": "api/search-history", "kind": "section", "title": "Query History", "heading": null, "group": "API", "url": "/docs/api/search-history", "summary": "Layer logs every query into a durable per-namespace JSONL trail in S3, mirrored into the hot cache for fast recent reads, with tagged fetch events landing in a sibling clickstream feed. Together they make a search session reconstructable for relevance tuning, A/B comparison, or incident review; both feeds are Layer-only.", "facts": [ { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/search-history" } ], "sources": [ { "chunkId": "api/search-history", "url": "/docs/api/search-history", "anchor": null } ], "mode": "source-primary", "terms": [ "layer", "logs", "every", "query", "durable", "namespace", "jsonl", "trail", "mirrored", "cache", "fast", "recent", "reads", "tagged", "fetch", "events", "landing", "sibling", "clickstream", "feed", "together", "make", "search", "session", "reconstructable", "relevance", "tuning", "comparison", "incident", "review", "both", "feeds", "only", "codetabs", "astro", "history", "backed", "gateway", "serves", "downstream" ] }, { "id": "api/search-history#clickstream-entry", "kind": "section", "title": "Query History", "heading": "Clickstream entry", "group": "API", "url": "/docs/api/search-history#clickstream-entry", "summary": "Clickstream entries record timestamp, trace id, namespace, document id, tags, source, and whether the fetch was served from cache; the trace id joins back to the producing search-history entry and is a supported query parameter for pulling a whole session's events.", "facts": [ { "kind": "code", "literal": "{\n \"events\": [\n {\n \"timestamp\": \"2026-05-22T08:00:02.143Z\",\n \"timestamp_nanos\": 1747900802143000000,\n \"trace_id\": \"f81d4fae-7dec-11d0-a765-00a0c91e6bf6\",\n \"namespace\": \"products\",\n \"doc_id\": \"asin-B08N5WRWNW\",\n \"tags\": [\"session:abc123\"],\n \"source\": \"fetch\",\n \"served_from\": \"cache\"\n }\n ],\n \"next_cursor\": \"1747900802142000000\"\n}", "chunkId": "api/search-history#clickstream-entry" }, { "kind": "code", "literal": "events = await client.list_clickstream(\n \"products\",\n trace_id=\"f81d4fae-7dec-11d0-a765-00a0c91e6bf6\",\n)", "chunkId": "api/search-history#clickstream-entry" }, { "kind": "code", "literal": "events, err := client.ListClickstream(ctx, \"products\",\n &hevlayer.ListClickstreamParams{\n TraceID: \"f81d4fae-7dec-11d0-a765-00a0c91e6bf6\",\n })", "chunkId": "api/search-history#clickstream-entry" }, { "kind": "code", "literal": "const events = await client.listClickstream(\"products\", {\n traceId: \"f81d4fae-7dec-11d0-a765-00a0c91e6bf6\",\n});", "chunkId": "api/search-history#clickstream-entry" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces/products/clickstream?trace_id=f81d4fae-7dec-11d0-a765-00a0c91e6bf6\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/search-history#clickstream-entry" }, { "kind": "code", "literal": "trace_id", "chunkId": "api/search-history#clickstream-entry" }, { "kind": "code", "literal": "served_from", "chunkId": "api/search-history#clickstream-entry" } ], "sources": [ { "chunkId": "api/search-history#clickstream-entry", "url": "/docs/api/search-history#clickstream-entry", "anchor": "clickstream-entry" } ], "mode": "source-primary", "terms": [ "clickstream", "entry", "entries", "record", "timestamp", "trace", "namespace", "document", "tags", "source", "whether", "fetch", "served", "cache", "joins", "back", "producing", "search", "history", "supported", "query", "parameter", "pulling", "whole", "session", "events", "2026", "22t08", "143z", "nanos", "1747900802143000000", "f81d4fae", "7dec", "11d0", "a765", "00a0c91e6bf6", "products", "asin", "b08n5wrwnw", "abc123" ] }, { "id": "api/search-history#query-parameters", "kind": "section", "title": "Query History", "heading": "Query parameters", "group": "API", "url": "/docs/api/search-history#query-parameters", "summary": "History list parameters: comma-separated tag filter with AND semantics, RFC3339 from/to bounds, a before cursor on the nanosecond timestamp, and a limit defaulting to 50 and capped at 500.", "facts": [ { "kind": "code", "literal": "tag", "chunkId": "api/search-history#query-parameters" }, { "kind": "code", "literal": "from", "chunkId": "api/search-history#query-parameters" }, { "kind": "code", "literal": "to", "chunkId": "api/search-history#query-parameters" }, { "kind": "code", "literal": "before", "chunkId": "api/search-history#query-parameters" }, { "kind": "code", "literal": "timestamp_nanos", "chunkId": "api/search-history#query-parameters" }, { "kind": "code", "literal": "limit", "chunkId": "api/search-history#query-parameters" } ], "sources": [ { "chunkId": "api/search-history#query-parameters", "url": "/docs/api/search-history#query-parameters", "anchor": "query-parameters" } ], "mode": "source-primary", "terms": [ "query", "parameters", "history", "list", "comma", "separated", "filter", "semantics", "rfc3339", "bounds", "before", "cursor", "nanosecond", "timestamp", "limit", "defaulting", "capped", "nanos", "param", "purpose", "every", "must", "match", "time", "pagination", "return", "entries", "strictly", "older", "given", "timestampnanos", "default" ] }, { "id": "api/search-history#routes", "kind": "section", "title": "Query History", "heading": "Routes", "group": "API", "url": "/docs/api/search-history#routes", "summary": "Two per-namespace GET routes — search-history (query log, newest first) and clickstream (correlated fetch events, newest first) — with identical /v1/ aliases kept for client compatibility.", "facts": [ { "kind": "code", "literal": "GET /v2/namespaces/{ns}/search-history", "chunkId": "api/search-history#routes" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/clickstream", "chunkId": "api/search-history#routes" }, { "kind": "code", "literal": "/v1/", "chunkId": "api/search-history#routes" } ], "sources": [ { "chunkId": "api/search-history#routes", "url": "/docs/api/search-history#routes", "anchor": "routes" } ], "mode": "source-primary", "terms": [ "routes", "namespace", "search", "history", "query", "newest", "first", "clickstream", "correlated", "fetch", "events", "identical", "aliases", "kept", "client", "compatibility", "namespaces", "route", "behavior", "versions", "both", "held" ] }, { "id": "api/search-history#search-history-entry", "kind": "section", "title": "Query History", "heading": "Search history entry", "group": "API", "url": "/docs/api/search-history#search-history-entry", "summary": "Each entry carries timestamps, a trace id joining to the clickstream, the caller-supplied raw query, the stable watermark used, a structured query summary, top result IDs in rank order, and caller tags. Hybrid queries log as one entry carrying the HybridText expression so replay reproduces the whole expansion, and routed queries log the routing decision for per-route engagement measurement and forced-route replay.", "facts": [ { "kind": "code", "literal": "timestamp", "chunkId": "api/search-history#search-history-entry" }, { "kind": "code", "literal": "timestamp_nanos", "chunkId": "api/search-history#search-history-entry" }, { "kind": "code", "literal": "trace_id", "chunkId": "api/search-history#search-history-entry" }, { "kind": "code", "literal": "raw_query", "chunkId": "api/search-history#search-history-entry" }, { "kind": "code", "literal": "x-hevlayer-search-query", "chunkId": "api/search-history#search-history-entry" }, { "kind": "code", "literal": "stable_as_of", "chunkId": "api/search-history#search-history-entry" }, { "kind": "code", "literal": "query", "chunkId": "api/search-history#search-history-entry" }, { "kind": "code", "literal": "top_result_ids", "chunkId": "api/search-history#search-history-entry" }, { "kind": "code", "literal": "tags", "chunkId": "api/search-history#search-history-entry" }, { "kind": "code", "literal": "HybridText", "chunkId": "api/search-history#search-history-entry" }, { "kind": "value", "literal": "e.g", "chunkId": "api/search-history#search-history-entry" } ], "sources": [ { "chunkId": "api/search-history#search-history-entry", "url": "/docs/api/search-history#search-history-entry", "anchor": "search-history-entry" } ], "mode": "source-primary", "terms": [ "search", "history", "entry", "carries", "timestamps", "trace", "joining", "clickstream", "caller", "supplied", "query", "stable", "watermark", "structured", "summary", "result", "rank", "order", "tags", "hybrid", "queries", "carrying", "hybridtext", "expression", "replay", "reproduces", "whole", "expansion", "routed", "routing", "decision", "route", "engagement", "measurement", "forced", "timestamp", "nanos", "hevlayer", "entries", "2026" ] }, { "id": "api/search-history#storage", "kind": "section", "title": "Query History", "heading": "Storage", "group": "API", "url": "/docs/api/search-history#storage", "summary": "History writes to a per-namespace, per-day JSONL key layout in S3, best-effort and never blocking the query response; Aerospike holds a recent window for fast reads, and a cache outage degrades latency but not durability since list calls walk the S3 prefix.", "facts": [ { "kind": "code", "literal": "search-history/{namespace}/{YYYY-MM-DD}/{timestamp_nanos}.jsonl", "chunkId": "api/search-history#storage" } ], "sources": [ { "chunkId": "api/search-history#storage", "url": "/docs/api/search-history#storage", "anchor": "storage" } ], "mode": "source-primary", "terms": [ "storage", "history", "writes", "namespace", "jsonl", "layout", "best", "effort", "never", "blocking", "query", "response", "aerospike", "holds", "recent", "window", "fast", "reads", "cache", "outage", "degrades", "latency", "durability", "since", "list", "calls", "walk", "prefix", "search", "yyyy", "timestamp", "nanos", "timestampnanos", "block", "durable", "store", "read", "merge", "inline" ] }, { "id": "api/search-history#tag-contract", "kind": "section", "title": "Query History", "heading": "Tag contract", "group": "API", "url": "/docs/api/search-history#tag-contract", "summary": "Tags are comma-split (commas can't be escaped), trimmed, deduped, and sorted before storing or matching, with limits of 32 unique tags per request, 128 bytes each, and a restricted ASCII character set. The list filter uses AND semantics — every named tag must match.", "facts": [ { "kind": "code", "literal": "x-hevlayer-tags", "chunkId": "api/search-history#tag-contract" }, { "kind": "code", "literal": "?tag=", "chunkId": "api/search-history#tag-contract" }, { "kind": "code", "literal": "?tag=a,b", "chunkId": "api/search-history#tag-contract" } ], "sources": [ { "chunkId": "api/search-history#tag-contract", "url": "/docs/api/search-history#tag-contract", "anchor": "tag-contract" } ], "mode": "source-primary", "terms": [ "contract", "tags", "comma", "split", "commas", "escaped", "trimmed", "deduped", "sorted", "before", "storing", "matching", "limits", "unique", "request", "bytes", "restricted", "ascii", "character", "list", "filter", "uses", "semantics", "every", "named", "must", "match", "hevlayer", "layer", "splits", "trims", "whitespace", "drops", "empty", "values", "sorts", "dedupes", "separators", "cannot", "limit" ] }, { "id": "api/search-history#writing-metadata", "kind": "section", "title": "Query History", "heading": "Writing metadata", "group": "API", "url": "/docs/api/search-history#writing-metadata", "summary": "Set a header for the human query text and another for comma-separated segmentation tags on query requests; the SDKs expose these as raw-query and tags options. Keep the query text in the raw-query field and use tags for segmentation, not for duplicating the query string.", "facts": [ { "kind": "code", "literal": "query = await client.query_namespace(\n \"products\",\n {\"vector\": embedding, \"top_k\": 10, \"include_attributes\": [\"title\"]},\n raw_query=\"wireless headphones\",\n tags=[\"app:hev-shop\", \"surface:storefront\", \"route:search\", \"page:first\"],\n)\n\nhistory = await client.list_search_history(\n \"products\",\n tags=[\"app:hev-shop\", \"route:search\", \"page:first\"],\n limit=20,\n)", "chunkId": "api/search-history#writing-metadata" }, { "kind": "code", "literal": "const query = await client.queryNamespace(\n \"products\",\n { vector: embedding, top_k: 10, include_attributes: [\"title\"] },\n {\n searchQuery: \"wireless headphones\",\n tags: [\"app:hev-shop\", \"surface:storefront\", \"route:search\", \"page:first\"],\n },\n);\n\nconst history = await client.listSearchHistory(\"products\", {\n tags: [\"app:hev-shop\", \"route:search\", \"page:first\"],\n limit: 20,\n});", "chunkId": "api/search-history#writing-metadata" }, { "kind": "code", "literal": "x-hevlayer-search-query", "chunkId": "api/search-history#writing-metadata" }, { "kind": "code", "literal": "x-hevlayer-tags", "chunkId": "api/search-history#writing-metadata" }, { "kind": "code", "literal": "raw_query", "chunkId": "api/search-history#writing-metadata" }, { "kind": "code", "literal": "tags", "chunkId": "api/search-history#writing-metadata" }, { "kind": "code", "literal": "WithSearchQuery", "chunkId": "api/search-history#writing-metadata" }, { "kind": "code", "literal": "WithSearchTags", "chunkId": "api/search-history#writing-metadata" } ], "sources": [ { "chunkId": "api/search-history#writing-metadata", "url": "/docs/api/search-history#writing-metadata", "anchor": "writing-metadata" } ], "mode": "source-primary", "terms": [ "writing", "metadata", "header", "human", "query", "text", "another", "comma", "separated", "segmentation", "tags", "requests", "sdks", "expose", "these", "options", "keep", "field", "duplicating", "string", "await", "client", "namespace", "products", "vector", "embedding", "include", "attributes", "title", "wireless", "headphones", "shop", "surface", "storefront", "route", "search", "page", "first", "history", "list" ] }, { "id": "api/snapshots", "kind": "section", "title": "Snapshot History", "heading": null, "group": "API", "url": "/docs/api/snapshots", "summary": "Snapshots are materialized facet histograms for a namespace — listings and counts stored durably in S3 with the latest body mirrored into Aerospike. POST materializes a field now; history and body routes read the durable chronology written by the consistency watcher.", "facts": [ { "kind": "code", "literal": "values[].v", "chunkId": "api/snapshots" }, { "kind": "code", "literal": "values[].n", "chunkId": "api/snapshots" }, { "kind": "code", "literal": "POST /snapshots", "chunkId": "api/snapshots" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/snapshots" } ], "sources": [ { "chunkId": "api/snapshots", "url": "/docs/api/snapshots", "anchor": null } ], "mode": "source-primary", "terms": [ "snapshots", "materialized", "facet", "histograms", "namespace", "listings", "counts", "stored", "durably", "latest", "body", "mirrored", "aerospike", "post", "materializes", "field", "history", "routes", "read", "durable", "chronology", "written", "consistency", "watcher", "values", "codetabs", "astro", "snapshot", "jobs", "bodies", "activity", "streams", "carry", "materialize" ] }, { "id": "api/snapshots#activity", "kind": "section", "title": "Snapshot History", "heading": "Activity", "group": "API", "url": "/docs/api/snapshots#activity", "summary": "The cross-namespace activity stream lists snapshot lifecycle events with a required epoch-ms since bound, optional namespace filter, cursor, and a limit capped at 500; search history and clickstream have separate feeds.", "facts": [ { "kind": "code", "literal": "activity = await client.list_snapshot_activity(since=1747200000000, limit=50)", "chunkId": "api/snapshots#activity" }, { "kind": "code", "literal": "activity, err := client.ListSnapshotActivity(ctx,\n &hevlayer.ListSnapshotActivityParams{Since: 1747200000000, Limit: 50})", "chunkId": "api/snapshots#activity" }, { "kind": "code", "literal": "const activity = await client.listSnapshotActivity({\n since: 1747200000000,\n limit: 50,\n});", "chunkId": "api/snapshots#activity" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/activity/snapshots?since=1747200000000&limit=50\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/snapshots#activity" }, { "kind": "code", "literal": "since", "chunkId": "api/snapshots#activity" }, { "kind": "code", "literal": "ts_ms", "chunkId": "api/snapshots#activity" }, { "kind": "code", "literal": "limit", "chunkId": "api/snapshots#activity" }, { "kind": "code", "literal": "namespace", "chunkId": "api/snapshots#activity" }, { "kind": "code", "literal": "cursor", "chunkId": "api/snapshots#activity" }, { "kind": "code", "literal": "next_cursor", "chunkId": "api/snapshots#activity" } ], "sources": [ { "chunkId": "api/snapshots#activity", "url": "/docs/api/snapshots#activity", "anchor": "activity" } ], "mode": "source-primary", "terms": [ "activity", "cross", "namespace", "stream", "lists", "snapshot", "lifecycle", "events", "required", "epoch", "since", "bound", "optional", "filter", "cursor", "limit", "capped", "search", "history", "clickstream", "separate", "feeds", "await", "client", "list", "1747200000000", "listsnapshotactivity", "hevlayer", "listsnapshotactivityparams", "const", "curl", "layer", "gateway", "snapshots", "authorization", "bearer", "next", "layergatewayurl", "layergatewayapikey", "query" ] }, { "id": "api/snapshots#history", "kind": "section", "title": "Snapshot History", "heading": "History", "group": "API", "url": "/docs/api/snapshots#history", "summary": "Namespace history lists snapshot entries (watermark, sha, tags) newest first with a limit and a before-SHA cursor accepting 7-character prefixes. It lists S3 keys only without reading bodies, and tags are operator metadata outside the content hash.", "facts": [ { "kind": "code", "literal": "history = await client.list_namespace_history(\"products\", limit=20)", "chunkId": "api/snapshots#history" }, { "kind": "code", "literal": "history, err := client.ListNamespaceHistory(ctx, \"products\",\n &hevlayer.ListNamespaceHistoryParams{Limit: 20})", "chunkId": "api/snapshots#history" }, { "kind": "code", "literal": "const history = await client.listNamespaceHistory(\"products\", { limit: 20 });", "chunkId": "api/snapshots#history" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces/products/history?limit=20\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/snapshots#history" }, { "kind": "code", "literal": "[\n {\"watermark_ms\": 1747300000123, \"sha\": \"3f9e8b21...\", \"tags\": [\"pre-migration\"]},\n {\"watermark_ms\": 1747299600045, \"sha\": \"a1c5b09f...\", \"tags\": []}\n]", "chunkId": "api/snapshots#history" }, { "kind": "code", "literal": "limit", "chunkId": "api/snapshots#history" }, { "kind": "code", "literal": "before", "chunkId": "api/snapshots#history" }, { "kind": "code", "literal": "tags", "chunkId": "api/snapshots#history" } ], "sources": [ { "chunkId": "api/snapshots#history", "url": "/docs/api/snapshots#history", "anchor": "history" } ], "mode": "source-primary", "terms": [ "history", "namespace", "lists", "snapshot", "entries", "watermark", "tags", "newest", "first", "limit", "before", "cursor", "accepting", "character", "prefixes", "keys", "only", "without", "reading", "bodies", "operator", "metadata", "outside", "content", "hash", "await", "client", "list", "products", "listnamespacehistory", "hevlayer", "listnamespacehistoryparams", "const", "curl", "layer", "gateway", "namespaces", "authorization", "bearer", "1747300000123" ] }, { "id": "api/snapshots#manual-snapshot", "kind": "section", "title": "Snapshot History", "heading": "Manual snapshot", "group": "API", "url": "/docs/api/snapshots#manual-snapshot", "summary": "An on-demand snapshot job materializes one field from a chosen source — auto (stored when possible), stored (latest S3 body, fastest for configured facet fields), cache (supports evaluable filters), or origin (authoritative, persists to S3). The 202 job is polled to completion, which includes the sha when a body was materialized.", "facts": [ { "kind": "code", "literal": "job = await client.create_snapshot(\"products\", {\n \"field\": \"category\",\n \"source\": \"auto\",\n \"filters\": [\"brand\", \"Eq\", \"Acme\"],\n \"page_size\": 1000,\n})", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "job, err := client.CreateSnapshot(ctx, \"products\", &hevlayer.CreateSnapshotRequest{\n Field: \"category\",\n Source: \"auto\",\n Filters: []interface{}{\"brand\", \"Eq\", \"Acme\"},\n PageSize: 1000,\n})", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "const job = await client.createSnapshot(\"products\", {\n field: \"category\",\n source: \"auto\",\n filters: [\"brand\", \"Eq\", \"Acme\"],\n page_size: 1000,\n});", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/snapshots\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"field\": \"category\",\n \"source\": \"auto\",\n \"filters\": [\"brand\", \"Eq\", \"Acme\"],\n \"page_size\": 1000\n }'", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "{\n \"id\": \"snapshot-job-uuid\",\n \"namespace\": \"products\",\n \"field\": \"category\",\n \"source\": \"auto\",\n \"status\": \"running\",\n \"progress\": 0,\n \"documents_scanned\": 0,\n \"created_at\": \"2026-05-26T10:00:00Z\"\n}", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "job = await client.get_snapshot_job(\"products\", job.id)", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "job, err := client.GetSnapshotJob(ctx, \"products\", jobID)", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "const job = await client.getSnapshotJob(\"products\", jobId);", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces/products/snapshot-jobs/snapshot-job-uuid\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "{\n \"id\": \"snapshot-job-uuid\",\n \"namespace\": \"products\",\n \"field\": \"category\",\n \"source\": \"origin\",\n \"status\": \"completed\",\n \"documents_scanned\": 12844,\n \"sha\": \"3f9e8b21\",\n \"stable_as_of\": 1747300000123\n}", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "auto", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "stored", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "cache", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "origin", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "202 Accepted", "chunkId": "api/snapshots#manual-snapshot" }, { "kind": "code", "literal": "sha", "chunkId": "api/snapshots#manual-snapshot" } ], "sources": [ { "chunkId": "api/snapshots#manual-snapshot", "url": "/docs/api/snapshots#manual-snapshot", "anchor": "manual-snapshot" } ], "mode": "source-primary", "terms": [ "manual", "snapshot", "demand", "materializes", "field", "chosen", "source", "auto", "stored", "possible", "latest", "body", "fastest", "configured", "facet", "fields", "cache", "supports", "evaluable", "filters", "origin", "authoritative", "persists", "polled", "completion", "includes", "materialized", "await", "client", "create", "products", "category", "brand", "acme", "page", "size", "1000", "createsnapshot", "hevlayer", "createsnapshotrequest" ] }, { "id": "api/snapshots#routes", "kind": "section", "title": "Snapshot History", "heading": "Routes", "group": "API", "url": "/docs/api/snapshots#routes", "summary": "Snapshot routes: create an on-demand job, read and set the API-managed snapshot policy, list and read in-memory jobs, list durable history, read a full body by SHA or 7-char prefix, and a cross-namespace activity stream; checkpoints label the newest durable watermark without a scan.", "facts": [ { "kind": "code", "literal": "POST /v2/namespaces/{ns}/snapshots", "chunkId": "api/snapshots#routes" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/snapshot-policy", "chunkId": "api/snapshots#routes" }, { "kind": "code", "literal": "PUT /v2/namespaces/{ns}/snapshot-policy", "chunkId": "api/snapshots#routes" }, { "kind": "code", "literal": "facetFields", "chunkId": "api/snapshots#routes" }, { "kind": "code", "literal": "interval", "chunkId": "api/snapshots#routes" }, { "kind": "code", "literal": "retention", "chunkId": "api/snapshots#routes" }, { "kind": "code", "literal": "Index.spec.snapshot", "chunkId": "api/snapshots#routes" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/snapshot-jobs", "chunkId": "api/snapshots#routes" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/snapshot-jobs/{id}", "chunkId": "api/snapshots#routes" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/history", "chunkId": "api/snapshots#routes" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/snapshots/{sha}", "chunkId": "api/snapshots#routes" }, { "kind": "code", "literal": "GET /v2/activity/snapshots", "chunkId": "api/snapshots#routes" } ], "sources": [ { "chunkId": "api/snapshots#routes", "url": "/docs/api/snapshots#routes", "anchor": "routes" } ], "mode": "source-primary", "terms": [ "routes", "snapshot", "create", "demand", "read", "managed", "policy", "list", "memory", "jobs", "durable", "history", "full", "body", "char", "prefix", "cross", "namespace", "activity", "stream", "checkpoints", "label", "newest", "watermark", "without", "scan", "post", "namespaces", "snapshots", "facetfields", "interval", "retention", "index", "spec", "route", "method", "behavior", "field", "shape", "first" ] }, { "id": "api/snapshots#snapshot-body", "kind": "section", "title": "Snapshot History", "heading": "Snapshot body", "group": "API", "url": "/docs/api/snapshots#snapshot-body", "summary": "A snapshot body carries the namespace, watermark, sha, scanned row count, and per-field value/count histograms; fields over the 10,000 distinct-value cap land in a skipped list with the reason rather than being partially materialized, and remain enumerable via a values scan with its larger cap. The row count also feeds namespace metadata's indexed reconciliation.", "facts": [ { "kind": "code", "literal": "body = await client.get_namespace_snapshot(\"products\", \"3f9e8b2\")", "chunkId": "api/snapshots#snapshot-body" }, { "kind": "code", "literal": "body, err := client.GetNamespaceSnapshot(ctx, \"products\", \"3f9e8b2\")", "chunkId": "api/snapshots#snapshot-body" }, { "kind": "code", "literal": "const body = await client.getNamespaceSnapshot(\"products\", \"3f9e8b2\");", "chunkId": "api/snapshots#snapshot-body" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces/products/snapshots/3f9e8b2\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/snapshots#snapshot-body" }, { "kind": "code", "literal": "fields[].values[].v", "chunkId": "api/snapshots#snapshot-body" }, { "kind": "code", "literal": "fields[].values[].n", "chunkId": "api/snapshots#snapshot-body" }, { "kind": "code", "literal": "row_count", "chunkId": "api/snapshots#snapshot-body" }, { "kind": "code", "literal": "indexed", "chunkId": "api/snapshots#snapshot-body" }, { "kind": "code", "literal": "index_lag_rows", "chunkId": "api/snapshots#snapshot-body" }, { "kind": "code", "literal": "fields[]", "chunkId": "api/snapshots#snapshot-body" }, { "kind": "code", "literal": "fields_skipped[]", "chunkId": "api/snapshots#snapshot-body" } ], "sources": [ { "chunkId": "api/snapshots#snapshot-body", "url": "/docs/api/snapshots#snapshot-body", "anchor": "snapshot-body" } ], "mode": "source-primary", "terms": [ "snapshot", "body", "carries", "namespace", "watermark", "scanned", "count", "field", "value", "histograms", "fields", "distinct", "land", "skipped", "list", "reason", "rather", "being", "partially", "materialized", "remain", "enumerable", "values", "scan", "larger", "also", "feeds", "metadata", "indexed", "reconciliation", "await", "client", "products", "3f9e8b2", "getnamespacesnapshot", "const", "curl", "layer", "gateway", "namespaces" ] }, { "id": "api/snapshots#snapshot-policy", "kind": "section", "title": "Snapshot History", "heading": "Snapshot policy", "group": "API", "url": "/docs/api/snapshots#snapshot-policy", "summary": "Automatic snapshot writes are configured with the Index snapshot shape — facet fields (empty disables the writer), a minimum interval flooring write spacing, and retention pruning old S3 bodies while keeping the newest. Snapshots are event-driven on stable advances rather than scheduled, policy edits take effect without a restart, and API-only namespaces can set the same shape via a gateway-side policy route.", "facts": [ { "kind": "code", "literal": "apiVersion: hevlayer.com/v1\nkind: Index\nmetadata:\n name: products\nspec:\n backend:\n namespace: products\n snapshot:\n interval: 5m\n retention: 30d\n facetFields:\n - category\n - brand", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "curl -X PUT \"$LAYER_GATEWAY_URL/v2/namespaces/products/snapshot-policy\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"facetFields\": [\"category\", \"brand\"],\n \"interval\": \"5m\",\n \"retention\": \"30d\"\n }'", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "Index.spec.snapshot", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "Index", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "facetFields", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "[]", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "interval", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "5m", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "LAYER_SNAPSHOT_MIN_INTERVAL_MS", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "retention", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "never", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "30d", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "GET /v2/namespaces/{ns}/snapshot-policy", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "POST /snapshots", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "source: origin", "chunkId": "api/snapshots#snapshot-policy" }, { "kind": "code", "literal": "spec.scan.threads", "chunkId": "api/snapshots#snapshot-policy" } ], "sources": [ { "chunkId": "api/snapshots#snapshot-policy", "url": "/docs/api/snapshots#snapshot-policy", "anchor": "snapshot-policy" } ], "mode": "source-primary", "terms": [ "snapshot", "policy", "automatic", "writes", "configured", "index", "shape", "facet", "fields", "empty", "disables", "writer", "minimum", "interval", "flooring", "write", "spacing", "retention", "pruning", "bodies", "while", "keeping", "newest", "snapshots", "event", "driven", "stable", "advances", "rather", "scheduled", "edits", "take", "effect", "without", "restart", "only", "namespaces", "same", "gateway", "side" ] }, { "id": "api/warm-cache", "kind": "section", "title": "Warm cache", "heading": null, "group": "API", "url": "/docs/api/warm-cache", "summary": "Two warm endpoints: the turbopuffer-compatible hint (which additionally runs gateway-side warm steps) and the Layer-only warm shortcut that creates a gateway warm job.", "facts": [ { "kind": "code", "literal": "hint_cache_warm", "chunkId": "api/warm-cache" }, { "kind": "code", "literal": "warm", "chunkId": "api/warm-cache" }, { "kind": "code", "literal": "GET /v1/namespaces/{ns}/hint_cache_warm", "chunkId": "api/warm-cache" }, { "kind": "value", "literal": "Upstream.astro", "chunkId": "api/warm-cache" }, { "kind": "value", "literal": "Callout.astro", "chunkId": "api/warm-cache" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/warm-cache" }, { "kind": "value", "literal": "turbopuffer.com", "chunkId": "api/warm-cache" } ], "sources": [ { "chunkId": "api/warm-cache", "url": "/docs/api/warm-cache", "anchor": null } ], "mode": "source-primary", "terms": [ "warm", "endpoints", "turbopuffer", "compatible", "hint", "additionally", "runs", "gateway", "side", "steps", "layer", "only", "shortcut", "creates", "cache", "namespaces", "upstream", "astro", "callout", "codetabs", "namespace", "document", "snapshot", "mirror", "exposes", "hintcachewarm", "matches", "call", "advises", "index", "load" ] }, { "id": "api/warm-cache#cache-cold-behavior", "kind": "section", "title": "Warm cache", "heading": "Cache-cold behavior", "group": "API", "url": "/docs/api/warm-cache#cache-cold-behavior", "summary": "When the document cache is unavailable, warm jobs, cache scans, cache snapshot jobs, and pipeline chunk reads return 503 cache-cold, while fetch falls through to upstream with a miss-on-error marker — fetch is correctness-first, warm is throughput-first. A bare hint passthrough never touches the gateway cache, so it succeeds even cold.", "facts": [ { "kind": "code", "literal": "cache_cold", "chunkId": "api/warm-cache#cache-cold-behavior" }, { "kind": "code", "literal": "x-layer-cache: miss-on-error", "chunkId": "api/warm-cache#cache-cold-behavior" }, { "kind": "code", "literal": "hint_cache_warm", "chunkId": "api/warm-cache#cache-cold-behavior" }, { "kind": "code", "literal": "documents", "chunkId": "api/warm-cache#cache-cold-behavior" }, { "kind": "code", "literal": "snapshots", "chunkId": "api/warm-cache#cache-cold-behavior" } ], "sources": [ { "chunkId": "api/warm-cache#cache-cold-behavior", "url": "/docs/api/warm-cache#cache-cold-behavior", "anchor": "cache-cold-behavior" } ], "mode": "source-primary", "terms": [ "cache", "cold", "behavior", "document", "unavailable", "warm", "jobs", "scans", "snapshot", "pipeline", "chunk", "reads", "return", "while", "fetch", "falls", "through", "upstream", "miss", "error", "marker", "correctness", "first", "throughput", "bare", "hint", "passthrough", "never", "touches", "gateway", "succeeds", "even", "layer", "documents", "snapshots", "cachecold", "many", "fall", "turbopuffer", "instead" ] }, { "id": "api/warm-cache#hint-cache-warm", "kind": "section", "title": "Warm cache", "heading": "Hint-cache warm", "group": "API", "url": "/docs/api/warm-cache#hint-cache-warm", "summary": "With no query parameters the hint is a raw upstream passthrough; supplying any warm option switches to Layer orchestration with three independently toggleable steps — forward the hint upstream, start an origin document-cache backfill job, and mirror the latest snapshot into cache — reported per-step in the response. The generated Go client can't turn steps off (it omits false params), so disable steps over REST or Python.", "facts": [ { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v1/namespaces/products/hint_cache_warm\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "result = await client.hint_cache_warm(\n \"products\",\n turbopuffer=False,\n documents=False,\n snapshots=True,\n)", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "const result = await client.hintCacheWarm(\"products\", {\n turbopuffer: false,\n documents: false,\n snapshots: true,\n});", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v1/namespaces/products/hint_cache_warm?turbopuffer=false&documents=false&snapshots=true\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "{\n \"namespace\": \"products\",\n \"turbopuffer\": { \"enabled\": true, \"status\": \"completed\" },\n \"documents\": {\n \"enabled\": true,\n \"status\": \"started\",\n \"job\": { \"id\": \"warm-job-uuid\", \"status\": \"running\" }\n },\n \"snapshots\": {\n \"enabled\": true,\n \"status\": \"completed\",\n \"key\": \"snapshots/products/...\",\n \"watermark_ms\": 1715600400000,\n \"sha\": \"...\"\n }\n}", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "turbopuffer", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "documents", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "snapshots", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "page_size", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "turbopuffer=true", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "documents=true", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "snapshots=true", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "false", "chunkId": "api/warm-cache#hint-cache-warm" }, { "kind": "code", "literal": "/warm-jobs/{id}", "chunkId": "api/warm-cache#hint-cache-warm" } ], "sources": [ { "chunkId": "api/warm-cache#hint-cache-warm", "url": "/docs/api/warm-cache#hint-cache-warm", "anchor": "hint-cache-warm" } ], "mode": "source-primary", "terms": [ "hint", "cache", "warm", "query", "parameters", "upstream", "passthrough", "supplying", "option", "switches", "layer", "orchestration", "three", "independently", "toggleable", "steps", "forward", "start", "origin", "document", "backfill", "mirror", "latest", "snapshot", "reported", "step", "response", "generated", "client", "turn", "omits", "false", "params", "disable", "rest", "python", "curl", "gateway", "namespaces", "products" ] }, { "id": "api/warm-cache#layer-warm", "kind": "section", "title": "Warm cache", "heading": "Layer warm", "group": "API", "url": "/docs/api/warm-cache#layer-warm", "summary": "The Layer warm route creates an asynchronous 202 job that pages through the upstream store, backfills Aerospike, and refreshes the warmed-through watermark — the bootstrap path for a namespace whose data was written outside the gateway. Poll the job through the warm-jobs route.", "facts": [ { "kind": "code", "literal": "job = await client.warm_cache(\"products\", page_size=1000)", "chunkId": "api/warm-cache#layer-warm" }, { "kind": "code", "literal": "job, err := client.WarmCache(ctx, \"products\", &hevlayer.WarmCacheParams{\n PageSize: 1000,\n})", "chunkId": "api/warm-cache#layer-warm" }, { "kind": "code", "literal": "const job = await client.warmCache(\"products\", { pageSize: 1000 });", "chunkId": "api/warm-cache#layer-warm" }, { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/products/warm?page_size=1000\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/warm-cache#layer-warm" }, { "kind": "code", "literal": "{\n \"id\": \"warm-job-uuid\",\n \"namespace\": \"products\",\n \"status\": \"running\",\n \"progress\": 0,\n \"documents_scanned\": 0,\n \"created_at\": \"2026-05-26T10:00:00Z\"\n}", "chunkId": "api/warm-cache#layer-warm" }, { "kind": "code", "literal": "job = await client.get_warm_job(\"products\", job.id)", "chunkId": "api/warm-cache#layer-warm" }, { "kind": "code", "literal": "job, err := client.GetWarmJob(ctx, \"products\", jobID)", "chunkId": "api/warm-cache#layer-warm" }, { "kind": "code", "literal": "const job = await client.getWarmJob(\"products\", jobId);", "chunkId": "api/warm-cache#layer-warm" }, { "kind": "code", "literal": "curl \"$LAYER_GATEWAY_URL/v2/namespaces/products/warm-jobs/warm-job-uuid\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\"", "chunkId": "api/warm-cache#layer-warm" }, { "kind": "code", "literal": "POST /v2/namespaces/{ns}/warm", "chunkId": "api/warm-cache#layer-warm" }, { "kind": "code", "literal": "cache_warmed_through", "chunkId": "api/warm-cache#layer-warm" }, { "kind": "code", "literal": "202 Accepted", "chunkId": "api/warm-cache#layer-warm" } ], "sources": [ { "chunkId": "api/warm-cache#layer-warm", "url": "/docs/api/warm-cache#layer-warm", "anchor": "layer-warm" } ], "mode": "source-primary", "terms": [ "layer", "warm", "route", "creates", "asynchronous", "pages", "through", "upstream", "store", "backfills", "aerospike", "refreshes", "warmed", "watermark", "bootstrap", "path", "namespace", "whose", "data", "written", "outside", "gateway", "poll", "jobs", "await", "client", "cache", "products", "page", "size", "1000", "warmcache", "hevlayer", "warmcacheparams", "pagesize", "const", "curl", "post", "namespaces", "authorization" ] }, { "id": "api/write", "kind": "section", "title": "Write & Stage", "heading": null, "group": "API", "url": "/docs/api/write", "summary": "Layer's write API is wire-compatible with the upstream write endpoint (upserts, deletes, patches, and filter writes in one request), forwarded as-is after validation. Layer stamps every row-producing write with its upserted-at watermark — which holds the read watermark — and mirrors the write to the document cache.", "facts": [ { "kind": "code", "literal": "POST /v2/namespaces/{ns}", "chunkId": "api/write" }, { "kind": "code", "literal": "write_namespace", "chunkId": "api/write" }, { "kind": "code", "literal": "_hevlayer_upserted_at", "chunkId": "api/write" }, { "kind": "value", "literal": "StoreSwitch.astro", "chunkId": "api/write" }, { "kind": "value", "literal": "StoreNote.astro", "chunkId": "api/write" }, { "kind": "value", "literal": "Upstream.astro", "chunkId": "api/write" }, { "kind": "value", "literal": "FeatureGate.astro", "chunkId": "api/write" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "api/write" }, { "kind": "value", "literal": "turbopuffer.com", "chunkId": "api/write" } ], "sources": [ { "chunkId": "api/write", "url": "/docs/api/write", "anchor": null } ], "mode": "source-primary", "terms": [ "layer", "write", "wire", "compatible", "upstream", "endpoint", "upserts", "deletes", "patches", "filter", "writes", "request", "forwarded", "after", "validation", "stamps", "every", "producing", "upserted", "watermark", "holds", "read", "mirrors", "document", "cache", "post", "namespaces", "namespace", "hevlayer", "storeswitch", "astro", "storenote", "featuregate", "codetabs", "turbopuffer", "rows", "stage", "documents", "native", "body" ] }, { "id": "api/write#stage", "kind": "section", "title": "Write & Stage", "heading": "Stage", "group": "API", "url": "/docs/api/write#stage", "summary": "Staging caches a document's chunks before upsert — the O(1) queue between a two-stage pipeline's CPU and GPU workers — storing them in the document cache and marking the document pending; re-staging replaces chunks and resets state. Staged documents are ephemeral until upserted: a cache outage loses anything still staged.", "facts": [ { "kind": "code", "literal": "await client.put_pipeline_document_chunks(\"product-images\", \"asin-B08N5WRWNW\", {\n \"chunks\": [\n {\"id\": \"asin-B08N5WRWNW-0\", \"text\": \"Wireless noise-cancelling headphones\"},\n {\"id\": \"asin-B08N5WRWNW-1\", \"text\": \"40-hour battery life\", \"metadata\": {\"page\": 2}},\n ],\n})", "chunkId": "api/write#stage" }, { "kind": "code", "literal": "client.PutPipelineDocumentChunks(ctx, \"product-images\", \"asin-B08N5WRWNW\", &hevlayer.PutChunksRequest{\n Chunks: []hevlayer.Chunk{\n {ID: \"asin-B08N5WRWNW-0\", Text: \"Wireless noise-cancelling headphones\"},\n {ID: \"asin-B08N5WRWNW-1\", Text: \"40-hour battery life\", Metadata: map[string]interface{}{\"page\": 2}},\n },\n})", "chunkId": "api/write#stage" }, { "kind": "code", "literal": "await client.putPipelineDocumentChunks(\"product-images\", \"asin-B08N5WRWNW\", {\n chunks: [\n { id: \"asin-B08N5WRWNW-0\", text: \"Wireless noise-cancelling headphones\" },\n { id: \"asin-B08N5WRWNW-1\", text: \"40-hour battery life\", metadata: { page: 2 } },\n ],\n});", "chunkId": "api/write#stage" }, { "kind": "code", "literal": "curl -X PUT \"$LAYER_GATEWAY_URL/v2/pipelines/product-images/documents/asin-B08N5WRWNW\" \\\n -H \"Authorization: Bearer $LAYER_GATEWAY_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"chunks\": [\n {\"id\": \"asin-B08N5WRWNW-0\", \"text\": \"Wireless noise-cancelling headphones\"},\n {\"id\": \"asin-B08N5WRWNW-1\", \"text\": \"40-hour battery life\", \"metadata\": {\"page\": 2}}\n ]\n }'", "chunkId": "api/write#stage" }, { "kind": "code", "literal": "pending", "chunkId": "api/write#stage" } ], "sources": [ { "chunkId": "api/write#stage", "url": "/docs/api/write#stage", "anchor": "stage" } ], "mode": "source-primary", "terms": [ "stage", "staging", "caches", "document", "chunks", "before", "upsert", "queue", "between", "pipeline", "workers", "storing", "cache", "marking", "pending", "replaces", "resets", "state", "staged", "documents", "ephemeral", "until", "upserted", "outage", "loses", "anything", "still", "await", "client", "product", "images", "asin", "b08n5wrwnw", "text", "wireless", "noise", "cancelling", "headphones", "hour", "battery" ] }, { "id": "api/write#status", "kind": "section", "title": "Write & Stage", "heading": "Status", "group": "API", "url": "/docs/api/write#status", "summary": "The write path's statuses: 200 applied and stamped, 422 when Layer rejects the body before forwarding (no recognized operation, reserved attribute, removed custom key, or a schema type the store can't represent — a Layer error body), verbatim passthrough of any upstream non-2xx including failed conditional writes, and 502 when the backing store is unreachable.", "facts": [ { "kind": "code", "literal": "_hevlayer_*", "chunkId": "api/write#status" }, { "kind": "code", "literal": "{ \"error\": \"validation_error\", … }", "chunkId": "api/write#status" }, { "kind": "code", "literal": "{ \"error\": \"UnsupportedByStore\", … }", "chunkId": "api/write#status" }, { "kind": "code", "literal": "{ \"error\": \"upstream_error\", … }", "chunkId": "api/write#status" }, { "kind": "code", "literal": "upsert_condition", "chunkId": "api/write#status" }, { "kind": "code", "literal": "patch_condition", "chunkId": "api/write#status" }, { "kind": "code", "literal": "delete_condition", "chunkId": "api/write#status" }, { "kind": "value", "literal": "non-2xx", "chunkId": "api/write#status" } ], "sources": [ { "chunkId": "api/write#status", "url": "/docs/api/write#status", "anchor": "status" } ], "mode": "source-primary", "terms": [ "status", "write", "path", "statuses", "applied", "stamped", "layer", "rejects", "body", "before", "forwarding", "recognized", "operation", "reserved", "attribute", "removed", "custom", "schema", "type", "store", "represent", "error", "verbatim", "passthrough", "upstream", "including", "failed", "conditional", "writes", "backing", "unreachable", "hevlayer", "validation", "unsupportedbystore", "upsert", "condition", "patch", "delete", "validates", "fail" ] }, { "id": "cli", "kind": "section", "title": "Layer CLI", "heading": null, "group": "Operations", "url": "/docs/cli", "summary": "The layer CLI operates hevlayer from the terminal: named environments, namespace shard initialization, read-only observation of indexes, pipelines, UDFs, and data-supply resources, key minting, and Function manifest runs. Every read goes through the gateway API with an API key; only run touches Kubernetes.", "facts": [ { "kind": "code", "literal": "layer", "chunkId": "cli" }, { "kind": "code", "literal": "install", "chunkId": "cli" }, { "kind": "code", "literal": "run", "chunkId": "cli" }, { "kind": "code", "literal": "--kube-context", "chunkId": "cli" }, { "kind": "code", "literal": "--kube-namespace", "chunkId": "cli" }, { "kind": "code", "literal": "--context", "chunkId": "cli" } ], "sources": [ { "chunkId": "cli", "url": "/docs/cli", "anchor": null } ], "mode": "agent-primary", "terms": [ "layer", "operates", "hevlayer", "terminal", "named", "environments", "namespace", "shard", "initialization", "read", "only", "observation", "indexes", "pipelines", "udfs", "data", "supply", "resources", "minting", "function", "manifest", "runs", "every", "goes", "through", "gateway", "touches", "kubernetes", "install", "kube", "context", "manages", "initializes", "sharding", "observes", "mints", "keys", "manifests", "adopted", "namespaces" ] }, { "id": "cli#ask-the-docs", "kind": "section", "title": "Layer CLI", "heading": "Ask The Docs", "group": "Operations", "url": "/docs/cli#ask-the-docs", "summary": "layer ask queries the committed docs digest with the ask CLI — keyless and local by default, finding the checkout's digest and falling back to installed packages or a PATH binary; an endpoint flag targets a deployed hev ask endpoint instead. Verbs include tree, grep, cat, and glossary get.", "facts": [ { "kind": "code", "literal": "layer ask tree\nlayer ask grep \"warm cache\"\nlayer ask cat api/query\nlayer ask glossary get watermark\nlayer -o json ask tree", "chunkId": "cli#ask-the-docs" }, { "kind": "code", "literal": "layer ask --endpoint https://hevlayer.com/api/ask tree", "chunkId": "cli#ask-the-docs" }, { "kind": "code", "literal": "layer ask", "chunkId": "cli#ask-the-docs" }, { "kind": "code", "literal": "ask", "chunkId": "cli#ask-the-docs" }, { "kind": "code", "literal": "site/.hev-ask", "chunkId": "cli#ask-the-docs" }, { "kind": "code", "literal": "../ask", "chunkId": "cli#ask-the-docs" }, { "kind": "code", "literal": "@hevmind/ask", "chunkId": "cli#ask-the-docs" }, { "kind": "code", "literal": "PATH", "chunkId": "cli#ask-the-docs" }, { "kind": "code", "literal": "--endpoint", "chunkId": "cli#ask-the-docs" } ], "sources": [ { "chunkId": "cli#ask-the-docs", "url": "/docs/cli#ask-the-docs", "anchor": "ask-the-docs" } ], "mode": "agent-primary", "terms": [ "docs", "layer", "queries", "committed", "digest", "keyless", "local", "default", "finding", "checkout", "falling", "back", "installed", "packages", "path", "binary", "endpoint", "flag", "targets", "deployed", "instead", "verbs", "include", "tree", "grep", "glossary", "warm", "cache", "query", "watermark", "json", "https", "hevlayer", "site", "hevmind", "finds", "prefers", "sibling", "source", "falls" ] }, { "id": "cli#configuration", "kind": "section", "title": "Layer CLI", "heading": "Configuration", "group": "Operations", "url": "/docs/cli#configuration", "summary": "The CLI reads named environments from a 0600-mode config file, resolving configuration in priority order: explicit flags, LAYER/HEVLAYER env vars, an environment selected per invocation, the active config environment, then the built-in default base URL. Output formats are table, json, and names.", "facts": [ { "kind": "code", "literal": "active = \"partner\"\n\n[envs.partner]\nbase_url = \"https://aws-us-east-1.hevlayer.com\"\napi_key = \"...\"\nkube_context = \"partner-cluster\"\nkube_namespace = \"hevlayer\"\n\n[envs.local]\nbase_url = \"http://localhost:8080\"\napi_key = \"dev\"\nkube_context = \"kind-hevlayer\"", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "layer", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "~/.hevlayer/config.toml", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "0700", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "0600", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "--base-url", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "--api-key", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "--context", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "--kube-namespace", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "LAYER_BASE_URL", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "LAYER_API_KEY", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "HEVLAYER_", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "--env", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "LAYER_ENV", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "HEVLAYER_BASE_URL", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "https://aws-us-east-1.hevlayer.com", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "HEVLAYER_API_KEY", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "-o", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "--output", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "table", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "json", "chunkId": "cli#configuration" }, { "kind": "code", "literal": "names", "chunkId": "cli#configuration" } ], "sources": [ { "chunkId": "cli#configuration", "url": "/docs/cli#configuration", "anchor": "configuration" } ], "mode": "agent-primary", "terms": [ "configuration", "reads", "named", "environments", "0600", "mode", "config", "file", "resolving", "priority", "order", "explicit", "flags", "layer", "hevlayer", "vars", "environment", "selected", "invocation", "active", "built", "default", "base", "output", "formats", "table", "json", "names", "partner", "envs", "https", "east", "kube", "context", "cluster", "namespace", "local", "http", "localhost", "8080" ] }, { "id": "cli#delete-an-index", "kind": "section", "title": "Layer CLI", "heading": "Delete An Index", "group": "Operations", "url": "/docs/cli#delete-an-index", "summary": "index delete purges the upstream namespace plus every Layer-side trace — cache rows, snapshot mirrors, S3 snapshots/history/clickstream/shard metadata, in-memory jobs, and the discovered Index CR where GC is enabled. It takes names or a mutually-exclusive prefix form that lists matches and confirms; a yes flag skips the prompt and is required off-TTY.", "facts": [ { "kind": "code", "literal": "layer index delete shop-products\nlayer index delete shop-products shop-products-staging\nlayer index delete --prefix shop-\nlayer index delete --prefix shop- --yes", "chunkId": "cli#delete-an-index" }, { "kind": "code", "literal": "index delete", "chunkId": "cli#delete-an-index" }, { "kind": "code", "literal": "--prefix", "chunkId": "cli#delete-an-index" }, { "kind": "code", "literal": "--yes", "chunkId": "cli#delete-an-index" } ], "sources": [ { "chunkId": "cli#delete-an-index", "url": "/docs/cli#delete-an-index", "anchor": "delete-an-index" } ], "mode": "agent-primary", "terms": [ "delete", "index", "purges", "upstream", "namespace", "plus", "every", "layer", "side", "trace", "cache", "rows", "snapshot", "mirrors", "snapshots", "history", "clickstream", "shard", "metadata", "memory", "jobs", "discovered", "enabled", "takes", "names", "mutually", "exclusive", "prefix", "form", "lists", "matches", "confirms", "flag", "skips", "prompt", "required", "shop", "products", "staging", "turbopuffer" ] }, { "id": "cli#environments", "kind": "section", "title": "Layer CLI", "heading": "Environments", "group": "Operations", "url": "/docs/cli#environments", "summary": "Environment subcommands add, select, list, show, and remove named environments; add prompts for missing values on a TTY and requires flags otherwise, and API keys are masked in listings.", "facts": [ { "kind": "code", "literal": "layer env add partner --base-url https://aws-us-east-1.hevlayer.com \\\n --api-key \"$LAYER_API_KEY\" --kube-context partner-cluster \\\n --kube-namespace hevlayer\nlayer env use partner\nlayer env ls\nlayer env show partner -o json\nlayer env rm partner", "chunkId": "cli#environments" }, { "kind": "code", "literal": "env add", "chunkId": "cli#environments" }, { "kind": "code", "literal": "env ls", "chunkId": "cli#environments" }, { "kind": "code", "literal": "env show", "chunkId": "cli#environments" } ], "sources": [ { "chunkId": "cli#environments", "url": "/docs/cli#environments", "anchor": "environments" } ], "mode": "agent-primary", "terms": [ "environments", "environment", "subcommands", "select", "list", "show", "remove", "named", "prompts", "missing", "values", "requires", "flags", "otherwise", "keys", "masked", "listings", "layer", "partner", "base", "https", "east", "hevlayer", "kube", "context", "cluster", "namespace", "json", "layerapikey", "required", "must", "supplied" ] }, { "id": "cli#initialize-a-namespace", "kind": "section", "title": "Layer CLI", "heading": "Initialize a Namespace", "group": "Operations", "url": "/docs/cli#initialize-a-namespace", "summary": "init calls the namespace init route to create or reattach the shard marker, start the shard backfill, and watch by default until shard lag reaches zero and scatter/gather activates. Re-running with the same shard count is idempotent; a different count returns a conflict instead of changing the marker.", "facts": [ { "kind": "code", "literal": "layer init products --shards 8\nlayer init products --shards 8 --watch=false\nlayer init products --shards 8 --poll-interval 5s", "chunkId": "cli#initialize-a-namespace" }, { "kind": "code", "literal": "init", "chunkId": "cli#initialize-a-namespace" }, { "kind": "code", "literal": "POST /v2/namespaces/{namespace}/init", "chunkId": "cli#initialize-a-namespace" }, { "kind": "code", "literal": "shard_lag_rows", "chunkId": "cli#initialize-a-namespace" } ], "sources": [ { "chunkId": "cli#initialize-a-namespace", "url": "/docs/cli#initialize-a-namespace", "anchor": "initialize-a-namespace" } ], "mode": "agent-primary", "terms": [ "initialize", "namespace", "init", "calls", "route", "create", "reattach", "shard", "marker", "start", "backfill", "watch", "default", "until", "reaches", "zero", "scatter", "gather", "activates", "running", "same", "count", "idempotent", "different", "returns", "conflict", "instead", "changing", "layer", "products", "shards", "false", "poll", "interval", "post", "namespaces", "rows", "through", "selected", "gateway" ] }, { "id": "cli#inspect-an-index", "kind": "section", "title": "Layer CLI", "heading": "Inspect An Index", "group": "Operations", "url": "/docs/cli#inspect-an-index", "summary": "index get reports row count, size, schema summary, last write, stable watermark and lag, and WAL/cache state on any gateway; the recent-snapshots table needs a managed/pro gateway serving snapshot history. Values are raw epoch-ms and bytes, with JSON output carrying the full snapshot list.", "facts": [ { "kind": "code", "literal": "layer index get shop-products\nlayer index get shop-products -o json", "chunkId": "cli#inspect-an-index" }, { "kind": "code", "literal": "index get", "chunkId": "cli#inspect-an-index" }, { "kind": "code", "literal": "index list", "chunkId": "cli#inspect-an-index" }, { "kind": "code", "literal": "-o json", "chunkId": "cli#inspect-an-index" } ], "sources": [ { "chunkId": "cli#inspect-an-index", "url": "/docs/cli#inspect-an-index", "anchor": "inspect-an-index" } ], "mode": "agent-primary", "terms": [ "inspect", "index", "reports", "count", "size", "schema", "summary", "last", "write", "stable", "watermark", "cache", "state", "gateway", "recent", "snapshots", "table", "needs", "managed", "serving", "snapshot", "history", "values", "epoch", "bytes", "json", "output", "carrying", "full", "list", "layer", "shop", "products", "column", "additionally", "need", "served", "only", "timestamps", "sizes" ] }, { "id": "cli#install", "kind": "section", "title": "Layer CLI", "heading": "Install", "group": "Operations", "url": "/docs/cli#install", "summary": "Build the CLI from the repository root with go build against the layer-cli app path.", "facts": [ { "kind": "code", "literal": "go build -o layer ./apps/layer-cli", "chunkId": "cli#install" } ], "sources": [ { "chunkId": "cli#install", "url": "/docs/cli#install", "anchor": "install" } ], "mode": "agent-primary", "terms": [ "install", "build", "repository", "root", "against", "layer", "path", "apps" ] }, { "id": "cli#install-an-aws-environment", "kind": "section", "title": "Layer CLI", "heading": "Install An AWS Environment", "group": "Operations", "url": "/docs/cli#install-an-aws-environment", "summary": "layer install provisions a full AWS environment — Terraform (VPC, EKS, IAM/IRSA, S3, ECR) then the Helm release — in one shot, with a demo profile (default, lean) and an indexing profile adding the document-cache node pool. It runs from a hev/layer source checkout, prompts for missing inputs on a TTY, prints the resolved plan before provisioning, and has status and uninstall companions.", "facts": [ { "kind": "code", "literal": "git clone https://github.com/hev/layer\ncd layer\ngo build -o layer ./apps/layer-cli\n./layer install", "chunkId": "cli#install-an-aws-environment" }, { "kind": "code", "literal": "layer install", "chunkId": "cli#install-an-aws-environment" }, { "kind": "code", "literal": "--profile demo", "chunkId": "cli#install-an-aws-environment" }, { "kind": "code", "literal": "adds the dedicated document-cache node pool.", "chunkId": "cli#install-an-aws-environment" }, { "kind": "code", "literal": "reports release and workload health, and", "chunkId": "cli#install-an-aws-environment" }, { "kind": "code", "literal": "hev/layer", "chunkId": "cli#install-an-aws-environment" }, { "kind": "flag", "literal": "--profile", "chunkId": "cli#install-an-aws-environment" } ], "sources": [ { "chunkId": "cli#install-an-aws-environment", "url": "/docs/cli#install-an-aws-environment", "anchor": "install-an-aws-environment" } ], "mode": "agent-primary", "terms": [ "install", "environment", "layer", "provisions", "full", "terraform", "irsa", "helm", "release", "shot", "demo", "profile", "default", "lean", "indexing", "adding", "document", "cache", "node", "pool", "runs", "source", "checkout", "prompts", "missing", "inputs", "prints", "resolved", "plan", "before", "provisioning", "status", "uninstall", "companions", "clone", "https", "github", "build", "apps", "adds" ] }, { "id": "cli#keys", "kind": "section", "title": "Layer CLI", "heading": "Keys", "group": "Operations", "url": "/docs/cli#keys", "summary": "keys mint creates a key through the gateway and prints the token once alone on stdout (metadata to stderr) with flags for entitlements, namespace globs, claims, and expiry; a manifest file form takes the same ApiKey YAML kubectl does. Listings show metadata only, revoke is the idempotent default retirement, and rm hard-deletes only already-revoked keys; all key commands need admin scope and no kube access.", "facts": [ { "kind": "code", "literal": "layer keys mint cohort-reader --owner acme \\\n --entitle vectorstore.prod-turbopuffer=read \\\n --namespaces \"cohort-*\" \\\n --claim warehouse.prod-snowflake=\"notes:cohort:*:read\"\nlayer keys ls\nlayer keys get cohort-reader\nlayer keys revoke cohort-reader\nlayer keys rm cohort-reader", "chunkId": "cli#keys" }, { "kind": "code", "literal": "keys mint", "chunkId": "cli#keys" }, { "kind": "code", "literal": "layer keys mint … | pbcopy", "chunkId": "cli#keys" }, { "kind": "code", "literal": "keys revoke", "chunkId": "cli#keys" }, { "kind": "code", "literal": "keys rm", "chunkId": "cli#keys" }, { "kind": "code", "literal": "Revoked", "chunkId": "cli#keys" }, { "kind": "code", "literal": "--entitle", "chunkId": "cli#keys" }, { "kind": "code", "literal": "TARGET[=SCOPE[+SCOPE]]", "chunkId": "cli#keys" }, { "kind": "code", "literal": "vectorstore.", "chunkId": "cli#keys" }, { "kind": "code", "literal": "warehouse.", "chunkId": "cli#keys" }, { "kind": "code", "literal": "layer", "chunkId": "cli#keys" }, { "kind": "code", "literal": "--namespaces", "chunkId": "cli#keys" }, { "kind": "code", "literal": "--claim", "chunkId": "cli#keys" }, { "kind": "code", "literal": "TARGET=STRING", "chunkId": "cli#keys" }, { "kind": "code", "literal": "--expires-after", "chunkId": "cli#keys" }, { "kind": "code", "literal": "never", "chunkId": "cli#keys" }, { "kind": "code", "literal": "365d", "chunkId": "cli#keys" }, { "kind": "code", "literal": "--entitle layer=admin", "chunkId": "cli#keys" }, { "kind": "code", "literal": "layer keys mint -f key.yaml", "chunkId": "cli#keys" }, { "kind": "code", "literal": "ApiKey", "chunkId": "cli#keys" }, { "kind": "code", "literal": "kubectl apply", "chunkId": "cli#keys" }, { "kind": "code", "literal": "keys ls", "chunkId": "cli#keys" }, { "kind": "code", "literal": "keys get", "chunkId": "cli#keys" }, { "kind": "code", "literal": "revoke", "chunkId": "cli#keys" } ], "sources": [ { "chunkId": "cli#keys", "url": "/docs/cli#keys", "anchor": "keys" } ], "mode": "agent-primary", "terms": [ "keys", "mint", "creates", "through", "gateway", "prints", "token", "once", "alone", "stdout", "metadata", "stderr", "flags", "entitlements", "namespace", "globs", "claims", "expiry", "manifest", "file", "form", "takes", "same", "apikey", "yaml", "kubectl", "does", "listings", "show", "only", "revoke", "idempotent", "default", "retirement", "hard", "deletes", "already", "revoked", "commands", "need" ] }, { "id": "cli#manage-snapshots", "kind": "section", "title": "Layer CLI", "heading": "Manage Snapshots", "group": "Operations", "url": "/docs/cli#manage-snapshots", "summary": "index snapshot creates an on-demand snapshot job and waits for completion; index policy writes the same facet-fields/interval/retention shape as the Index snapshot spec so shared-gateway namespaces can enable the automatic writer without a Kubernetes resource. Both work against the free self-hosted gateway with no operator or license.", "facts": [ { "kind": "code", "literal": "layer index snapshot shop-products --field category\nlayer index policy shop-products --facet-field category --facet-field brand --interval 5m --retention 30d", "chunkId": "cli#manage-snapshots" }, { "kind": "code", "literal": "index snapshot", "chunkId": "cli#manage-snapshots" }, { "kind": "code", "literal": "index policy", "chunkId": "cli#manage-snapshots" }, { "kind": "code", "literal": "facetFields", "chunkId": "cli#manage-snapshots" }, { "kind": "code", "literal": "interval", "chunkId": "cli#manage-snapshots" }, { "kind": "code", "literal": "retention", "chunkId": "cli#manage-snapshots" }, { "kind": "code", "literal": "Index.spec.snapshot", "chunkId": "cli#manage-snapshots" } ], "sources": [ { "chunkId": "cli#manage-snapshots", "url": "/docs/cli#manage-snapshots", "anchor": "manage-snapshots" } ], "mode": "agent-primary", "terms": [ "manage", "snapshots", "index", "snapshot", "creates", "demand", "waits", "completion", "policy", "writes", "same", "facet", "fields", "interval", "retention", "shape", "spec", "shared", "gateway", "namespaces", "enable", "automatic", "writer", "without", "kubernetes", "resource", "both", "work", "against", "free", "self", "hosted", "operator", "license", "layer", "shop", "products", "field", "category", "brand" ] }, { "id": "cli#pipelines", "kind": "section", "title": "Layer CLI", "heading": "Pipelines", "group": "Operations", "url": "/docs/cli#pipelines", "summary": "pipeline list reads registered pipelines and fans out to the status API for live queue depth; pipeline get adds target namespace, distance metric, and creation time. Pipelines with no staged worker render without queue counts rather than erroring; reads need only an API key, and push is deferred to a later milestone.", "facts": [ { "kind": "code", "literal": "layer pipeline list\nlayer pipeline get product-images", "chunkId": "cli#pipelines" }, { "kind": "code", "literal": "pipeline list", "chunkId": "cli#pipelines" }, { "kind": "code", "literal": "pending", "chunkId": "cli#pipelines" }, { "kind": "code", "literal": "processing", "chunkId": "cli#pipelines" }, { "kind": "code", "literal": "failed", "chunkId": "cli#pipelines" }, { "kind": "code", "literal": "rate/min", "chunkId": "cli#pipelines" }, { "kind": "code", "literal": "layer push", "chunkId": "cli#pipelines" } ], "sources": [ { "chunkId": "cli#pipelines", "url": "/docs/cli#pipelines", "anchor": "pipelines" } ], "mode": "agent-primary", "terms": [ "pipelines", "pipeline", "list", "reads", "registered", "fans", "status", "live", "queue", "depth", "adds", "target", "namespace", "distance", "metric", "creation", "time", "staged", "worker", "render", "without", "counts", "rather", "erroring", "need", "only", "push", "deferred", "later", "milestone", "layer", "product", "images", "pending", "processing", "failed", "rate", "created", "renders", "kube" ] }, { "id": "cli#run-a-function", "kind": "section", "title": "Layer CLI", "heading": "Run A Function", "group": "Operations", "url": "/docs/cli#run-a-function", "summary": "layer run applies a Kubernetes Function manifest, registers the UDF spec with the gateway, triggers discovery, and by default polls until the queue drains (failures exit non-zero); flags override the target index, kube context and namespace, skip the apply, detach, or clean up registration and CR after a clean drain. Bump the manifest's version before re-running after changing a model, prompt, taxonomy, or write contract; watch runs from another terminal with the udf list/get commands.", "facts": [ { "kind": "code", "literal": "layer run -f tag-products.yaml\nlayer run -f tag-products.yaml --index amazon-products-staging\nlayer run -f tag-products.yaml --detach\nlayer run -f tag-products.yaml --rm", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "layer udf list\nlayer udf get product-tags --watch", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "Function", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "--index", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "spec.targetNamespaces", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "--context", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "--kube-namespace", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "--no-apply", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "spec.version", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "--detach", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "pending_count", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "processing_count", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "--rm", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "udf list", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "udf get", "chunkId": "cli#run-a-function" }, { "kind": "code", "literal": "--watch", "chunkId": "cli#run-a-function" } ], "sources": [ { "chunkId": "cli#run-a-function", "url": "/docs/cli#run-a-function", "anchor": "run-a-function" } ], "mode": "agent-primary", "terms": [ "function", "layer", "applies", "kubernetes", "manifest", "registers", "spec", "gateway", "triggers", "discovery", "default", "polls", "until", "queue", "drains", "failures", "exit", "zero", "flags", "override", "target", "index", "kube", "context", "namespace", "skip", "apply", "detach", "clean", "registration", "after", "drain", "bump", "version", "before", "running", "changing", "model", "prompt", "taxonomy" ] }, { "id": "cli#tui", "kind": "section", "title": "Layer CLI", "heading": "TUI", "group": "Operations", "url": "/docs/cli#tui", "summary": "Bare layer on a TTY opens a read-only operations TUI with single-key switching between indexes, functions, pipelines, keys, and environments; every view has a non-interactive command twin emitting raw values for scripting. Keys views are read-only — minting and revoking stay in the commands.", "facts": [ { "kind": "code", "literal": "layer", "chunkId": "cli#tui" }, { "kind": "code", "literal": "layer browse", "chunkId": "cli#tui" }, { "kind": "code", "literal": "enter", "chunkId": "cli#tui" }, { "kind": "code", "literal": "esc", "chunkId": "cli#tui" }, { "kind": "code", "literal": "layer env ls", "chunkId": "cli#tui" }, { "kind": "code", "literal": "layer udf list", "chunkId": "cli#tui" }, { "kind": "code", "literal": "layer udf get UDF_ID [--watch]", "chunkId": "cli#tui" }, { "kind": "code", "literal": "layer index list", "chunkId": "cli#tui" }, { "kind": "code", "literal": "layer index get NAME", "chunkId": "cli#tui" }, { "kind": "code", "literal": "layer pipeline list", "chunkId": "cli#tui" }, { "kind": "code", "literal": "layer pipeline get ID", "chunkId": "cli#tui" }, { "kind": "code", "literal": "layer keys ls", "chunkId": "cli#tui" }, { "kind": "code", "literal": "layer keys get KEY_ID", "chunkId": "cli#tui" } ], "sources": [ { "chunkId": "cli#tui", "url": "/docs/cli#tui", "anchor": "tui" } ], "mode": "agent-primary", "terms": [ "bare", "layer", "opens", "read", "only", "operations", "single", "switching", "between", "indexes", "functions", "pipelines", "keys", "environments", "every", "view", "interactive", "command", "twin", "emitting", "values", "scripting", "views", "minting", "revoking", "stay", "commands", "browse", "enter", "list", "watch", "index", "name", "pipeline", "explicit", "spelling", "prints", "usage", "exits", "press" ] }, { "id": "cli#vector-store-and-warehouse", "kind": "section", "title": "Layer CLI", "heading": "Vector Store And Warehouse", "group": "Operations", "url": "/docs/cli#vector-store-and-warehouse", "summary": "vectorstore and warehouse list/get commands show declared stores (kind, default, region, reachability, org id, credential Secret reference, auth mode) and warehouses (source identity, verification status, failure reason, dependents). All are read-only, gateway-backed, and never read or print Secret contents; a nameless vectorstore get selects the default or only store.", "facts": [ { "kind": "code", "literal": "layer vectorstore list\nlayer vectorstore get\nlayer vectorstore get prod-turbopuffer -o json\nlayer warehouse list\nlayer warehouse get prod-snowflake", "chunkId": "cli#vector-store-and-warehouse" }, { "kind": "code", "literal": "vectorstore list", "chunkId": "cli#vector-store-and-warehouse" }, { "kind": "code", "literal": "vectorstore get", "chunkId": "cli#vector-store-and-warehouse" }, { "kind": "code", "literal": "spec.turbopuffer.orgId", "chunkId": "cli#vector-store-and-warehouse" }, { "kind": "code", "literal": "warehouse list", "chunkId": "cli#vector-store-and-warehouse" }, { "kind": "code", "literal": "warehouse get NAME", "chunkId": "cli#vector-store-and-warehouse" } ], "sources": [ { "chunkId": "cli#vector-store-and-warehouse", "url": "/docs/cli#vector-store-and-warehouse", "anchor": "vector-store-and-warehouse" } ], "mode": "agent-primary", "terms": [ "vector", "store", "warehouse", "vectorstore", "list", "commands", "show", "declared", "stores", "kind", "default", "region", "reachability", "credential", "secret", "reference", "auth", "mode", "warehouses", "source", "identity", "verification", "status", "failure", "reason", "dependents", "read", "only", "gateway", "backed", "never", "print", "contents", "nameless", "selects", "layer", "prod", "turbopuffer", "json", "snowflake" ] }, { "id": "concepts", "kind": "section", "title": "Concepts", "heading": null, "group": "Overview", "url": "/docs/concepts", "summary": "Overview of how the gateway composes turbopuffer, the document cache, PostgreSQL, S3, and metrics, and the core nouns used throughout the docs.", "facts": [ { "kind": "value", "literal": "Callout.astro", "chunkId": "concepts" } ], "sources": [ { "chunkId": "concepts", "url": "/docs/concepts", "anchor": null } ], "mode": "agent-primary", "terms": [ "overview", "gateway", "composes", "turbopuffer", "document", "cache", "postgresql", "metrics", "core", "nouns", "throughout", "docs", "callout", "astro", "work" ] }, { "id": "concepts#control-loops", "kind": "section", "title": "Concepts", "heading": "Control loops", "group": "Overview", "url": "/docs/concepts#control-loops", "summary": "Layer uses control loops as a core primitive: reconciling index state against metrics emitted by the search system is how it applies row-level UDF transformations and keeps an index's stable view current.", "facts": [], "sources": [ { "chunkId": "concepts#control-loops", "url": "/docs/concepts#control-loops", "anchor": "control-loops" } ], "mode": "agent-primary", "terms": [ "control", "loops", "layer", "uses", "core", "primitive", "reconciling", "index", "state", "against", "metrics", "emitted", "search", "system", "applies", "level", "transformations", "keeps", "stable", "view", "current", "loop", "managing", "indexes", "reconciles", "udfs", "related", "snapshots", "watermark" ] }, { "id": "concepts#document-cache", "kind": "section", "title": "Concepts", "heading": "Document cache", "group": "Overview", "url": "/docs/concepts#document-cache", "summary": "The document cache does two jobs — pull-through document reads (check cache, read through to origin on miss, backfill best-effort) and pipeline chunk handoff between CPU and GPU workers — and neither makes it a hard dependency, since reads fall through to origin and chunks fall back to S3. One logical cache serves every path with dedicated cache sets per use.", "facts": [], "sources": [ { "chunkId": "concepts#document-cache", "url": "/docs/concepts#document-cache", "anchor": "document-cache" } ], "mode": "agent-primary", "terms": [ "document", "cache", "does", "jobs", "pull", "through", "reads", "check", "read", "origin", "miss", "backfill", "best", "effort", "pipeline", "chunk", "handoff", "between", "workers", "neither", "makes", "hard", "dependency", "since", "fall", "chunks", "back", "logical", "serves", "every", "path", "dedicated", "sets", "layer", "served", "gateway", "checks", "first", "turbopuffer", "snapshots" ] }, { "id": "concepts#gateway-enhancements", "kind": "section", "title": "Concepts", "heading": "Gateway enhancements", "group": "Overview", "url": "/docs/concepts#gateway-enhancements", "summary": "The gateway extends the search system with common query patterns and filtering primitives using reserved hevlayer attributes, exposed through one API surface across the three clients and REST. Layer works best when all traffic flows through it consistently, even calls needing no extra behavior.", "facts": [ { "kind": "code", "literal": "_hevlayer_*", "chunkId": "concepts#gateway-enhancements" } ], "sources": [ { "chunkId": "concepts#gateway-enhancements", "url": "/docs/concepts#gateway-enhancements", "anchor": "gateway-enhancements" } ], "mode": "agent-primary", "terms": [ "gateway", "enhancements", "extends", "search", "system", "common", "query", "patterns", "filtering", "primitives", "reserved", "hevlayer", "attributes", "exposed", "through", "surface", "across", "three", "clients", "rest", "layer", "works", "best", "traffic", "flows", "consistently", "even", "calls", "needing", "extra", "behavior", "helpful", "changing", "schema", "those", "breaks", "guarantees", "should", "degrade", "gracefully" ] }, { "id": "concepts#glossary", "kind": "section", "title": "Concepts", "heading": "Glossary", "group": "Overview", "url": "/docs/concepts#glossary", "summary": "The product glossary defining namespace, document, document cache, stable watermark, ready signal, pipeline, snapshot, facet listing and count, scan, UDF, gateway, operator, shard, leg, RRF, tokenizer policy, route, routing policy, deferral, CRD, and PromQL — the canonical one-line meanings for every core noun in the docs.", "facts": [ { "kind": "code", "literal": "/v2/namespaces/{namespace}", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "row_count", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "indexed", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "index_lag_rows", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "fields[].values[].v", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "fields[].values[].n", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "values[].n", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "fts", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "ann", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "_hevlayer_shard", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "rerank_by: [\"RRF\", ...]", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "HybridText", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "alyze", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "word_v4", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "Auto", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "hybrid_text", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "semantic", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "fused", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "routing", "chunkId": "concepts#glossary" }, { "kind": "code", "literal": "executed: false", "chunkId": "concepts#glossary" } ], "sources": [ { "chunkId": "concepts#glossary", "url": "/docs/concepts#glossary", "anchor": "glossary" } ], "mode": "agent-primary", "terms": [ "glossary", "product", "defining", "namespace", "document", "cache", "stable", "watermark", "ready", "signal", "pipeline", "snapshot", "facet", "listing", "count", "scan", "gateway", "operator", "shard", "tokenizer", "policy", "route", "routing", "deferral", "promql", "canonical", "line", "meanings", "every", "core", "noun", "docs", "namespaces", "indexed", "index", "rows", "fields", "values", "hevlayer", "rerank" ] }, { "id": "concepts#kubernetes-autoscaling", "kind": "section", "title": "Concepts", "heading": "Kubernetes autoscaling", "group": "Overview", "url": "/docs/concepts#kubernetes-autoscaling", "summary": "Because Layer is stateless, every tier autoscales independently: Karpenter handles node-level scaling and KEDA scales pods against signals from an embedded PostgreSQL queue whose data is used for scaling decisions only.", "facts": [], "sources": [ { "chunkId": "concepts#kubernetes-autoscaling", "url": "/docs/concepts#kubernetes-autoscaling", "anchor": "kubernetes-autoscaling" } ], "mode": "agent-primary", "terms": [ "kubernetes", "autoscaling", "because", "layer", "stateless", "every", "tier", "autoscales", "independently", "karpenter", "handles", "node", "level", "scaling", "keda", "scales", "pods", "against", "signals", "embedded", "postgresql", "queue", "whose", "data", "decisions", "only", "autoscale", "carries", "recoverable", "system", "state" ] }, { "id": "concepts#scattergather", "kind": "section", "title": "Concepts", "heading": "Scatter/gather", "group": "Overview", "url": "/docs/concepts#scattergather", "summary": "Layer can partition one namespace into hash-bucket shards via a reserved per-row shard attribute, scattering a query to every bucket in parallel and gathering/re-ranking to the requested topk — invisible to the client. Adopted namespaces initialize sharding through the init route with a backfill; scatter/gather activates only once shard lag reaches zero so unstamped rows aren't missed.", "facts": [ { "kind": "code", "literal": "_hevlayer_shard", "chunkId": "concepts#scattergather" }, { "kind": "code", "literal": "top_k", "chunkId": "concepts#scattergather" }, { "kind": "code", "literal": "POST /v2/namespaces/{namespace}/init", "chunkId": "concepts#scattergather" }, { "kind": "code", "literal": "shard_count", "chunkId": "concepts#scattergather" }, { "kind": "code", "literal": "layer.shard_lag_rows: 0", "chunkId": "concepts#scattergather" }, { "kind": "code", "literal": "layer init --shards N", "chunkId": "concepts#scattergather" }, { "kind": "code", "literal": "shard_lag_rows", "chunkId": "concepts#scattergather" }, { "kind": "flag", "literal": "-filtered", "chunkId": "concepts#scattergather" } ], "sources": [ { "chunkId": "concepts#scattergather", "url": "/docs/concepts#scattergather", "anchor": "scattergather" } ], "mode": "agent-primary", "terms": [ "scatter", "gather", "layer", "partition", "namespace", "hash", "bucket", "shards", "reserved", "shard", "attribute", "scattering", "query", "every", "parallel", "gathering", "ranking", "requested", "topk", "invisible", "client", "adopted", "namespaces", "initialize", "sharding", "through", "init", "route", "backfill", "activates", "only", "once", "reaches", "zero", "unstamped", "rows", "aren", "missed", "hevlayer", "post" ] }, { "id": "dashboard", "kind": "section", "title": "Dashboard", "heading": null, "group": "Operations", "url": "/docs/dashboard", "summary": "The Layer dashboard is the operator UI shipped in-cluster alongside the gateway as its own Deployment and Service; this page covers the access it needs, networking, auth gating, and disabling it.", "facts": [ { "kind": "code", "literal": "layer-dashboard", "chunkId": "dashboard" }, { "kind": "value", "literal": "Callout.astro", "chunkId": "dashboard" } ], "sources": [ { "chunkId": "dashboard", "url": "/docs/dashboard", "anchor": null } ], "mode": "agent-primary", "terms": [ "layer", "dashboard", "operator", "shipped", "cluster", "alongside", "gateway", "deployment", "service", "page", "covers", "access", "needs", "networking", "auth", "gating", "disabling", "callout", "astro", "running", "operations", "turning", "ships", "reach", "gate", "turn" ] }, { "id": "dashboard#access-it-needs", "kind": "section", "title": "Dashboard", "heading": "Access it needs", "group": "Operations", "url": "/docs/dashboard#access-it-needs", "summary": "The dashboard reads three sources with separate grants: the gateway API with a bearer key (including the metrics proxy and cost route — it never touches PostgreSQL, Aerospike, or VictoriaMetrics directly), the Kubernetes API through RBAC toggles for read and separately-governed write access, and cost data via the gateway's Cost Explorer IRSA role. Terraform installs still annotate the dashboard ServiceAccount with its IRSA role.", "facts": [ { "kind": "code", "literal": "/v2/metrics", "chunkId": "dashboard#access-it-needs" }, { "kind": "code", "literal": "LAYER_GATEWAY_API_KEY", "chunkId": "dashboard#access-it-needs" }, { "kind": "code", "literal": "deriveFromStore", "chunkId": "dashboard#access-it-needs" }, { "kind": "code", "literal": "VectorStore", "chunkId": "dashboard#access-it-needs" }, { "kind": "code", "literal": "keys", "chunkId": "dashboard#access-it-needs" }, { "kind": "code", "literal": "/v2/cost", "chunkId": "dashboard#access-it-needs" }, { "kind": "code", "literal": "hevlayer.com", "chunkId": "dashboard#access-it-needs" }, { "kind": "code", "literal": "dashboard.kubeAccess.enabled", "chunkId": "dashboard#access-it-needs" }, { "kind": "code", "literal": "dashboard.writeAccess.enabled", "chunkId": "dashboard#access-it-needs" }, { "kind": "code", "literal": "false", "chunkId": "dashboard#access-it-needs" }, { "kind": "code", "literal": "dashboard.serviceAccount.roleArn", "chunkId": "dashboard#access-it-needs" }, { "kind": "code", "literal": "layer_dashboard_role_arn", "chunkId": "dashboard#access-it-needs" } ], "sources": [ { "chunkId": "dashboard#access-it-needs", "url": "/docs/dashboard#access-it-needs", "anchor": "access-it-needs" } ], "mode": "agent-primary", "terms": [ "access", "needs", "dashboard", "reads", "three", "sources", "separate", "grants", "gateway", "bearer", "including", "metrics", "proxy", "cost", "route", "never", "touches", "postgresql", "aerospike", "victoriametrics", "directly", "kubernetes", "through", "rbac", "toggles", "read", "separately", "governed", "write", "data", "explorer", "irsa", "role", "terraform", "installs", "still", "annotate", "serviceaccount", "layer", "derivefromstore" ] }, { "id": "dashboard#basic-auth", "kind": "section", "title": "Dashboard", "heading": "Basic auth", "group": "Operations", "url": "/docs/dashboard#basic-auth", "summary": "HTTP Basic auth fronts every dashboard route and is required — the dashboard refuses to start without it, and the chart render fails if either credential field is blank while the dashboard is enabled.", "facts": [ { "kind": "code", "literal": "dashboard:\n basicAuth:\n user: ops\n password: ", "chunkId": "dashboard#basic-auth" } ], "sources": [ { "chunkId": "dashboard#basic-auth", "url": "/docs/dashboard#basic-auth", "anchor": "basic-auth" } ], "mode": "agent-primary", "terms": [ "basic", "auth", "http", "fronts", "every", "dashboard", "route", "required", "refuses", "start", "without", "chart", "render", "fails", "either", "credential", "field", "blank", "while", "enabled", "basicauth", "user", "password", "strong", "sits", "front", "credentials", "through" ] }, { "id": "dashboard#disabling-the-dashboard", "kind": "section", "title": "Dashboard", "heading": "Disabling the dashboard", "group": "Operations", "url": "/docs/dashboard#disabling-the-dashboard", "summary": "The dashboard is optional: disabling it skips rendering the Deployment, Service, RBAC, and ingress, and the gateway and transform runtime run unchanged — you lose only the operator UI.", "facts": [ { "kind": "code", "literal": "dashboard:\n enabled: false", "chunkId": "dashboard#disabling-the-dashboard" } ], "sources": [ { "chunkId": "dashboard#disabling-the-dashboard", "url": "/docs/dashboard#disabling-the-dashboard", "anchor": "disabling-the-dashboard" } ], "mode": "agent-primary", "terms": [ "disabling", "dashboard", "optional", "skips", "rendering", "deployment", "service", "rbac", "ingress", "gateway", "transform", "runtime", "unchanged", "lose", "only", "operator", "enabled", "false", "disable", "skip", "without" ] }, { "id": "dashboard#networking", "kind": "section", "title": "Dashboard", "heading": "Networking", "group": "Operations", "url": "/docs/dashboard#networking", "summary": "The dashboard is an operator tool reached over a kubectl port-forward rather than public exposure; customer workloads only ever receive the gateway base URL and credentials, never the dashboard.", "facts": [ { "kind": "code", "literal": "kubectl port-forward -n svc/layer-dashboard 8081:8081", "chunkId": "dashboard#networking" }, { "kind": "code", "literal": "http://localhost:8081", "chunkId": "dashboard#networking" } ], "sources": [ { "chunkId": "dashboard#networking", "url": "/docs/dashboard#networking", "anchor": "networking" } ], "mode": "agent-primary", "terms": [ "networking", "dashboard", "operator", "tool", "reached", "kubectl", "port", "forward", "rather", "public", "exposure", "customer", "workloads", "only", "ever", "receive", "gateway", "base", "credentials", "never", "release", "namespace", "layer", "8081", "http", "localhost", "reach", "exposing", "publicly", "open" ] }, { "id": "dashboard#operational-notes", "kind": "section", "title": "Dashboard", "heading": "Operational notes", "group": "Operations", "url": "/docs/dashboard#operational-notes", "summary": "Heading introducing the dashboard's operational notes; the substantive content lives in the pipeline queue states subsection.", "facts": [], "sources": [ { "chunkId": "dashboard#operational-notes", "url": "/docs/dashboard#operational-notes", "anchor": "operational-notes" } ], "mode": "agent-primary", "terms": [ "operational", "notes", "heading", "introducing", "dashboard", "substantive", "content", "lives", "pipeline", "queue", "states", "subsection" ] }, { "id": "dashboard#pipeline-queue-states", "kind": "section", "title": "Dashboard", "heading": "Pipeline queue states", "group": "Operations", "url": "/docs/dashboard#pipeline-queue-states", "summary": "The pipeline overview combines gateway queue counters with Pipeline resources and their operator-managed workloads, classifying queues with pending work as PAUSED (intentionally zero replicas), CRON-GATED (inactive cron scaler, showing the next window), STALLED (pending with no activity and no explanation), or BACKLOG (growing but still processing). The data tab mirrors the credential-safe CLI/SDK shapes, and mutating actions are gated behind confirm dialogs and a separate write-access toggle.", "facts": [ { "kind": "code", "literal": "Pipeline", "chunkId": "dashboard#pipeline-queue-states" }, { "kind": "code", "literal": "spec.paused: true", "chunkId": "dashboard#pipeline-queue-states" }, { "kind": "code", "literal": "/v2/namespaces", "chunkId": "dashboard#pipeline-queue-states" }, { "kind": "code", "literal": "/v2/vectorstores", "chunkId": "dashboard#pipeline-queue-states" }, { "kind": "code", "literal": "/v2/warehouses", "chunkId": "dashboard#pipeline-queue-states" }, { "kind": "code", "literal": "dashboard.writeAccess.enabled", "chunkId": "dashboard#pipeline-queue-states" } ], "sources": [ { "chunkId": "dashboard#pipeline-queue-states", "url": "/docs/dashboard#pipeline-queue-states", "anchor": "pipeline-queue-states" } ], "mode": "agent-primary", "terms": [ "pipeline", "queue", "states", "overview", "combines", "gateway", "counters", "resources", "their", "operator", "managed", "workloads", "classifying", "queues", "pending", "work", "paused", "intentionally", "zero", "replicas", "cron", "gated", "inactive", "scaler", "showing", "next", "window", "stalled", "activity", "explanation", "backlog", "growing", "still", "processing", "data", "mirrors", "credential", "safe", "shapes", "mutating" ] }, { "id": "demos", "kind": "section", "title": "Demos", "heading": null, "group": "Overview", "url": "/docs/demos", "summary": "Six live demos built on Layer, each composing shipped gateway features over a different corpus without reimplementing anything: shelf (query routing on books), chart (clinical routing with qrels), hybrid-text (fusion proven on SciFact), wiki (Auto plus CPU Lattice at Wikipedia scale), lens (text-to-image with zero GPUs), and shop (the everything-together storefront).", "facts": [ { "kind": "value", "literal": "shelf.hevlayer.com", "chunkId": "demos" }, { "kind": "value", "literal": "chart.hevlayer.com", "chunkId": "demos" }, { "kind": "value", "literal": "hybrid-text.hevlayer.com", "chunkId": "demos" }, { "kind": "value", "literal": "wiki.hevlayer.com", "chunkId": "demos" }, { "kind": "value", "literal": "lens.hevlayer.com", "chunkId": "demos" }, { "kind": "value", "literal": "shop.hevlayer.com", "chunkId": "demos" } ], "sources": [ { "chunkId": "demos", "url": "/docs/demos", "anchor": null } ], "mode": "agent-primary", "terms": [ "live", "demos", "built", "layer", "composing", "shipped", "gateway", "features", "different", "corpus", "without", "reimplementing", "anything", "shelf", "query", "routing", "books", "chart", "clinical", "qrels", "hybrid", "text", "fusion", "proven", "scifact", "wiki", "auto", "plus", "lattice", "wikipedia", "scale", "lens", "image", "zero", "gpus", "shop", "everything", "together", "storefront", "hevlayer" ] }, { "id": "demos#chart--clinical-patient-notes-search-that-shows-its-routing", "kind": "section", "title": "Demos", "heading": "chart — clinical patient-notes search that shows its routing", "group": "Overview", "url": "/docs/demos#chart--clinical-patient-notes-search-that-shows-its-routing", "summary": "chart runs the routing story on clinical search, where queries are sharply bimodal between exact tokens and clinical pictures, and is the first demo with real relevance judgments (PMC-Patients ReCDS qrels) so routing and hybrid claims are measured. A scale-to-zero Gemma GPU cascade extracts clinical events and facet labels as the function-runtime showcase; the corpus is published de-identified case reports.", "facts": [ { "kind": "code", "literal": "metformin 500mg", "chunkId": "demos#chart--clinical-patient-notes-search-that-shows-its-routing" }, { "kind": "code", "literal": "CABG", "chunkId": "demos#chart--clinical-patient-notes-search-that-shows-its-routing" }, { "kind": "code", "literal": "aspirn", "chunkId": "demos#chart--clinical-patient-notes-search-that-shows-its-routing" }, { "kind": "value", "literal": "chart.hevlayer.com", "chunkId": "demos#chart--clinical-patient-notes-search-that-shows-its-routing" } ], "sources": [ { "chunkId": "demos#chart--clinical-patient-notes-search-that-shows-its-routing", "url": "/docs/demos#chart--clinical-patient-notes-search-that-shows-its-routing", "anchor": "chart--clinical-patient-notes-search-that-shows-its-routing" } ], "mode": "agent-primary", "terms": [ "chart", "clinical", "patient", "notes", "search", "shows", "routing", "runs", "story", "queries", "sharply", "bimodal", "between", "exact", "tokens", "pictures", "first", "demo", "real", "relevance", "judgments", "patients", "recds", "qrels", "hybrid", "claims", "measured", "scale", "zero", "gemma", "cascade", "extracts", "events", "facet", "labels", "function", "runtime", "showcase", "corpus", "published" ] }, { "id": "demos#hybrid-text--hybrid-text-fusion-over-scifact", "kind": "section", "title": "Demos", "heading": "hybrid-text — hybrid text fusion over SciFact", "group": "Overview", "url": "/docs/demos#hybrid-text--hybrid-text-fusion-over-scifact", "summary": "The eval-shaped demo: purely lexical hybrid text fusion over 5,000 BEIR/SciFact abstracts, with qrels so the UI flags known-relevant results and scores nDCG@10 and recall@10, plus a fusion inspector showing tokens, legs, and the RRF constant. No embeddings, GPU, or vector index.", "facts": [ { "kind": "value", "literal": "hybrid-text.hevlayer.com", "chunkId": "demos#hybrid-text--hybrid-text-fusion-over-scifact" }, { "kind": "value", "literal": "github.com", "chunkId": "demos#hybrid-text--hybrid-text-fusion-over-scifact" } ], "sources": [ { "chunkId": "demos#hybrid-text--hybrid-text-fusion-over-scifact", "url": "/docs/demos#hybrid-text--hybrid-text-fusion-over-scifact", "anchor": "hybrid-text--hybrid-text-fusion-over-scifact" } ], "mode": "agent-primary", "terms": [ "hybrid", "text", "fusion", "scifact", "eval", "shaped", "demo", "purely", "lexical", "beir", "abstracts", "qrels", "flags", "known", "relevant", "results", "scores", "ndcg", "recall", "plus", "inspector", "showing", "tokens", "legs", "constant", "embeddings", "vector", "index", "hevlayer", "github", "live", "source", "sibling", "routing", "demos", "scientific", "query", "string", "fans", "full" ] }, { "id": "demos#lens--text-to-image-search-with-zero-gpus", "kind": "section", "title": "Demos", "heading": "lens — text-to-image search with zero GPUs", "group": "Overview", "url": "/docs/demos#lens--text-to-image-search-with-zero-gpus", "summary": "lens is cross-modal search over Wikimedia Commons Quality images with a two-line schema: a string image-URL attribute with a local CLIP profile, the gateway running the image tower on CPU at write time and the text tower at query time. The app contains no embedding or preprocessing code and no GPU exists anywhere in the path; every result shows the serving contract and live embedding-latency echo with Commons attribution.", "facts": [ { "kind": "code", "literal": "sunset over water", "chunkId": "demos#lens--text-to-image-search-with-zero-gpus" }, { "kind": "code", "literal": "image_url", "chunkId": "demos#lens--text-to-image-search-with-zero-gpus" }, { "kind": "code", "literal": "prefer: local", "chunkId": "demos#lens--text-to-image-search-with-zero-gpus" }, { "kind": "code", "literal": "performance.embedding_ms", "chunkId": "demos#lens--text-to-image-search-with-zero-gpus" }, { "kind": "code", "literal": "serving.prefer: local", "chunkId": "demos#lens--text-to-image-search-with-zero-gpus" }, { "kind": "code", "literal": "modality: image", "chunkId": "demos#lens--text-to-image-search-with-zero-gpus" }, { "kind": "value", "literal": "lens.hevlayer.com", "chunkId": "demos#lens--text-to-image-search-with-zero-gpus" }, { "kind": "value", "literal": "github.com", "chunkId": "demos#lens--text-to-image-search-with-zero-gpus" }, { "kind": "value", "literal": "commons.wikimedia.org", "chunkId": "demos#lens--text-to-image-search-with-zero-gpus" } ], "sources": [ { "chunkId": "demos#lens--text-to-image-search-with-zero-gpus", "url": "/docs/demos#lens--text-to-image-search-with-zero-gpus", "anchor": "lens--text-to-image-search-with-zero-gpus" } ], "mode": "agent-primary", "terms": [ "lens", "text", "image", "search", "zero", "gpus", "cross", "modal", "wikimedia", "commons", "quality", "images", "line", "schema", "string", "attribute", "local", "clip", "profile", "gateway", "running", "tower", "write", "time", "query", "contains", "embedding", "preprocessing", "code", "exists", "anywhere", "path", "every", "result", "shows", "serving", "contract", "live", "latency", "echo" ] }, { "id": "demos#shelf--book-search-that-shows-its-routing", "kind": "section", "title": "Demos", "heading": "shelf — book search that shows its routing", "group": "Overview", "url": "/docs/demos#shelf--book-search-that-shows-its-routing", "summary": "shelf is the text-native routing showcase: one search box over books where the Auto expression picks keyword, semantic, or fused from the query's shape, rendered as a badge with the reason — and canned chips visibly change route as queries lengthen since the policy keys on token count.", "facts": [ { "kind": "code", "literal": "Auto", "chunkId": "demos#shelf--book-search-that-shows-its-routing" }, { "kind": "code", "literal": "hybrid_text", "chunkId": "demos#shelf--book-search-that-shows-its-routing" }, { "kind": "code", "literal": "semantic", "chunkId": "demos#shelf--book-search-that-shows-its-routing" }, { "kind": "code", "literal": "fused", "chunkId": "demos#shelf--book-search-that-shows-its-routing" }, { "kind": "value", "literal": "shelf.hevlayer.com", "chunkId": "demos#shelf--book-search-that-shows-its-routing" }, { "kind": "value", "literal": "github.com", "chunkId": "demos#shelf--book-search-that-shows-its-routing" } ], "sources": [ { "chunkId": "demos#shelf--book-search-that-shows-its-routing", "url": "/docs/demos#shelf--book-search-that-shows-its-routing", "anchor": "shelf--book-search-that-shows-its-routing" } ], "mode": "agent-primary", "terms": [ "shelf", "book", "search", "shows", "routing", "text", "native", "showcase", "books", "auto", "expression", "picks", "keyword", "semantic", "fused", "query", "shape", "rendered", "badge", "reason", "canned", "chips", "visibly", "change", "route", "queries", "lengthen", "since", "policy", "keys", "token", "count", "hybrid", "hevlayer", "github", "live", "source", "three", "routes", "type" ] }, { "id": "demos#shop--semantic-shopping-everything-together", "kind": "section", "title": "Demos", "heading": "shop — semantic shopping, everything together", "group": "Overview", "url": "/docs/demos#shop--semantic-shopping-everything-together", "summary": "shop is the end-to-end workload: CLIP image embedding through pipelines into turbopuffer, image-native semantic search, nearest-to-id recommendations, facet exploration from snapshots, freshness signals, and KEDA/Karpenter autoscaling — the demo exercising the most of Layer at once.", "facts": [ { "kind": "code", "literal": "hev-shop.com", "chunkId": "demos#shop--semantic-shopping-everything-together" }, { "kind": "code", "literal": "nearest_to_id", "chunkId": "demos#shop--semantic-shopping-everything-together" }, { "kind": "value", "literal": "shop.hevlayer.com", "chunkId": "demos#shop--semantic-shopping-everything-together" }, { "kind": "value", "literal": "github.com", "chunkId": "demos#shop--semantic-shopping-everything-together" } ], "sources": [ { "chunkId": "demos#shop--semantic-shopping-everything-together", "url": "/docs/demos#shop--semantic-shopping-everything-together", "anchor": "shop--semantic-shopping-everything-together" } ], "mode": "agent-primary", "terms": [ "shop", "semantic", "shopping", "everything", "together", "workload", "clip", "image", "embedding", "through", "pipelines", "turbopuffer", "native", "search", "nearest", "recommendations", "facet", "exploration", "snapshots", "freshness", "signals", "keda", "karpenter", "autoscaling", "demo", "exercising", "most", "layer", "once", "hevlayer", "github", "live", "formerly", "redirects", "source", "application", "indexing", "pipeline", "facets", "observability" ] }, { "id": "demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu", "kind": "section", "title": "Demos", "heading": "wiki — all of Simple English Wikipedia, routed and embedded on CPU", "group": "Overview", "url": "/docs/demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu", "summary": "wiki runs Auto routing over all 283,997 Simple English Wikipedia articles (1.74M paragraph rows), rendering the routing echo beside every result. Its semantic leg is the Lattice showcase — the whole corpus embedded through an 8 MB int4 artifact served in-process on the gateway CPU with embedding measurements echoed, and no GPU anywhere in the write or query path.", "facts": [ { "kind": "code", "literal": "Auto", "chunkId": "demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu" }, { "kind": "code", "literal": "routing", "chunkId": "demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu" }, { "kind": "code", "literal": "prefer: lattice", "chunkId": "demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu" }, { "kind": "code", "literal": "performance.embedding_ms", "chunkId": "demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu" }, { "kind": "code", "literal": "embedding_tokens", "chunkId": "demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu" }, { "kind": "value", "literal": "wiki.hevlayer.com", "chunkId": "demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu" }, { "kind": "value", "literal": "github.com", "chunkId": "demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu" }, { "kind": "value", "literal": "1.74M", "chunkId": "demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu" } ], "sources": [ { "chunkId": "demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu", "url": "/docs/demos#wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu", "anchor": "wiki--all-of-simple-english-wikipedia-routed-and-embedded-on-cpu" } ], "mode": "agent-primary", "terms": [ "wiki", "simple", "english", "wikipedia", "routed", "embedded", "runs", "auto", "routing", "articles", "paragraph", "rows", "rendering", "echo", "beside", "every", "result", "semantic", "lattice", "showcase", "whole", "corpus", "through", "int4", "artifact", "served", "process", "gateway", "embedding", "measurements", "echoed", "anywhere", "write", "query", "path", "prefer", "performance", "tokens", "hevlayer", "github" ] }, { "id": "document-model", "kind": "section", "title": "Document model", "heading": null, "group": "Overview", "url": "/docs/document-model", "summary": "Layer reserves the hevlayer attribute prefix for read-only bookkeeping the gateway stamps and maintains: the server-stamped upserted-at watermark behind stable reads, the shard hash bucket on sharded namespaces, the Function completion version marker, and the Function invalidation timestamp discovery reclaims on expiry. Writes and UDF patches must never set these; the reserved set depends on the backing engine.", "facts": [ { "kind": "code", "literal": "_hevlayer_*", "chunkId": "document-model" }, { "kind": "code", "literal": "_hevlayer_upserted_at", "chunkId": "document-model" }, { "kind": "code", "literal": "_hevlayer_shard", "chunkId": "document-model" }, { "kind": "code", "literal": "xxh64(id) % shard_count", "chunkId": "document-model" }, { "kind": "code", "literal": "_hevlayer_udf__v", "chunkId": "document-model" }, { "kind": "code", "literal": "spec.version", "chunkId": "document-model" }, { "kind": "code", "literal": "_hevlayer_udf__stale_after", "chunkId": "document-model" }, { "kind": "code", "literal": "_hevlayer_", "chunkId": "document-model" }, { "kind": "value", "literal": "StoreSwitch.astro", "chunkId": "document-model" }, { "kind": "value", "literal": "FeatureGate.astro", "chunkId": "document-model" } ], "sources": [ { "chunkId": "document-model", "url": "/docs/document-model", "anchor": null } ], "mode": "agent-primary", "terms": [ "layer", "reserves", "hevlayer", "attribute", "prefix", "read", "only", "bookkeeping", "gateway", "stamps", "maintains", "server", "stamped", "upserted", "watermark", "behind", "stable", "reads", "shard", "hash", "bucket", "sharded", "namespaces", "function", "completion", "version", "marker", "invalidation", "timestamp", "discovery", "reclaims", "expiry", "writes", "patches", "must", "never", "these", "reserved", "depends", "backing" ] }, { "id": "failure-modes", "kind": "section", "title": "Failure Modes", "heading": null, "group": "Operations", "url": "/docs/failure-modes", "summary": "Layer degrades gracefully — queries and document fetch served from turbopuffer keep functioning when surrounding components fail — and this page details the scenarios where that does not apply.", "facts": [ { "kind": "value", "literal": "Callout.astro", "chunkId": "failure-modes" } ], "sources": [ { "chunkId": "failure-modes", "url": "/docs/failure-modes", "anchor": null } ], "mode": "agent-primary", "terms": [ "layer", "degrades", "gracefully", "queries", "document", "fetch", "served", "turbopuffer", "keep", "functioning", "surrounding", "components", "fail", "page", "details", "scenarios", "does", "apply", "callout", "astro", "reads", "writes", "degrade", "gateway", "cache", "pipeline", "runs", "trouble", "strives", "around" ] }, { "id": "failure-modes#client-failures", "kind": "section", "title": "Failure Modes", "heading": "Client failures", "group": "Operations", "url": "/docs/failure-modes#client-failures", "summary": "When the gateway is unreachable the SDKs return the connection error rather than retrying against a backing store, keeping the client surface backend-neutral so callers never need store-specific credentials or fallback paths.", "facts": [ { "kind": "code", "literal": "VectorStore", "chunkId": "failure-modes#client-failures" } ], "sources": [ { "chunkId": "failure-modes#client-failures", "url": "/docs/failure-modes#client-failures", "anchor": "client-failures" } ], "mode": "agent-primary", "terms": [ "client", "failures", "gateway", "unreachable", "sdks", "return", "connection", "error", "rather", "retrying", "against", "backing", "store", "keeping", "surface", "backend", "neutral", "callers", "never", "need", "specific", "credentials", "fallback", "paths", "vectorstore", "retry", "directly", "keeps", "namespace", "backed", "turbopuffer", "another", "selected", "should", "credential", "path" ] }, { "id": "failure-modes#pipeline-stop-writes", "kind": "section", "title": "Failure Modes", "heading": "Pipeline stop-writes", "group": "Operations", "url": "/docs/failure-modes#pipeline-stop-writes", "summary": "The primary write failure is Aerospike stop-writes during a pipeline job when staged data exceeds the drive allocation; the pipeline does not stall because chunks persist to S3 before the best-effort cache write and state lives in PostgreSQL, with reads degrading to S3 backing. Recovery is automatic — the Helm cache restarts and resets on stop-writes and refills from S3 on demand — and the section lists the operator metrics and warn logs to watch.", "facts": [ { "kind": "code", "literal": "documentCache.autoRestartOnStopWrites: true", "chunkId": "failure-modes#pipeline-stop-writes" }, { "kind": "code", "literal": "layer_aerospike_op_duration_seconds{status=\"aerospike_stop_writes\"}", "chunkId": "failure-modes#pipeline-stop-writes" }, { "kind": "code", "literal": "hevlayer_cache_cold_responses_total", "chunkId": "failure-modes#pipeline-stop-writes" }, { "kind": "code", "literal": "hevlayer_document_cache_cold_starts_total", "chunkId": "failure-modes#pipeline-stop-writes" }, { "kind": "code", "literal": "hevlayer_document_cache_cold_start_seconds", "chunkId": "failure-modes#pipeline-stop-writes" }, { "kind": "code", "literal": "Aerospike chunk write failed (best-effort)", "chunkId": "failure-modes#pipeline-stop-writes" }, { "kind": "code", "literal": "Aerospike chunk read failed; falling back to S3 backing", "chunkId": "failure-modes#pipeline-stop-writes" }, { "kind": "value", "literal": "documentCache.storage.resetOnStart", "chunkId": "failure-modes#pipeline-stop-writes" } ], "sources": [ { "chunkId": "failure-modes#pipeline-stop-writes", "url": "/docs/failure-modes#pipeline-stop-writes", "anchor": "pipeline-stop-writes" } ], "mode": "agent-primary", "terms": [ "pipeline", "stop", "writes", "primary", "write", "failure", "aerospike", "during", "staged", "data", "exceeds", "drive", "allocation", "does", "stall", "because", "chunks", "persist", "before", "best", "effort", "cache", "state", "lives", "postgresql", "reads", "degrading", "backing", "recovery", "automatic", "helm", "restarts", "resets", "refills", "demand", "section", "lists", "operator", "metrics", "warn" ] }, { "id": "failure-modes#read", "kind": "section", "title": "Failure Modes", "heading": "Read", "group": "Operations", "url": "/docs/failure-modes#read", "summary": "A gateway outage doesn't take queries dark: the Python and Go SDKs fall through to turbopuffer direct for compatible queries (losing cache, history, and enhancements), while Layer-only read paths fail fast because they depend on gateway-owned state. The stateless document cache can scale to zero — a cache failure degrades latency, not availability.", "facts": [], "sources": [ { "chunkId": "failure-modes#read", "url": "/docs/failure-modes#read", "anchor": "read" } ], "mode": "agent-primary", "terms": [ "read", "gateway", "outage", "doesn", "take", "queries", "dark", "python", "sdks", "fall", "through", "turbopuffer", "direct", "compatible", "losing", "cache", "history", "enhancements", "while", "layer", "only", "paths", "fail", "fast", "because", "depend", "owned", "state", "stateless", "document", "scale", "zero", "failure", "degrades", "latency", "availability", "reads", "route", "does", "unreachable" ] }, { "id": "failure-modes#write", "kind": "section", "title": "Failure Modes", "heading": "Write", "group": "Operations", "url": "/docs/failure-modes#write", "summary": "Writes also fall through to turbopuffer direct when the gateway is unreachable: the durable upstream accepts the row, but the write skips document-cache warming and pipeline staging until the gateway returns.", "facts": [], "sources": [ { "chunkId": "failure-modes#write", "url": "/docs/failure-modes#write", "anchor": "write" } ], "mode": "agent-primary", "terms": [ "write", "writes", "also", "fall", "through", "turbopuffer", "direct", "gateway", "unreachable", "durable", "upstream", "accepts", "skips", "document", "cache", "warming", "pipeline", "staging", "until", "returns", "again", "client", "still" ] }, { "id": "faq", "kind": "section", "title": "FAQ", "heading": null, "group": "Overview", "url": "/docs/faq", "summary": "The FAQ covers what the rest of the docs don't: licensing, pricing, trials, and where the project is headed.", "facts": [], "sources": [ { "chunkId": "faq", "url": "/docs/faq", "anchor": null } ], "mode": "agent-primary", "terms": [ "covers", "rest", "docs", "licensing", "pricing", "trials", "project", "headed", "started", "page", "answers", "questions" ] }, { "id": "faq#how-do-i-know-whether-my-license-is-healthy", "kind": "section", "title": "FAQ", "heading": "How do I know whether my license is healthy?", "group": "Overview", "url": "/docs/faq#how-do-i-know-whether-my-license-is-healthy", "summary": "Call the license route on your gateway: it reports licensed, grace, or floor from the configured key, and operator and dashboard surfaces will use the same state model when their enforcement ships.", "facts": [ { "kind": "code", "literal": "GET /v2/license", "chunkId": "faq#how-do-i-know-whether-my-license-is-healthy" }, { "kind": "code", "literal": "licensed", "chunkId": "faq#how-do-i-know-whether-my-license-is-healthy" }, { "kind": "code", "literal": "grace", "chunkId": "faq#how-do-i-know-whether-my-license-is-healthy" }, { "kind": "code", "literal": "floor", "chunkId": "faq#how-do-i-know-whether-my-license-is-healthy" } ], "sources": [ { "chunkId": "faq#how-do-i-know-whether-my-license-is-healthy", "url": "/docs/faq#how-do-i-know-whether-my-license-is-healthy", "anchor": "how-do-i-know-whether-my-license-is-healthy" } ], "mode": "agent-primary", "terms": [ "know", "whether", "license", "healthy", "call", "route", "gateway", "reports", "licensed", "grace", "floor", "configured", "operator", "dashboard", "surfaces", "same", "state", "model", "their", "enforcement", "ships", "install", "ship" ] }, { "id": "faq#how-do-i-start-a-trial", "kind": "section", "title": "FAQ", "heading": "How do I start a trial?", "group": "Overview", "url": "/docs/faq#how-do-i-start-a-trial", "summary": "Use the trial signup with a work email; hev layer emails a signed trial key plus current install instructions.", "facts": [], "sources": [ { "chunkId": "faq#how-do-i-start-a-trial", "url": "/docs/faq#how-do-i-start-a-trial", "anchor": "how-do-i-start-a-trial" } ], "mode": "agent-primary", "terms": [ "start", "trial", "signup", "work", "email", "layer", "emails", "signed", "plus", "current", "install", "instructions", "submit" ] }, { "id": "faq#how-much-will-it-cost", "kind": "section", "title": "FAQ", "heading": "How much will it cost?", "group": "Overview", "url": "/docs/faq#how-much-will-it-cost", "summary": "Layer is licensed per operator deployment — one license per operator install (production, staging, and DR each carry their own), with replicas within an install not counting. Pricing is not final for the design-partner cut; commercial terms are handled directly with each design partner.", "facts": [], "sources": [ { "chunkId": "faq#how-much-will-it-cost", "url": "/docs/faq#how-much-will-it-cost", "anchor": "how-much-will-it-cost" } ], "mode": "agent-primary", "terms": [ "much", "cost", "layer", "licensed", "operator", "deployment", "license", "install", "production", "staging", "carry", "their", "replicas", "within", "counting", "pricing", "final", "design", "partner", "commercial", "terms", "handled", "directly", "cluster", "count", "start", "trial" ] }, { "id": "faq#what-is-the-licensing-for-hev-layer", "kind": "section", "title": "FAQ", "heading": "What is the licensing for hev layer?", "group": "Overview", "url": "/docs/faq#what-is-the-licensing-for-hev-layer", "summary": "The standalone gateway is source-available under BSL 1.1 in hev/layer, free to self-host at any scale and converting to Apache-2.0 on its change date; the operator, function runtime, and dashboard require a commercial license using signed keys supplied to Helm as a token or referenced Secret.", "facts": [ { "kind": "code", "literal": "LICENSE", "chunkId": "faq#what-is-the-licensing-for-hev-layer" }, { "kind": "code", "literal": "license.token", "chunkId": "faq#what-is-the-licensing-for-hev-layer" }, { "kind": "value", "literal": "Apache-2", "chunkId": "faq#what-is-the-licensing-for-hev-layer" }, { "kind": "value", "literal": "1.1", "chunkId": "faq#what-is-the-licensing-for-hev-layer" }, { "kind": "value", "literal": "github.com", "chunkId": "faq#what-is-the-licensing-for-hev-layer" }, { "kind": "value", "literal": "Apache-2.0", "chunkId": "faq#what-is-the-licensing-for-hev-layer" }, { "kind": "value", "literal": "2.0", "chunkId": "faq#what-is-the-licensing-for-hev-layer" } ], "sources": [ { "chunkId": "faq#what-is-the-licensing-for-hev-layer", "url": "/docs/faq#what-is-the-licensing-for-hev-layer", "anchor": "what-is-the-licensing-for-hev-layer" } ], "mode": "agent-primary", "terms": [ "licensing", "layer", "standalone", "gateway", "source", "available", "under", "free", "self", "host", "scale", "converting", "apache", "change", "date", "operator", "function", "runtime", "dashboard", "require", "commercial", "license", "signed", "keys", "supplied", "helm", "token", "referenced", "secret", "github", "business", "converts", "licensed", "installs", "trial", "access", "through", "kubernetes", "install", "lifecycle" ] }, { "id": "faq#who-built-hev-layer", "kind": "section", "title": "FAQ", "heading": "Who built hev layer?", "group": "Overview", "url": "/docs/faq#who-built-hev-layer", "summary": "Adam Hevenor built hev layer; it is a hev mind product.", "facts": [ { "kind": "value", "literal": "hevmind.com", "chunkId": "faq#who-built-hev-layer" } ], "sources": [ { "chunkId": "faq#who-built-hev-layer", "url": "/docs/faq#who-built-hev-layer", "anchor": "who-built-hev-layer" } ], "mode": "agent-primary", "terms": [ "built", "layer", "adam", "hevenor", "mind", "product", "hevmind" ] }, { "id": "guarantees", "kind": "section", "title": "No Guarantees", "heading": null, "group": "Overview", "url": "/docs/guarantees", "summary": "Layer can't offer guarantees; instead it makes a set of design, security, and distribution promises intended to make the software easy to use and durable, and this page covers their specific status.", "facts": [ { "kind": "value", "literal": "Callout.astro", "chunkId": "guarantees" } ], "sources": [ { "chunkId": "guarantees", "url": "/docs/guarantees", "anchor": null } ], "mode": "agent-primary", "terms": [ "layer", "offer", "guarantees", "instead", "makes", "design", "security", "distribution", "promises", "intended", "make", "software", "easy", "durable", "page", "covers", "their", "specific", "status", "callout", "astro", "here", "commit", "best", "provide", "secure", "hands", "infrastructure", "ultimately", "responsible", "while", "distribute", "believe", "stand", "test", "time", "those" ] }, { "id": "guarantees#commitments", "kind": "section", "title": "No Guarantees", "heading": "Commitments", "group": "Overview", "url": "/docs/guarantees#commitments", "summary": "The commitments: history and snapshots back up to your S3 bucket (format may change), hot data serves from the local cache, the docs being wrong is treated as a software bug, graceful degradation is added wherever possible, client compatibility is (almost) always preserved with divergences being deliberate improvements, and every multi-leg query expansion sees one consistency cut. Layer was developed by one person orchestrating agentic coding tools with no hand-written code.", "facts": [ { "kind": "value", "literal": "hevmind.com", "chunkId": "guarantees#commitments" } ], "sources": [ { "chunkId": "guarantees#commitments", "url": "/docs/guarantees#commitments", "anchor": "commitments" } ], "mode": "agent-primary", "terms": [ "commitments", "history", "snapshots", "back", "bucket", "format", "change", "data", "serves", "local", "cache", "docs", "being", "wrong", "treated", "software", "graceful", "degradation", "added", "wherever", "possible", "client", "compatibility", "almost", "always", "preserved", "divergences", "deliberate", "improvements", "every", "multi", "query", "expansion", "sees", "consistency", "layer", "developed", "person", "orchestrating", "agentic" ] }, { "id": "index", "kind": "section", "title": "Introduction", "heading": null, "group": "Overview", "url": "/docs", "summary": "Layer is a gateway and function runtime for retrieval systems: one install gives a transparent Rust proxy in front of turbopuffer (fetch, scans, snapshots, cache, pipelines, cost) adopted without client changes, and a function runtime that treats every per-row job as a stateless Kubernetes-native Function the gateway discovers, leases, retries, and writes back with KEDA scale-to-zero. All durable state lives in object storage; open-source companions are Karpenter, Aerospike, PostgreSQL, and VictoriaMetrics, and the gateway is callable from Python, Go, TypeScript, or REST.", "facts": [ { "kind": "code", "literal": "Function", "chunkId": "index" }, { "kind": "value", "literal": "Apache-2", "chunkId": "index" }, { "kind": "value", "literal": "AGPL-3", "chunkId": "index" }, { "kind": "value", "literal": "Diagram.astro", "chunkId": "index" }, { "kind": "value", "literal": "karpenter.sh", "chunkId": "index" }, { "kind": "value", "literal": "Apache-2.0", "chunkId": "index" }, { "kind": "value", "literal": "aerospike.com", "chunkId": "index" }, { "kind": "value", "literal": "AGPL-3.0", "chunkId": "index" }, { "kind": "value", "literal": "www.postgresql.org", "chunkId": "index" }, { "kind": "value", "literal": "victoriametrics.com", "chunkId": "index" }, { "kind": "value", "literal": "2.0", "chunkId": "index" }, { "kind": "value", "literal": "3.0", "chunkId": "index" } ], "sources": [ { "chunkId": "index", "url": "/docs", "anchor": null } ], "mode": "agent-primary", "terms": [ "layer", "gateway", "function", "runtime", "retrieval", "systems", "install", "gives", "transparent", "rust", "proxy", "front", "turbopuffer", "fetch", "scans", "snapshots", "cache", "pipelines", "cost", "adopted", "without", "client", "changes", "treats", "every", "stateless", "kubernetes", "native", "discovers", "leases", "retries", "writes", "back", "keda", "scale", "zero", "durable", "state", "lives", "object" ] }, { "id": "install", "kind": "section", "title": "Install", "heading": null, "group": "Operations", "url": "/docs/install", "summary": "A hev layer install has two stages driven by one command, layer install: Terraform provisions the AWS resources (IAM, S3, ECR, networking, cost-read roles, and optionally a fresh EKS cluster), then Helm installs the gateway, operator, and document cache wired to those outputs. Terraform can be skipped if you already have equivalent AWS resources, minimally an S3 bucket and a gateway IRSA role.", "facts": [ { "kind": "code", "literal": "layer install", "chunkId": "install" }, { "kind": "value", "literal": "Callout.astro", "chunkId": "install" }, { "kind": "value", "literal": "FeatureGate.astro", "chunkId": "install" } ], "sources": [ { "chunkId": "install", "url": "/docs/install", "anchor": null } ], "mode": "agent-primary", "terms": [ "layer", "install", "stages", "driven", "command", "terraform", "provisions", "resources", "networking", "cost", "read", "roles", "optionally", "fresh", "cluster", "helm", "installs", "gateway", "operator", "document", "cache", "wired", "those", "outputs", "skipped", "already", "equivalent", "minimally", "bucket", "irsa", "role", "callout", "astro", "featuregate", "bring", "environment", "runtime", "required", "recommended", "path" ] }, { "id": "install#built-in-compute-pools", "kind": "section", "title": "Install", "heading": "Built-in compute pools", "group": "Operations", "url": "/docs/install#built-in-compute-pools", "summary": "When no cluster-scoped InfraRules/default object exists, the operator supplies built-in compute pools: cpu for general workers, cpu-large for workers needing ephemeral-storage headroom, and gpu for one-NVIDIA-GPU workers. Workloads that omit a pool are mapped from their compute class, and the stock pools select the chart's Karpenter worker node roles; apply InfraRules/default to tune resources, placement, or replica ceilings.", "facts": [ { "kind": "code", "literal": "InfraRules/default", "chunkId": "install#built-in-compute-pools" }, { "kind": "code", "literal": "scaling.pool", "chunkId": "install#built-in-compute-pools" }, { "kind": "code", "literal": "worker.computeClass: cpu", "chunkId": "install#built-in-compute-pools" }, { "kind": "code", "literal": "gpu", "chunkId": "install#built-in-compute-pools" }, { "kind": "code", "literal": "cpu", "chunkId": "install#built-in-compute-pools" }, { "kind": "code", "literal": "cpu-large", "chunkId": "install#built-in-compute-pools" }, { "kind": "code", "literal": "layer.hev.dev/node-role=worker-cpu", "chunkId": "install#built-in-compute-pools" }, { "kind": "code", "literal": "worker-gpu", "chunkId": "install#built-in-compute-pools" }, { "kind": "code", "literal": "workerKarpenter", "chunkId": "install#built-in-compute-pools" } ], "sources": [ { "chunkId": "install#built-in-compute-pools", "url": "/docs/install#built-in-compute-pools", "anchor": "built-in-compute-pools" } ], "mode": "agent-primary", "terms": [ "built", "compute", "pools", "cluster", "scoped", "infrarules", "default", "object", "exists", "operator", "supplies", "general", "workers", "large", "needing", "ephemeral", "storage", "headroom", "nvidia", "workloads", "omit", "pool", "mapped", "their", "class", "stock", "select", "chart", "karpenter", "worker", "node", "roles", "apply", "tune", "resources", "placement", "replica", "ceilings", "scaling", "computeclass" ] }, { "id": "install#cluster-recommended", "kind": "section", "title": "Install", "heading": "Cluster: recommended", "group": "Operations", "url": "/docs/install#cluster-recommended", "summary": "The recommended path is a fresh EKS cluster: a VPC, one always-on arm64 system node, public worker subnets with no NAT Gateway, Karpenter for scale-from-zero CPU and GPU indexing capacity, EBS CSI with a gp3 StorageClass, the AWS Load Balancer Controller, and EFS. Existing clusters are supported by disabling the cluster modules, but you then own the functional prerequisites (S3 bucket, IRSA, registry access, node autoscaling, ingress controller), and existing NAT routing makes every pipeline run a metered transfer.", "facts": [ { "kind": "code", "literal": "system", "chunkId": "install#cluster-recommended" }, { "kind": "code", "literal": "m8g.large", "chunkId": "install#cluster-recommended" }, { "kind": "code", "literal": "worker-cpu", "chunkId": "install#cluster-recommended" }, { "kind": "code", "literal": "worker-gpu", "chunkId": "install#cluster-recommended" }, { "kind": "code", "literal": "gp3", "chunkId": "install#cluster-recommended" } ], "sources": [ { "chunkId": "install#cluster-recommended", "url": "/docs/install#cluster-recommended", "anchor": "cluster-recommended" } ], "mode": "agent-primary", "terms": [ "cluster", "recommended", "path", "fresh", "always", "arm64", "system", "node", "public", "worker", "subnets", "gateway", "karpenter", "scale", "zero", "indexing", "capacity", "storageclass", "load", "balancer", "controller", "existing", "clusters", "supported", "disabling", "modules", "functional", "prerequisites", "bucket", "irsa", "registry", "access", "autoscaling", "ingress", "routing", "makes", "every", "pipeline", "metered", "transfer" ] }, { "id": "install#cost-notes", "kind": "section", "title": "Install", "heading": "Cost notes", "group": "Operations", "url": "/docs/install#cost-notes", "summary": "At-rest fixed cost is roughly $40-70/mo on us-east-1 on-demand pricing for the demo profile (EKS, one small ARM node, shared ALB, small storage), before variable traffic and upstream vector-store usage. Indexing bursts scale worker nodes up through Karpenter and back down; switching workers to private subnets adds standing NAT cost, and read-heavy deployments may need more gateway replicas or dedicated cache capacity.", "facts": [ { "kind": "code", "literal": "system", "chunkId": "install#cost-notes" }, { "kind": "value", "literal": "us-east-1", "chunkId": "install#cost-notes" } ], "sources": [ { "chunkId": "install#cost-notes", "url": "/docs/install#cost-notes", "anchor": "cost-notes" } ], "mode": "agent-primary", "terms": [ "cost", "notes", "rest", "fixed", "roughly", "east", "demand", "pricing", "demo", "profile", "small", "node", "shared", "storage", "before", "variable", "traffic", "upstream", "vector", "store", "usage", "indexing", "bursts", "scale", "worker", "nodes", "through", "karpenter", "back", "down", "switching", "workers", "private", "subnets", "adds", "standing", "read", "heavy", "deployments", "need" ] }, { "id": "install#gateway-auth-modes", "kind": "section", "title": "Install", "heading": "Gateway auth modes", "group": "Operations", "url": "/docs/install#gateway-auth-modes", "summary": "The default deriveFromStore mode is the single-tenant BYOC path where the upstream store credential is the gateway bearer. keys mode adds gateway-only bearer keys with scopes; the chart renders key values into the release Secret (or references pre-created Secrets), and operator workers, KEDA, and the dashboard authenticate with the configured worker key.", "facts": [ { "kind": "code", "literal": "vectorStore:\n credential:\n apiKey: tpuf_...\n inboundAuth:\n mode: deriveFromStore", "chunkId": "install#gateway-auth-modes" }, { "kind": "code", "literal": "vectorStore:\n credential:\n apiKey: tpuf_...\n inboundAuth:\n mode: keys\n workerSecretKey: layer-inbound-worker-api-key\n keys:\n - name: worker\n scopes: [read, write, admin]\n apiKey: layer_worker_...\n secretRef:\n key: layer-inbound-worker-api-key", "chunkId": "install#gateway-auth-modes" }, { "kind": "code", "literal": "deriveFromStore", "chunkId": "install#gateway-auth-modes" }, { "kind": "code", "literal": "keys", "chunkId": "install#gateway-auth-modes" }, { "kind": "code", "literal": "apiKey", "chunkId": "install#gateway-auth-modes" }, { "kind": "code", "literal": "VectorStore", "chunkId": "install#gateway-auth-modes" }, { "kind": "code", "literal": "workerSecretName", "chunkId": "install#gateway-auth-modes" }, { "kind": "code", "literal": "workerSecretKey", "chunkId": "install#gateway-auth-modes" }, { "kind": "code", "literal": "layer-inbound-worker-api-key", "chunkId": "install#gateway-auth-modes" } ], "sources": [ { "chunkId": "install#gateway-auth-modes", "url": "/docs/install#gateway-auth-modes", "anchor": "gateway-auth-modes" } ], "mode": "agent-primary", "terms": [ "gateway", "auth", "modes", "default", "derivefromstore", "mode", "single", "tenant", "byoc", "path", "upstream", "store", "credential", "bearer", "keys", "adds", "only", "scopes", "chart", "renders", "values", "release", "secret", "references", "created", "secrets", "operator", "workers", "keda", "dashboard", "authenticate", "configured", "worker", "vectorstore", "apikey", "tpuf", "inboundauth", "workersecretkey", "layer", "inbound" ] }, { "id": "install#helm", "kind": "section", "title": "Install", "heading": "Helm", "group": "Operations", "url": "/docs/install#helm", "summary": "The Helm chart at infra/helm/layer/ installs the gateway, operator, and document cache into a cluster that already has the AWS resources. layer install applies it with a profile overlay plus a generated values file; bring-your-own-cluster installs run Helm directly with the same values.", "facts": [ { "kind": "code", "literal": "infra/helm/layer/", "chunkId": "install#helm" }, { "kind": "code", "literal": "layer install", "chunkId": "install#helm" }, { "kind": "code", "literal": "values-demo.yaml", "chunkId": "install#helm" }, { "kind": "code", "literal": "values-indexing.yaml", "chunkId": "install#helm" } ], "sources": [ { "chunkId": "install#helm", "url": "/docs/install#helm", "anchor": "helm" } ], "mode": "agent-primary", "terms": [ "helm", "chart", "infra", "layer", "installs", "gateway", "operator", "document", "cache", "cluster", "already", "resources", "install", "applies", "profile", "overlay", "plus", "generated", "values", "file", "bring", "directly", "same", "demo", "yaml", "indexing", "terraform", "equivalent", "manage", "runs" ] }, { "id": "install#image-coordinates", "kind": "section", "title": "Install", "heading": "Image Coordinates", "group": "Operations", "url": "/docs/install#image-coordinates", "summary": "Layer-owned runtime images (gateway pro, operator, dashboard) are pulled from Docker Hub; they are public to pull but licensed surfaces only work with a valid license key, projecting to the license floor otherwise. The in-cluster search backend image is distributed separately, and customer Function/Pipeline worker images are built into your own registry.", "facts": [ { "kind": "code", "literal": "gateway:\n image: hevlayer/layer-gateway-pro:\n\noperator:\n enabled: true\n image: hevlayer/layer-operator:\n\ndashboard:\n enabled: true\n image: hevlayer/layer-dashboard:", "chunkId": "install#image-coordinates" }, { "kind": "code", "literal": "license.token", "chunkId": "install#image-coordinates" }, { "kind": "code", "literal": "license.existingSecret", "chunkId": "install#image-coordinates" }, { "kind": "code", "literal": "search", "chunkId": "install#image-coordinates" } ], "sources": [ { "chunkId": "install#image-coordinates", "url": "/docs/install#image-coordinates", "anchor": "image-coordinates" } ], "mode": "agent-primary", "terms": [ "image", "coordinates", "layer", "owned", "runtime", "images", "gateway", "operator", "dashboard", "pulled", "docker", "public", "pull", "licensed", "surfaces", "only", "work", "valid", "license", "projecting", "floor", "otherwise", "cluster", "search", "backend", "distributed", "separately", "customer", "function", "pipeline", "worker", "built", "registry", "hevlayer", "version", "enabled", "true", "token", "existingsecret", "path" ] }, { "id": "install#install-shape", "kind": "section", "title": "Install", "heading": "Install shape", "group": "Operations", "url": "/docs/install#install-shape", "summary": "An install is one Helm release per environment with one S3 bucket for snapshot and history data. The chart renders a default VectorStore from the provided credential; additional VectorStores can each carry their own upstream credential and inbound auth policy, with namespaces routed between them by the Index resource's store reference.", "facts": [ { "kind": "code", "literal": "VectorStore", "chunkId": "install#install-shape" }, { "kind": "code", "literal": "Index.spec.backend.storeRef", "chunkId": "install#install-shape" }, { "kind": "code", "literal": "keys", "chunkId": "install#install-shape" } ], "sources": [ { "chunkId": "install#install-shape", "url": "/docs/install#install-shape", "anchor": "install-shape" } ], "mode": "agent-primary", "terms": [ "install", "shape", "helm", "release", "environment", "bucket", "snapshot", "history", "data", "chart", "renders", "default", "vectorstore", "provided", "credential", "additional", "vectorstores", "carry", "their", "upstream", "inbound", "auth", "policy", "namespaces", "routed", "between", "index", "resource", "store", "reference", "spec", "backend", "storeref", "keys", "provide", "define", "resources", "route", "scoped", "gateway" ] }, { "id": "install#install-status", "kind": "section", "title": "Install", "heading": "install status", "group": "Operations", "url": "/docs/install#install-status", "summary": "layer install status reports the installed release and workload health: Helm release status, pod readiness, and document-cache node age, type, and utilization — surfacing forgotten billing-quietly nodes. It takes the same profile/region/cluster flags and environment variables as layer install.", "facts": [ { "kind": "code", "literal": "./layer install status", "chunkId": "install#install-status" }, { "kind": "code", "literal": "layer install status", "chunkId": "install#install-status" }, { "kind": "code", "literal": "--aws-profile", "chunkId": "install#install-status" }, { "kind": "code", "literal": "--region", "chunkId": "install#install-status" }, { "kind": "code", "literal": "--cluster-name", "chunkId": "install#install-status" }, { "kind": "code", "literal": "--namespace", "chunkId": "install#install-status" }, { "kind": "code", "literal": "--helm-release", "chunkId": "install#install-status" }, { "kind": "code", "literal": "layer install", "chunkId": "install#install-status" } ], "sources": [ { "chunkId": "install#install-status", "url": "/docs/install#install-status", "anchor": "install-status" } ], "mode": "agent-primary", "terms": [ "install", "status", "layer", "reports", "installed", "release", "workload", "health", "helm", "readiness", "document", "cache", "node", "type", "utilization", "surfacing", "forgotten", "billing", "quietly", "nodes", "takes", "same", "profile", "region", "cluster", "flags", "environment", "variables", "name", "namespace", "instance", "store", "shows", "instead", "defaults" ] }, { "id": "install#install-uninstall", "kind": "section", "title": "Install", "heading": "install uninstall", "group": "Operations", "url": "/docs/install#install-uninstall", "summary": "layer install uninstall removes the Helm release and cluster components and destroys the Terraform footprint, confirming before destruction and running idempotently. A skip-terraform variant removes only the in-cluster install while retaining the AWS footprint.", "facts": [ { "kind": "code", "literal": "./layer install uninstall", "chunkId": "install#install-uninstall" }, { "kind": "code", "literal": "layer install uninstall", "chunkId": "install#install-uninstall" }, { "kind": "code", "literal": "--yes", "chunkId": "install#install-uninstall" }, { "kind": "code", "literal": "--skip-terraform", "chunkId": "install#install-uninstall" } ], "sources": [ { "chunkId": "install#install-uninstall", "url": "/docs/install#install-uninstall", "anchor": "install-uninstall" } ], "mode": "agent-primary", "terms": [ "install", "uninstall", "layer", "removes", "helm", "release", "cluster", "components", "destroys", "terraform", "footprint", "confirming", "before", "destruction", "running", "idempotently", "skip", "variant", "only", "while", "retaining", "confirms", "destroying", "pass", "idempotent", "retains", "rest", "page", "reference", "sets", "bring", "alternative" ] }, { "id": "install#layer-install", "kind": "section", "title": "Install", "heading": "layer install", "group": "Operations", "url": "/docs/install#layer-install", "summary": "Prerequisites are aws, terraform, helm, and kubectl on PATH plus a Turbopuffer API key; you clone the hev/layer repo, build the CLI, and run the install from the checkout. The command prints its resolved plan and asks for confirmation before provisioning, supports flags/environment variables for profile, region, cluster name, credentials, license token, and dashboard login, and offers dry-run, non-interactive (must confirm explicitly), and skip-terraform modes.", "facts": [ { "kind": "code", "literal": "git clone https://github.com/hev/layer\ncd layer\ngo build -o layer ./apps/layer-cli\nexport TURBOPUFFER_API_KEY=\"tpuf_...\"\n./layer install", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "aws", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "terraform", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "helm", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "kubectl", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "hev/layer", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "layer install", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "--aws-profile", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "AWS_PROFILE", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "--profile", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "LAYER_INSTALL_PROFILE", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "demo", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "--region", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "AWS_REGION", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "us-east-1", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "--cluster-name", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "CLUSTER_NAME", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "layer", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "--namespace", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "NAMESPACE", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "--helm-release", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "HELM_RELEASE", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "--node-type", "chunkId": "install#layer-install" }, { "kind": "code", "literal": "SYSTEM_NODE_INSTANCE_TYPE", "chunkId": "install#layer-install" } ], "sources": [ { "chunkId": "install#layer-install", "url": "/docs/install#layer-install", "anchor": "layer-install" } ], "mode": "agent-primary", "terms": [ "layer", "install", "prerequisites", "terraform", "helm", "kubectl", "path", "plus", "turbopuffer", "clone", "repo", "build", "checkout", "command", "prints", "resolved", "plan", "asks", "confirmation", "before", "provisioning", "supports", "flags", "environment", "variables", "profile", "region", "cluster", "name", "credentials", "license", "token", "dashboard", "login", "offers", "interactive", "must", "confirm", "explicitly", "skip" ] }, { "id": "install#layer-operated-search", "kind": "section", "title": "Install", "heading": "Layer-Operated Search", "group": "Operations", "url": "/docs/install#layer-operated-search", "summary": "Enabling the in-chart search backend runs it beside the gateway using the existing IRSA, storing backend data under an S3 search prefix, mounting a node-local object cache, and restricting ingress to Layer's own components. The backend takes no credential of its own and is reachable only internally; a default search-kind VectorStore must use keys or open inbound auth since deriveFromStore requires an upstream credential.", "facts": [ { "kind": "code", "literal": "search.enabled=true", "chunkId": "install#layer-operated-search" }, { "kind": "code", "literal": "search", "chunkId": "install#layer-operated-search" }, { "kind": "code", "literal": "s3:///search", "chunkId": "install#layer-operated-search" }, { "kind": "code", "literal": "kind: search", "chunkId": "install#layer-operated-search" }, { "kind": "code", "literal": "vectorStore.inboundAuth.mode", "chunkId": "install#layer-operated-search" }, { "kind": "code", "literal": "keys", "chunkId": "install#layer-operated-search" }, { "kind": "code", "literal": "open", "chunkId": "install#layer-operated-search" }, { "kind": "code", "literal": "deriveFromStore", "chunkId": "install#layer-operated-search" } ], "sources": [ { "chunkId": "install#layer-operated-search", "url": "/docs/install#layer-operated-search", "anchor": "layer-operated-search" } ], "mode": "agent-primary", "terms": [ "layer", "operated", "search", "enabling", "chart", "backend", "runs", "beside", "gateway", "existing", "irsa", "storing", "data", "under", "prefix", "mounting", "node", "local", "object", "cache", "restricting", "ingress", "components", "takes", "credential", "reachable", "only", "internally", "default", "kind", "vectorstore", "must", "keys", "open", "inbound", "auth", "since", "derivefromstore", "requires", "upstream" ] }, { "id": "install#local-gateway-development", "kind": "section", "title": "Install", "heading": "Local gateway development", "group": "Operations", "url": "/docs/install#local-gateway-development", "summary": "Docker compose starts the gateway's local dependencies, not a replacement control plane: the gateway still resolves VectorStore and Index resources from Kubernetes at startup, so compose runs need a current kube context with Layer CRDs and matching resources applied. A local search backend on the compose network needs a local-only endpoint URL the gateway container can reach.", "facts": [ { "kind": "code", "literal": "docker compose", "chunkId": "install#local-gateway-development" }, { "kind": "code", "literal": "VectorStore", "chunkId": "install#local-gateway-development" }, { "kind": "code", "literal": "Index", "chunkId": "install#local-gateway-development" }, { "kind": "code", "literal": "VectorStore.endpoint.url", "chunkId": "install#local-gateway-development" } ], "sources": [ { "chunkId": "install#local-gateway-development", "url": "/docs/install#local-gateway-development", "anchor": "local-gateway-development" } ], "mode": "agent-primary", "terms": [ "local", "gateway", "development", "docker", "compose", "starts", "dependencies", "replacement", "control", "plane", "still", "resolves", "vectorstore", "index", "resources", "kubernetes", "startup", "runs", "need", "current", "kube", "context", "layer", "crds", "matching", "applied", "search", "backend", "network", "needs", "only", "endpoint", "container", "reach", "based", "installed", "secret", "objects", "running", "cluster" ] }, { "id": "install#outputs", "kind": "section", "title": "Install", "heading": "Outputs", "group": "Operations", "url": "/docs/install#outputs", "summary": "Terraform emits the values Helm needs: the S3 bucket name, gateway IRSA role ARN, and cluster metadata, with runtime images pulled from Docker Hub. Managed resources are tagged Project=hevlayer; activating that as a cost-allocation tag in AWS Billing lets the gateway scope Cost Explorer reads to the Layer stack.", "facts": [ { "kind": "code", "literal": "Project=hevlayer", "chunkId": "install#outputs" } ], "sources": [ { "chunkId": "install#outputs", "url": "/docs/install#outputs", "anchor": "outputs" } ], "mode": "agent-primary", "terms": [ "outputs", "terraform", "emits", "values", "helm", "needs", "bucket", "name", "gateway", "irsa", "role", "cluster", "metadata", "runtime", "images", "pulled", "docker", "managed", "resources", "tagged", "project", "hevlayer", "activating", "cost", "allocation", "billing", "lets", "scope", "explorer", "reads", "layer", "stack", "chart", "install", "owned", "operator", "dashboard", "containers", "pass", "these" ] }, { "id": "install#profiles", "kind": "section", "title": "Install", "heading": "Profiles", "group": "Operations", "url": "/docs/install#profiles", "summary": "The profile flag selects the install footprint: demo (default) is the lean evaluation shape with one arm64 system node and a gp3-PVC-backed document cache at roughly $40-70/mo always-on; indexing is the design-partner shape adding a dedicated NVMe instance-store Karpenter pool for the cache. The cache is ephemeral on both profiles, so losing the volume or node is a non-event.", "facts": [ { "kind": "code", "literal": "--profile", "chunkId": "install#profiles" }, { "kind": "code", "literal": "demo", "chunkId": "install#profiles" }, { "kind": "code", "literal": "m8g.large", "chunkId": "install#profiles" }, { "kind": "code", "literal": "indexing", "chunkId": "install#profiles" }, { "kind": "code", "literal": "i4g.large", "chunkId": "install#profiles" }, { "kind": "code", "literal": "--node-type", "chunkId": "install#profiles" }, { "kind": "code", "literal": "resetOnStart: true", "chunkId": "install#profiles" }, { "kind": "value", "literal": "us-east-1", "chunkId": "install#profiles" } ], "sources": [ { "chunkId": "install#profiles", "url": "/docs/install#profiles", "anchor": "profiles" } ], "mode": "agent-primary", "terms": [ "profiles", "profile", "flag", "selects", "install", "footprint", "demo", "default", "lean", "evaluation", "shape", "arm64", "system", "node", "backed", "document", "cache", "roughly", "always", "indexing", "design", "partner", "adding", "dedicated", "nvme", "instance", "store", "karpenter", "pool", "ephemeral", "both", "losing", "volume", "event", "large", "type", "resetonstart", "true", "east", "data" ] }, { "id": "install#required-values", "kind": "section", "title": "Install", "heading": "Required values", "group": "Operations", "url": "/docs/install#required-values", "summary": "Most of the chart is opinionated defaults; the required values are the upstream store credential, endpoint, and region, the S3 bucket, gateway and dashboard IRSA identities, and image coordinates, with the license token needed for trial/commercial installs. Optional values cover inbound auth mode, the in-cluster search backend, document-cache storage and placement, index-config and GC toggles, consistency polling, cost-tag filters, and ingress; the store API key is the one credential hev layer cannot generate, and license health is exposed on a gateway endpoint with licensed, grace, and floor states.", "facts": [ { "kind": "code", "literal": "VectorStore", "chunkId": "install#required-values" }, { "kind": "code", "literal": "vectorStore.credential.apiKey", "chunkId": "install#required-values" }, { "kind": "code", "literal": "deriveFromStore", "chunkId": "install#required-values" }, { "kind": "code", "literal": "vectorStore.endpoint.url", "chunkId": "install#required-values" }, { "kind": "code", "literal": "vectorStore.endpoint.region", "chunkId": "install#required-values" }, { "kind": "code", "literal": "vectorStore.inboundAuth.mode", "chunkId": "install#required-values" }, { "kind": "code", "literal": "keys", "chunkId": "install#required-values" }, { "kind": "code", "literal": "open", "chunkId": "install#required-values" }, { "kind": "code", "literal": "vectorStore.inboundAuth.keys", "chunkId": "install#required-values" }, { "kind": "code", "literal": "read", "chunkId": "install#required-values" }, { "kind": "code", "literal": "write", "chunkId": "install#required-values" }, { "kind": "code", "literal": "admin", "chunkId": "install#required-values" }, { "kind": "code", "literal": "search.enabled", "chunkId": "install#required-values" }, { "kind": "code", "literal": "search", "chunkId": "install#required-values" }, { "kind": "code", "literal": "vectorStore.kind=search", "chunkId": "install#required-values" }, { "kind": "code", "literal": "search.image", "chunkId": "install#required-values" }, { "kind": "code", "literal": "ghcr.io/hev/*", "chunkId": "install#required-values" }, { "kind": "code", "literal": "gateway.image", "chunkId": "install#required-values" }, { "kind": "code", "literal": "hevlayer/layer-gateway-pro:", "chunkId": "install#required-values" }, { "kind": "code", "literal": "operator.image", "chunkId": "install#required-values" }, { "kind": "code", "literal": "operator.enabled", "chunkId": "install#required-values" }, { "kind": "code", "literal": "hevlayer/layer-operator:", "chunkId": "install#required-values" }, { "kind": "code", "literal": "dashboard.image", "chunkId": "install#required-values" }, { "kind": "code", "literal": "dashboard.enabled", "chunkId": "install#required-values" } ], "sources": [ { "chunkId": "install#required-values", "url": "/docs/install#required-values", "anchor": "required-values" } ], "mode": "agent-primary", "terms": [ "required", "values", "most", "chart", "opinionated", "defaults", "upstream", "store", "credential", "endpoint", "region", "bucket", "gateway", "dashboard", "irsa", "identities", "image", "coordinates", "license", "token", "needed", "trial", "commercial", "installs", "optional", "cover", "inbound", "auth", "mode", "cluster", "search", "backend", "document", "cache", "storage", "placement", "index", "config", "toggles", "consistency" ] }, { "id": "install#run-the-install", "kind": "section", "title": "Install", "heading": "Run the install", "group": "Operations", "url": "/docs/install#run-the-install", "summary": "On a bring-your-own cluster you run Helm directly with a profile overlay plus your own values file. The chart is not published to a public Helm repository — install from the source path or the chart artifact provided during onboarding.", "facts": [ { "kind": "code", "literal": "helm upgrade --install layer ./infra/helm/layer \\\n --namespace layer --create-namespace \\\n -f ./infra/helm/layer/values-demo.yaml \\\n -f values.customer.yaml", "chunkId": "install#run-the-install" } ], "sources": [ { "chunkId": "install#run-the-install", "url": "/docs/install#run-the-install", "anchor": "run-the-install" } ], "mode": "agent-primary", "terms": [ "install", "bring", "cluster", "helm", "directly", "profile", "overlay", "plus", "values", "file", "chart", "published", "public", "repository", "source", "path", "artifact", "provided", "during", "onboarding", "upgrade", "layer", "infra", "namespace", "create", "demo", "yaml", "customer" ] }, { "id": "install#terraform", "kind": "section", "title": "Install", "heading": "Terraform", "group": "Operations", "url": "/docs/install#terraform", "summary": "The Terraform configuration in infra/terraform/ provisions the AWS resources the gateway and operator need, opinionated about required resources and conservative about surrounding ones. Route53 hosted zones and ACM certificates are opt-in since most installs bring existing DNS and TLS.", "facts": [ { "kind": "code", "literal": "infra/terraform/", "chunkId": "install#terraform" } ], "sources": [ { "chunkId": "install#terraform", "url": "/docs/install#terraform", "anchor": "terraform" } ], "mode": "agent-primary", "terms": [ "terraform", "configuration", "infra", "provisions", "resources", "gateway", "operator", "need", "opinionated", "about", "required", "conservative", "surrounding", "ones", "route53", "hosted", "zones", "certificates", "since", "most", "installs", "bring", "existing", "layer", "needs", "behave", "correctly", "around" ] }, { "id": "install#what-gets-installed", "kind": "section", "title": "Install", "heading": "What gets installed", "group": "Operations", "url": "/docs/install#what-gets-installed", "summary": "The chart installs the Rust gateway (turbopuffer-compatible routes, fetch, scans, snapshots, warm jobs, pipeline state), the operator (reconciling the five CRDs), and the Aerospike-backed document cache (scale-to-zero by default), plus optional Karpenter worker and cache node pools and supporting service accounts, IRSA bindings, ingress, and CRDs.", "facts": [ { "kind": "code", "literal": "layer-gateway", "chunkId": "install#what-gets-installed" }, { "kind": "code", "literal": "layer-operator", "chunkId": "install#what-gets-installed" }, { "kind": "code", "literal": "layer-document-cache", "chunkId": "install#what-gets-installed" }, { "kind": "code", "literal": "NodePool", "chunkId": "install#what-gets-installed" }, { "kind": "code", "literal": "EC2NodeClass", "chunkId": "install#what-gets-installed" }, { "kind": "code", "literal": "worker-cpu", "chunkId": "install#what-gets-installed" }, { "kind": "code", "literal": "worker-gpu", "chunkId": "install#what-gets-installed" }, { "kind": "code", "literal": "workerKarpenter.enabled=true", "chunkId": "install#what-gets-installed" }, { "kind": "code", "literal": "document-cache", "chunkId": "install#what-gets-installed" }, { "kind": "code", "literal": "documentCache.karpenter.enabled=true", "chunkId": "install#what-gets-installed" } ], "sources": [ { "chunkId": "install#what-gets-installed", "url": "/docs/install#what-gets-installed", "anchor": "what-gets-installed" } ], "mode": "agent-primary", "terms": [ "gets", "installed", "chart", "installs", "rust", "gateway", "turbopuffer", "compatible", "routes", "fetch", "scans", "snapshots", "warm", "jobs", "pipeline", "state", "operator", "reconciling", "five", "crds", "aerospike", "backed", "document", "cache", "scale", "zero", "default", "plus", "optional", "karpenter", "worker", "node", "pools", "supporting", "service", "accounts", "irsa", "bindings", "ingress", "layer" ] }, { "id": "install#what-it-sets-up", "kind": "section", "title": "Install", "heading": "What it sets up", "group": "Operations", "url": "/docs/install#what-it-sets-up", "summary": "Terraform sets up the S3 bucket for snapshots/history/clickstream, IAM roles and IRSA policies for gateway S3 and Cost Explorer access, ECR repositories for customer worker images, the recommended fresh EKS cluster with VPC and node pools, and optional Route53/ACM DNS and TLS.", "facts": [ { "kind": "code", "literal": "manage_public_dns=true", "chunkId": "install#what-it-sets-up" } ], "sources": [ { "chunkId": "install#what-it-sets-up", "url": "/docs/install#what-it-sets-up", "anchor": "what-it-sets-up" } ], "mode": "agent-primary", "terms": [ "sets", "terraform", "bucket", "snapshots", "history", "clickstream", "roles", "irsa", "policies", "gateway", "cost", "explorer", "access", "repositories", "customer", "worker", "images", "recommended", "fresh", "cluster", "node", "pools", "optional", "route53", "manage", "public", "true", "resource", "purpose", "durable", "storage", "namespace", "search", "events", "plus", "operator", "registry", "space", "built", "function" ] }, { "id": "kubernetes/agent-crd", "kind": "section", "title": "Agent CRD", "heading": null, "group": "Operations", "url": "/docs/kubernetes/agent-crd", "summary": "An Agent is a saved agentic-search configuration as a Kubernetes resource: it binds an inference model, a turn budget, and a set of indices to a name so callers send only a query string, with cost and access decided on the resource rather than the request. It adds no retrieval primitive — its only tool is the federated query, sitting above the Auto router to reformulate, fan out, and score — and returns the same row shape as every other search endpoint.", "facts": [ { "kind": "code", "literal": "Agent", "chunkId": "kubernetes/agent-crd" }, { "kind": "code", "literal": "POST /v2/agents/{name}/query", "chunkId": "kubernetes/agent-crd" }, { "kind": "code", "literal": "Auto", "chunkId": "kubernetes/agent-crd" }, { "kind": "code", "literal": "kubectl get agent -o yaml", "chunkId": "kubernetes/agent-crd" }, { "kind": "code", "literal": "client.agent(\"support-search\").apply()", "chunkId": "kubernetes/agent-crd" }, { "kind": "value", "literal": "FeatureGate.astro", "chunkId": "kubernetes/agent-crd" }, { "kind": "value", "literal": "Callout.astro", "chunkId": "kubernetes/agent-crd" } ], "sources": [ { "chunkId": "kubernetes/agent-crd", "url": "/docs/kubernetes/agent-crd", "anchor": null } ], "mode": "agent-primary", "terms": [ "agent", "saved", "agentic", "search", "configuration", "kubernetes", "resource", "binds", "inference", "model", "turn", "budget", "indices", "name", "callers", "send", "only", "query", "string", "cost", "access", "decided", "rather", "request", "adds", "retrieval", "primitive", "tool", "federated", "sitting", "above", "auto", "router", "reformulate", "score", "returns", "same", "shape", "every", "other" ] }, { "id": "kubernetes/agent-crd#auth", "kind": "section", "title": "Agent CRD", "heading": "Auth", "group": "Operations", "url": "/docs/kubernetes/agent-crd#auth", "summary": "Agent auth follows the same model as the other API endpoints, with multi-namespace queries following federated query auth behavior. Who may invoke an agent is governed by an agent entitlement on the caller's ApiKey.", "facts": [ { "kind": "code", "literal": "agent.", "chunkId": "kubernetes/agent-crd#auth" } ], "sources": [ { "chunkId": "kubernetes/agent-crd#auth", "url": "/docs/kubernetes/agent-crd#auth", "anchor": "auth" } ], "mode": "agent-primary", "terms": [ "auth", "agent", "follows", "same", "model", "other", "endpoints", "multi", "namespace", "queries", "following", "federated", "query", "behavior", "invoke", "governed", "entitlement", "caller", "apikey", "name", "follow" ] }, { "id": "kubernetes/agent-crd#budget", "kind": "section", "title": "Agent CRD", "heading": "Budget", "group": "Operations", "url": "/docs/kubernetes/agent-crd#budget", "summary": "The budget sets a wall-clock deadline for the whole request; on deadline the default best-effort behavior returns the best ranking the agent has, while the error option fails the request instead.", "facts": [ { "kind": "code", "literal": "deadlineMs", "chunkId": "kubernetes/agent-crd#budget" }, { "kind": "code", "literal": "onDeadline", "chunkId": "kubernetes/agent-crd#budget" }, { "kind": "code", "literal": "bestEffort", "chunkId": "kubernetes/agent-crd#budget" }, { "kind": "code", "literal": "error", "chunkId": "kubernetes/agent-crd#budget" } ], "sources": [ { "chunkId": "kubernetes/agent-crd#budget", "url": "/docs/kubernetes/agent-crd#budget", "anchor": "budget" } ], "mode": "agent-primary", "terms": [ "budget", "sets", "wall", "clock", "deadline", "whole", "request", "default", "best", "effort", "behavior", "returns", "ranking", "agent", "while", "error", "option", "fails", "instead", "deadlinems", "ondeadline", "besteffort", "field", "purpose", "hits" ] }, { "id": "kubernetes/agent-crd#indices", "kind": "section", "title": "Agent CRD", "heading": "Indices", "group": "Operations", "url": "/docs/kubernetes/agent-crd#indices", "summary": "The indices list is the set of namespaces the agent searches, passed to the federated query. The operator validates the list is present and non-empty (surfaced as a status condition) but does not confirm each entry exists; reads run under the caller's own credential, so a caller only reaches namespaces its key grants.", "facts": [ { "kind": "code", "literal": "indices", "chunkId": "kubernetes/agent-crd#indices" }, { "kind": "code", "literal": "namespaces", "chunkId": "kubernetes/agent-crd#indices" }, { "kind": "code", "literal": "IndicesResolved", "chunkId": "kubernetes/agent-crd#indices" } ], "sources": [ { "chunkId": "kubernetes/agent-crd#indices", "url": "/docs/kubernetes/agent-crd#indices", "anchor": "indices" } ], "mode": "agent-primary", "terms": [ "indices", "list", "namespaces", "agent", "searches", "passed", "federated", "query", "operator", "validates", "present", "empty", "surfaced", "status", "condition", "does", "confirm", "entry", "exists", "reads", "under", "caller", "credential", "only", "reaches", "grants", "indicesresolved", "checks", "every", "string", "currently", "names", "namespace", "listed" ] }, { "id": "kubernetes/agent-crd#model", "kind": "section", "title": "Agent CRD", "heading": "Model", "group": "Operations", "url": "/docs/kubernetes/agent-crd#model", "summary": "The model block names the inference provider (openrouter), the model id, an optional fallback used on primary timeout or error, and a Secret reference for the provider credential — the token is never inline on the resource. An inline-token escape hatch exists only for local dev seeding of the gateway's in-memory spec; the operator rejects it on the CRD.", "facts": [ { "kind": "code", "literal": "provider", "chunkId": "kubernetes/agent-crd#model" }, { "kind": "code", "literal": "openrouter", "chunkId": "kubernetes/agent-crd#model" }, { "kind": "code", "literal": "name", "chunkId": "kubernetes/agent-crd#model" }, { "kind": "code", "literal": "anthropic/claude-haiku-4-5", "chunkId": "kubernetes/agent-crd#model" }, { "kind": "code", "literal": "fallback", "chunkId": "kubernetes/agent-crd#model" }, { "kind": "code", "literal": "apiKeySecretRef", "chunkId": "kubernetes/agent-crd#model" }, { "kind": "code", "literal": "key", "chunkId": "kubernetes/agent-crd#model" }, { "kind": "code", "literal": "model.apiKey", "chunkId": "kubernetes/agent-crd#model" }, { "kind": "code", "literal": "Agent", "chunkId": "kubernetes/agent-crd#model" }, { "kind": "value", "literal": "e.g", "chunkId": "kubernetes/agent-crd#model" } ], "sources": [ { "chunkId": "kubernetes/agent-crd#model", "url": "/docs/kubernetes/agent-crd#model", "anchor": "model" } ], "mode": "agent-primary", "terms": [ "model", "block", "names", "inference", "provider", "openrouter", "optional", "fallback", "primary", "timeout", "error", "secret", "reference", "credential", "token", "never", "inline", "resource", "escape", "hatch", "exists", "only", "local", "seeding", "gateway", "memory", "spec", "operator", "rejects", "name", "anthropic", "claude", "haiku", "apikeysecretref", "apikey", "agent", "field", "purpose", "backend", "passed" ] }, { "id": "kubernetes/agent-crd#naming", "kind": "section", "title": "Agent CRD", "heading": "Naming", "group": "Operations", "url": "/docs/kubernetes/agent-crd#naming", "summary": "Agent names are cluster-unique at the gateway because callers address agents by name only. If two namespaces define the same name, the gateway keeps the lexicographically later namespace and logs a warning — keep names unique across the cluster.", "facts": [], "sources": [ { "chunkId": "kubernetes/agent-crd#naming", "url": "/docs/kubernetes/agent-crd#naming", "anchor": "naming" } ], "mode": "agent-primary", "terms": [ "naming", "agent", "names", "cluster", "unique", "gateway", "because", "callers", "address", "agents", "name", "only", "namespaces", "define", "same", "keeps", "lexicographically", "later", "namespace", "logs", "warning", "keep", "across", "sorts", "both" ] }, { "id": "kubernetes/agent-crd#observability", "kind": "section", "title": "Agent CRD", "heading": "Observability", "group": "Operations", "url": "/docs/kubernetes/agent-crd#observability", "summary": "Deadline-hit rate, provider latency, and token usage per agent export as gateway metrics, with dollar cost derived downstream from token counts. The reasoning trace is written into the search-history record alongside its query, so agentic and plain searches share one history surface for evaluation.", "facts": [ { "kind": "code", "literal": "hevlayer_*", "chunkId": "kubernetes/agent-crd#observability" } ], "sources": [ { "chunkId": "kubernetes/agent-crd#observability", "url": "/docs/kubernetes/agent-crd#observability", "anchor": "observability" } ], "mode": "agent-primary", "terms": [ "observability", "deadline", "rate", "provider", "latency", "token", "usage", "agent", "export", "gateway", "metrics", "dollar", "cost", "derived", "downstream", "counts", "reasoning", "trace", "written", "search", "history", "record", "alongside", "query", "agentic", "plain", "searches", "share", "surface", "evaluation", "hevlayer", "come", "back", "inference", "response", "extra", "call", "rather", "fetched", "request" ] }, { "id": "kubernetes/agent-crd#output", "kind": "section", "title": "Agent CRD", "heading": "Output", "group": "Operations", "url": "/docs/kubernetes/agent-crd#output", "summary": "Provenance mode adds a per-row agent field with retrieval and relevance scores plus a top-level agent echo, and trace mode adds the full reasoning trace to that echo. Both default off, making the response byte-for-byte the federated query shape — a client cannot tell a reasoning loop produced it.", "facts": [ { "kind": "code", "literal": "provenance", "chunkId": "kubernetes/agent-crd#output" }, { "kind": "code", "literal": "$agent", "chunkId": "kubernetes/agent-crd#output" }, { "kind": "code", "literal": "retrievalScore", "chunkId": "kubernetes/agent-crd#output" }, { "kind": "code", "literal": "relevanceScore", "chunkId": "kubernetes/agent-crd#output" }, { "kind": "code", "literal": "agent", "chunkId": "kubernetes/agent-crd#output" }, { "kind": "code", "literal": "trace", "chunkId": "kubernetes/agent-crd#output" } ], "sources": [ { "chunkId": "kubernetes/agent-crd#output", "url": "/docs/kubernetes/agent-crd#output", "anchor": "output" } ], "mode": "agent-primary", "terms": [ "output", "provenance", "mode", "adds", "agent", "field", "retrieval", "relevance", "scores", "plus", "level", "echo", "trace", "full", "reasoning", "both", "default", "making", "response", "byte", "federated", "query", "shape", "client", "cannot", "tell", "loop", "produced", "retrievalscore", "relevancescore", "purpose", "true", "carries", "gains", "also", "agentic", "search", "request", "contract" ] }, { "id": "kubernetes/agent-crd#retrieval", "kind": "section", "title": "Agent CRD", "heading": "Retrieval", "group": "Operations", "url": "/docs/kubernetes/agent-crd#retrieval", "summary": "All retrieval is expressed against the federated query: fanout (default 8) controls parallel query reformulations, recallDepth (default 50) bounds candidates gathered for ranking, rankBy picks the default route per leg (auto default; semantic legs need a caller-supplied vector or fall back to lexical), and relevanceWeight (default 0.6) balances relevance against recall in the final ranking.", "facts": [ { "kind": "code", "literal": "fanout", "chunkId": "kubernetes/agent-crd#retrieval" }, { "kind": "code", "literal": "recallDepth", "chunkId": "kubernetes/agent-crd#retrieval" }, { "kind": "code", "literal": "50", "chunkId": "kubernetes/agent-crd#retrieval" }, { "kind": "code", "literal": "top_k", "chunkId": "kubernetes/agent-crd#retrieval" }, { "kind": "code", "literal": "rankBy", "chunkId": "kubernetes/agent-crd#retrieval" }, { "kind": "code", "literal": "auto", "chunkId": "kubernetes/agent-crd#retrieval" }, { "kind": "code", "literal": "hybridText", "chunkId": "kubernetes/agent-crd#retrieval" }, { "kind": "code", "literal": "semantic", "chunkId": "kubernetes/agent-crd#retrieval" }, { "kind": "code", "literal": "vector", "chunkId": "kubernetes/agent-crd#retrieval" }, { "kind": "code", "literal": "relevanceWeight", "chunkId": "kubernetes/agent-crd#retrieval" }, { "kind": "code", "literal": "0.6", "chunkId": "kubernetes/agent-crd#retrieval" } ], "sources": [ { "chunkId": "kubernetes/agent-crd#retrieval", "url": "/docs/kubernetes/agent-crd#retrieval", "anchor": "retrieval" } ], "mode": "agent-primary", "terms": [ "retrieval", "expressed", "against", "federated", "query", "fanout", "default", "controls", "parallel", "reformulations", "recalldepth", "bounds", "candidates", "gathered", "ranking", "rankby", "picks", "route", "auto", "semantic", "legs", "need", "caller", "supplied", "vector", "fall", "back", "lexical", "relevanceweight", "balances", "relevance", "recall", "final", "hybridtext", "behavior", "nothing", "reaches", "upstream", "field", "purpose" ] }, { "id": "kubernetes/agent-crd#status", "kind": "section", "title": "Agent CRD", "heading": "Status", "group": "Operations", "url": "/docs/kubernetes/agent-crd#status", "summary": "Status carries health and validation only — latency, turn counts, and spend go to metrics and history, never etcd. Conditions cover Secret resolution, model reachability, and indices validation; phases are Ready, Degraded, and Invalid, and edits land within a periodic gateway refresh interval rather than instantly.", "facts": [ { "kind": "code", "literal": "status", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "SecretResolved", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "model.apiKeySecretRef", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "ModelReachable", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "IndicesResolved", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "spec.indices", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "Ready", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "Degraded", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "Invalid", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "kubectl get agent", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "MODEL", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "INDICES", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "PHASE", "chunkId": "kubernetes/agent-crd#status" }, { "kind": "code", "literal": "kubectl apply", "chunkId": "kubernetes/agent-crd#status" } ], "sources": [ { "chunkId": "kubernetes/agent-crd#status", "url": "/docs/kubernetes/agent-crd#status", "anchor": "status" } ], "mode": "agent-primary", "terms": [ "status", "carries", "health", "validation", "only", "latency", "turn", "counts", "spend", "metrics", "history", "never", "etcd", "conditions", "cover", "secret", "resolution", "model", "reachability", "indices", "phases", "ready", "degraded", "invalid", "edits", "land", "within", "periodic", "gateway", "refresh", "interval", "rather", "instantly", "secretresolved", "apikeysecretref", "modelreachable", "indicesresolved", "spec", "kubectl", "agent" ] }, { "id": "kubernetes/apikey-crd", "kind": "section", "title": "ApiKey CRD", "heading": null, "group": "Operations", "url": "/docs/kubernetes/apikey-crd", "summary": "An ApiKey is a minted credential as a Kubernetes resource: Layer owns the lifecycle (mint, verify, revoke, expire) while each entitlement names a VectorStore, Warehouse, Agent, or Layer itself with scopes and claims for that target. Claims are opaque to Layer, so external systems can use Layer as a key store while keeping authorization decisions to themselves; kubectl YAML and the REST key API are two spellings of one schema.", "facts": [ { "kind": "code", "literal": "ApiKey", "chunkId": "kubernetes/apikey-crd" }, { "kind": "code", "literal": "VectorStore", "chunkId": "kubernetes/apikey-crd" }, { "kind": "code", "literal": "Warehouse", "chunkId": "kubernetes/apikey-crd" }, { "kind": "code", "literal": "Agent", "chunkId": "kubernetes/apikey-crd" }, { "kind": "code", "literal": "kubectl get apikey -o yaml", "chunkId": "kubernetes/apikey-crd" }, { "kind": "code", "literal": "GET /v2/keys/{keyId}", "chunkId": "kubernetes/apikey-crd" } ], "sources": [ { "chunkId": "kubernetes/apikey-crd", "url": "/docs/kubernetes/apikey-crd", "anchor": null } ], "mode": "agent-primary", "terms": [ "apikey", "minted", "credential", "kubernetes", "resource", "layer", "owns", "lifecycle", "mint", "verify", "revoke", "expire", "while", "entitlement", "names", "vectorstore", "warehouse", "agent", "itself", "scopes", "claims", "target", "opaque", "external", "systems", "store", "keeping", "authorization", "decisions", "themselves", "kubectl", "yaml", "rest", "spellings", "schema", "keys", "keyid", "resources", "entitlements", "opens" ] }, { "id": "kubernetes/apikey-crd#backup-and-migration", "kind": "section", "title": "ApiKey CRD", "heading": "Backup and migration", "group": "Operations", "url": "/docs/kubernetes/apikey-crd#backup-and-migration", "summary": "Portable ApiKey migration requires a full namespace Velero backup (or etcd snapshot) rather than YAML export, because the verifier lives in status and apply paths drop that subresource. The procedure restores status explicitly, preserves the pepper Secret before Helm runs, quiesces the operator through the restore window, treats MintBlocked as a failed restore, and is validated end-to-end by a migration script; backups are credential-bearing and must be stored encrypted.", "facts": [ { "kind": "code", "literal": "SOURCE_CONTEXT=layer-a \\\nTARGET_CONTEXT=layer-b \\\nLAYER_NAMESPACE=layer \\\nSOURCE_BASE_URL=https://source.example.com \\\nTARGET_BASE_URL=https://target.example.com \\\nSOURCE_ADMIN_KEY=hvl_... \\\nscripts/apikey-velero-migration.sh", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "ApiKey", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "status", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "status.keyId", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "phase", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "lookupHash", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "tokenHash", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "*-keys", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "LAYER_KEY_PEPPER", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "status.secretRef", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "--status-include-resources apikeys.hevlayer.com", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "Restore", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "Backup", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "Completed", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "MintBlocked", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "Pending", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "apikeys.hevlayer.com", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "Expired", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "/v2/keys", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "code", "literal": "kid", "chunkId": "kubernetes/apikey-crd#backup-and-migration" }, { "kind": "value", "literal": "spec.restoreStatus.includedResources", "chunkId": "kubernetes/apikey-crd#backup-and-migration" } ], "sources": [ { "chunkId": "kubernetes/apikey-crd#backup-and-migration", "url": "/docs/kubernetes/apikey-crd#backup-and-migration", "anchor": "backup-and-migration" } ], "mode": "agent-primary", "terms": [ "backup", "migration", "portable", "apikey", "requires", "full", "namespace", "velero", "etcd", "snapshot", "rather", "yaml", "export", "because", "verifier", "lives", "status", "apply", "paths", "drop", "subresource", "procedure", "restores", "explicitly", "preserves", "pepper", "secret", "before", "helm", "runs", "quiesces", "operator", "through", "restore", "window", "treats", "mintblocked", "failed", "validated", "script" ] }, { "id": "kubernetes/apikey-crd#bootstrapping", "kind": "section", "title": "ApiKey CRD", "heading": "Bootstrapping", "group": "Operations", "url": "/docs/kubernetes/apikey-crd#bootstrapping", "summary": "The gateway's bootstrap credential mints the first admin key, after which routine minting uses minted admin keys. Cluster operators can equally bootstrap by applying an ApiKey resource, since CRD authoring needs only kubectl access.", "facts": [ { "kind": "code", "literal": "spec:\n entitlements:\n layer:\n scopes: [admin]", "chunkId": "kubernetes/apikey-crd#bootstrapping" }, { "kind": "code", "literal": "LAYER_GATEWAY_API_KEY", "chunkId": "kubernetes/apikey-crd#bootstrapping" }, { "kind": "code", "literal": "ApiKey", "chunkId": "kubernetes/apikey-crd#bootstrapping" } ], "sources": [ { "chunkId": "kubernetes/apikey-crd#bootstrapping", "url": "/docs/kubernetes/apikey-crd#bootstrapping", "anchor": "bootstrapping" } ], "mode": "agent-primary", "terms": [ "bootstrapping", "gateway", "bootstrap", "credential", "mints", "first", "admin", "after", "routine", "minting", "uses", "minted", "keys", "cluster", "operators", "equally", "applying", "apikey", "resource", "since", "authoring", "needs", "only", "kubectl", "access", "spec", "entitlements", "layer", "scopes", "layergatewayapikey" ] }, { "id": "kubernetes/apikey-crd#entitlements", "kind": "section", "title": "ApiKey CRD", "heading": "Entitlements", "group": "Operations", "url": "/docs/kubernetes/apikey-crd#entitlements", "summary": "Entitlements are keyed by target: vectorstore entitlements gate data-plane routes with read/write scopes and namespace globs, warehouse entitlements carry only opaque claims that grant nothing in Layer, agent entitlements grant invocation of a named Agent, and the layer entitlement covers the control plane with admin satisfying read and write everywhere. Claims are opaque strings Layer stores and echoes but never interprets; a missing entitlement target grants nothing and surfaces as a status condition rather than an admission error.", "facts": [ { "kind": "code", "literal": "vectorstore.", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "scopes", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "read", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "write", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "Index", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "namespaces", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "warehouse.", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "claims", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "agent.", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "Agent", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "POST /v2/agents//query", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "vectorstore", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "layer", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "scopes: [admin]", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "service:resource_type:resource_id:action", "chunkId": "kubernetes/apikey-crd#entitlements" }, { "kind": "code", "literal": "EntitlementTargetMissing", "chunkId": "kubernetes/apikey-crd#entitlements" } ], "sources": [ { "chunkId": "kubernetes/apikey-crd#entitlements", "url": "/docs/kubernetes/apikey-crd#entitlements", "anchor": "entitlements" } ], "mode": "agent-primary", "terms": [ "entitlements", "keyed", "target", "vectorstore", "gate", "data", "plane", "routes", "read", "write", "scopes", "namespace", "globs", "warehouse", "carry", "only", "opaque", "claims", "grant", "nothing", "layer", "agent", "invocation", "named", "entitlement", "covers", "control", "admin", "satisfying", "everywhere", "strings", "stores", "echoes", "never", "interprets", "missing", "grants", "surfaces", "status", "condition" ] }, { "id": "kubernetes/apikey-crd#kubernetes-rbac", "kind": "section", "title": "ApiKey CRD", "heading": "Kubernetes RBAC", "group": "Operations", "url": "/docs/kubernetes/apikey-crd#kubernetes-rbac", "summary": "Since CRD authoring makes kubectl a minting surface, the chart ships a key-admin ClusterRole (full verbs plus delivered-token Secret reads) and a key-viewer ClusterRole (audit-only, no Secret access). Neither aggregates into the built-in view/edit/admin roles, so namespace viewer never silently means key viewer; bindings are the operator's explicit act via a Helm value.", "facts": [ { "kind": "code", "literal": "hevlayer-key-admin", "chunkId": "kubernetes/apikey-crd#kubernetes-rbac" }, { "kind": "code", "literal": "apikeys", "chunkId": "kubernetes/apikey-crd#kubernetes-rbac" }, { "kind": "code", "literal": "get", "chunkId": "kubernetes/apikey-crd#kubernetes-rbac" }, { "kind": "code", "literal": "hevlayer-key-viewer", "chunkId": "kubernetes/apikey-crd#kubernetes-rbac" }, { "kind": "code", "literal": "list", "chunkId": "kubernetes/apikey-crd#kubernetes-rbac" }, { "kind": "code", "literal": "watch", "chunkId": "kubernetes/apikey-crd#kubernetes-rbac" }, { "kind": "code", "literal": "view", "chunkId": "kubernetes/apikey-crd#kubernetes-rbac" }, { "kind": "code", "literal": "edit", "chunkId": "kubernetes/apikey-crd#kubernetes-rbac" }, { "kind": "code", "literal": "admin", "chunkId": "kubernetes/apikey-crd#kubernetes-rbac" }, { "kind": "code", "literal": "rbac.keyRoleBindings", "chunkId": "kubernetes/apikey-crd#kubernetes-rbac" } ], "sources": [ { "chunkId": "kubernetes/apikey-crd#kubernetes-rbac", "url": "/docs/kubernetes/apikey-crd#kubernetes-rbac", "anchor": "kubernetes-rbac" } ], "mode": "agent-primary", "terms": [ "kubernetes", "rbac", "since", "authoring", "makes", "kubectl", "minting", "surface", "chart", "ships", "admin", "clusterrole", "full", "verbs", "plus", "delivered", "token", "secret", "reads", "viewer", "audit", "only", "access", "neither", "aggregates", "built", "view", "edit", "roles", "namespace", "never", "silently", "means", "bindings", "operator", "explicit", "helm", "value", "hevlayer", "apikeys" ] }, { "id": "kubernetes/apikey-crd#minting", "kind": "section", "title": "ApiKey CRD", "heading": "Minting", "group": "Operations", "url": "/docs/kubernetes/apikey-crd#minting", "summary": "REST minting generates the token, creates the resource, and returns the token exactly once — Layer persists only one-way hashes — with management routes requiring an admin-scoped layer entitlement and an unauthenticated authenticate route where the token itself is the credential. CRD minting applies an ApiKey with no credential and the operator writes the token to an owned, garbage-collected delivery Secret; rotation is mint-deploy-revoke.", "facts": [ { "kind": "code", "literal": "POST /v2/keys # 201 { keyId, …, token } — token returned once\nGET /v2/keys # metadata only; ?includeRevoked\nGET /v2/keys/{keyId}\nPOST /v2/keys/{keyId}/revoke # idempotent\nDELETE /v2/keys/{keyId} # hard delete; Revoked keys only\nPOST /v2/keys/authenticate # body { token } → 200 { keyId, entitlements, … } | 401", "chunkId": "kubernetes/apikey-crd#minting" }, { "kind": "code", "literal": "POST /v2/keys", "chunkId": "kubernetes/apikey-crd#minting" }, { "kind": "code", "literal": "ApiKey", "chunkId": "kubernetes/apikey-crd#minting" }, { "kind": "code", "literal": "layer", "chunkId": "kubernetes/apikey-crd#minting" }, { "kind": "code", "literal": "admin", "chunkId": "kubernetes/apikey-crd#minting" }, { "kind": "code", "literal": "POST /v2/keys/authenticate", "chunkId": "kubernetes/apikey-crd#minting" }, { "kind": "code", "literal": "status.secretRef", "chunkId": "kubernetes/apikey-crd#minting" }, { "kind": "code", "literal": "token", "chunkId": "kubernetes/apikey-crd#minting" }, { "kind": "code", "literal": "phase", "chunkId": "kubernetes/apikey-crd#minting" }, { "kind": "code", "literal": "Pending", "chunkId": "kubernetes/apikey-crd#minting" }, { "kind": "code", "literal": "Active", "chunkId": "kubernetes/apikey-crd#minting" } ], "sources": [ { "chunkId": "kubernetes/apikey-crd#minting", "url": "/docs/kubernetes/apikey-crd#minting", "anchor": "minting" } ], "mode": "agent-primary", "terms": [ "minting", "rest", "generates", "token", "creates", "resource", "returns", "exactly", "once", "layer", "persists", "only", "hashes", "management", "routes", "requiring", "admin", "scoped", "entitlement", "unauthenticated", "authenticate", "route", "itself", "credential", "applies", "apikey", "operator", "writes", "owned", "garbage", "collected", "delivery", "secret", "rotation", "mint", "deploy", "revoke", "post", "keys", "keyid" ] }, { "id": "kubernetes/apikey-crd#spec", "kind": "section", "title": "ApiKey CRD", "heading": "Spec", "group": "Operations", "url": "/docs/kubernetes/apikey-crd#spec", "summary": "The spec carries an optional free-form owner and description, the entitlements map keyed by target resource (each with scopes, namespaces, and claims), and an expiry duration defaulting to 365 days with the concrete expiration computed at mint.", "facts": [ { "kind": "code", "literal": "owner", "chunkId": "kubernetes/apikey-crd#spec" }, { "kind": "code", "literal": "description", "chunkId": "kubernetes/apikey-crd#spec" }, { "kind": "code", "literal": "entitlements", "chunkId": "kubernetes/apikey-crd#spec" }, { "kind": "code", "literal": "scopes", "chunkId": "kubernetes/apikey-crd#spec" }, { "kind": "code", "literal": "namespaces", "chunkId": "kubernetes/apikey-crd#spec" }, { "kind": "code", "literal": "claims", "chunkId": "kubernetes/apikey-crd#spec" }, { "kind": "code", "literal": "expiresAfter", "chunkId": "kubernetes/apikey-crd#spec" }, { "kind": "code", "literal": "never", "chunkId": "kubernetes/apikey-crd#spec" }, { "kind": "code", "literal": "365d", "chunkId": "kubernetes/apikey-crd#spec" }, { "kind": "code", "literal": "status.expiresAt", "chunkId": "kubernetes/apikey-crd#spec" } ], "sources": [ { "chunkId": "kubernetes/apikey-crd#spec", "url": "/docs/kubernetes/apikey-crd#spec", "anchor": "spec" } ], "mode": "agent-primary", "terms": [ "spec", "carries", "optional", "free", "form", "owner", "description", "entitlements", "keyed", "target", "resource", "scopes", "namespaces", "claims", "expiry", "duration", "defaulting", "days", "concrete", "expiration", "computed", "mint", "expiresafter", "never", "365d", "status", "expiresat", "field", "purpose", "label", "echoed", "list", "authenticate", "responses", "entry", "defaults" ] }, { "id": "kubernetes/apikey-crd#verification", "kind": "section", "title": "ApiKey CRD", "heading": "Verification", "group": "Operations", "url": "/docs/kubernetes/apikey-crd#verification", "summary": "External systems present a raw token to the authenticate route and get back a stable key id plus the full entitlements map for their own authorization decisions; the gateway also accepts any Active key's token as a bearer, with verification as one indexed lookup plus a hash check against a watch-fed in-memory map. Phases are Pending, Active, Revoked, and Expired; revoke is the default end-state with resources retained for audit, and hard delete accepts only already-revoked keys.", "facts": [ { "kind": "code", "literal": "POST /v2/keys/authenticate", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "keyId", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "entitlements", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "Active", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "status.lastSeenAt", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "Pending", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "Revoked", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "POST /v2/keys/{keyId}/revoke", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "Expired", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "status.expiresAt", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "ApiKey", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "DELETE /v2/keys/{keyId}", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "409 Conflict", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "VectorStore", "chunkId": "kubernetes/apikey-crd#verification" }, { "kind": "code", "literal": "Warehouse", "chunkId": "kubernetes/apikey-crd#verification" } ], "sources": [ { "chunkId": "kubernetes/apikey-crd#verification", "url": "/docs/kubernetes/apikey-crd#verification", "anchor": "verification" } ], "mode": "agent-primary", "terms": [ "verification", "external", "systems", "present", "token", "authenticate", "route", "back", "stable", "plus", "full", "entitlements", "their", "authorization", "decisions", "gateway", "also", "accepts", "active", "bearer", "indexed", "lookup", "hash", "check", "against", "watch", "memory", "phases", "pending", "revoked", "expired", "revoke", "default", "state", "resources", "retained", "audit", "hard", "delete", "only" ] }, { "id": "kubernetes/function-crd", "kind": "section", "title": "Function CRD", "heading": null, "group": "Operations", "url": "/docs/kubernetes/function-crd", "summary": "The Function CRD declares a stateless user-defined function (UDF) that runs over rows already in an Index — the right shape for classifiers, enrichment, backfills, fan-out, and deterministic re-upserts. The operator creates worker resources, the gateway owns discovery, queueing, retries, leases, and completion markers, workers own their data writes; use a Pipeline when external data becomes rows and a Function when compute starts from existing rows.", "facts": [ { "kind": "code", "literal": "Function", "chunkId": "kubernetes/function-crd" }, { "kind": "value", "literal": "CodeTabs.astro", "chunkId": "kubernetes/function-crd" } ], "sources": [ { "chunkId": "kubernetes/function-crd", "url": "/docs/kubernetes/function-crd", "anchor": null } ], "mode": "agent-primary", "terms": [ "function", "declares", "stateless", "user", "defined", "runs", "rows", "already", "index", "right", "shape", "classifiers", "enrichment", "backfills", "deterministic", "upserts", "operator", "creates", "worker", "resources", "gateway", "owns", "discovery", "queueing", "retries", "leases", "completion", "markers", "workers", "their", "data", "writes", "pipeline", "external", "becomes", "compute", "starts", "existing", "codetabs", "astro" ] }, { "id": "kubernetes/function-crd#gpu-classifier", "kind": "section", "title": "Function CRD", "heading": "GPU classifier", "group": "Operations", "url": "/docs/kubernetes/function-crd#gpu-classifier", "summary": "GPU-bound classifiers (like a CLIP zero-shot vision model) declare the gpu compute class, which defaults to the stock GPU pool selecting the GPU worker node role with one NVIDIA GPU and the standard tolerations. Guidance: bake model weights into the image, keep batch sizes low with timeouts high enough for a batch of forward passes, make lease duration outlast a batch, and choose min replicas of 1 versus 0 based on whether cold-start dominates.", "facts": [ { "kind": "code", "literal": "worker.computeClass: gpu", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "scaling.pool", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "gpu", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "layer.hev.dev/node-role=worker-gpu", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "torch", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "transformers", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "pillow", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "httpx", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "hevlayer", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "worker.batchSize", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "worker.timeoutSeconds", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "schedule.leaseSeconds", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "replicas.min: 1", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "code", "literal": "min: 0", "chunkId": "kubernetes/function-crd#gpu-classifier" }, { "kind": "value", "literal": "e.g", "chunkId": "kubernetes/function-crd#gpu-classifier" } ], "sources": [ { "chunkId": "kubernetes/function-crd#gpu-classifier", "url": "/docs/kubernetes/function-crd#gpu-classifier", "anchor": "gpu-classifier" } ], "mode": "agent-primary", "terms": [ "classifier", "bound", "classifiers", "like", "clip", "zero", "shot", "vision", "model", "declare", "compute", "class", "defaults", "stock", "pool", "selecting", "worker", "node", "role", "nvidia", "standard", "tolerations", "guidance", "bake", "weights", "image", "keep", "batch", "sizes", "timeouts", "high", "enough", "forward", "passes", "make", "lease", "duration", "outlast", "choose", "replicas" ] }, { "id": "kubernetes/function-crd#lifecycle", "kind": "section", "title": "Function CRD", "heading": "Lifecycle", "group": "Operations", "url": "/docs/kubernetes/function-crd#lifecycle", "summary": "Functions are inspected and controlled via kubectl, the layer CLI, pause/unpause spec patches, and a gateway reset-failed route. Registration in the gateway's UDF registry happens at reconciliation, not first discovery — a paused Function is registered immediately as paused, spec changes upsert the definition in place preserving queue state, and a 404 from the per-UDF status route means the Function was never registered, a real failure rather than an intentional pause.", "facts": [ { "kind": "code", "literal": "GET /v2/udfs\n → 200 {\"udfs\": [{\"id\": \"product-tags\", \"paused\": true, ...}]}\n\nGET /v2/udfs/product-tags/status\n → 200 {\"udf_id\": \"product-tags\", \"paused\": true, ...}", "chunkId": "kubernetes/function-crd#lifecycle" }, { "kind": "code", "literal": "spec.paused: true", "chunkId": "kubernetes/function-crd#lifecycle" }, { "kind": "code", "literal": "paused: true", "chunkId": "kubernetes/function-crd#lifecycle" }, { "kind": "code", "literal": "404", "chunkId": "kubernetes/function-crd#lifecycle" }, { "kind": "code", "literal": "/v2/udfs/{id}/status", "chunkId": "kubernetes/function-crd#lifecycle" }, { "kind": "code", "literal": "paused", "chunkId": "kubernetes/function-crd#lifecycle" }, { "kind": "code", "literal": "Udf", "chunkId": "kubernetes/function-crd#lifecycle" }, { "kind": "code", "literal": "UdfStatus", "chunkId": "kubernetes/function-crd#lifecycle" } ], "sources": [ { "chunkId": "kubernetes/function-crd#lifecycle", "url": "/docs/kubernetes/function-crd#lifecycle", "anchor": "lifecycle" } ], "mode": "agent-primary", "terms": [ "lifecycle", "functions", "inspected", "controlled", "kubectl", "layer", "pause", "unpause", "spec", "patches", "gateway", "reset", "failed", "route", "registration", "registry", "happens", "reconciliation", "first", "discovery", "paused", "function", "registered", "immediately", "changes", "upsert", "definition", "place", "preserving", "queue", "state", "status", "means", "never", "real", "failure", "rather", "intentional", "udfs", "product" ] }, { "id": "kubernetes/function-crd#scaling", "kind": "section", "title": "Function CRD", "heading": "Scaling", "group": "Operations", "url": "/docs/kubernetes/function-crd#scaling", "summary": "Function scaling uses the same config as Pipelines: a pool, a mode, and replica bounds, with autoscale emitting a KEDA ScaledObject triggered by UDF queue depth and replica maxima capped by the pool ceiling. For GPU Functions on scale-to-zero pools, a warm-window setting holds the worker and node warm after the queue drains so adjacent batches skip the cold start.", "facts": [ { "kind": "code", "literal": "spec.scaling", "chunkId": "kubernetes/function-crd#scaling" }, { "kind": "code", "literal": "InfraRules/default", "chunkId": "kubernetes/function-crd#scaling" }, { "kind": "code", "literal": "mode: autoscale", "chunkId": "kubernetes/function-crd#scaling" }, { "kind": "code", "literal": "ScaledObject", "chunkId": "kubernetes/function-crd#scaling" }, { "kind": "code", "literal": "layer_udf_queue_depth", "chunkId": "kubernetes/function-crd#scaling" }, { "kind": "code", "literal": "maxReplicasPerWorkload", "chunkId": "kubernetes/function-crd#scaling" }, { "kind": "code", "literal": "spec.scaling.warmWindowSeconds", "chunkId": "kubernetes/function-crd#scaling" } ], "sources": [ { "chunkId": "kubernetes/function-crd#scaling", "url": "/docs/kubernetes/function-crd#scaling", "anchor": "scaling" } ], "mode": "agent-primary", "terms": [ "scaling", "function", "uses", "same", "config", "pipelines", "pool", "mode", "replica", "bounds", "autoscale", "emitting", "keda", "scaledobject", "triggered", "queue", "depth", "maxima", "capped", "ceiling", "functions", "scale", "zero", "pools", "warm", "window", "setting", "holds", "worker", "node", "after", "drains", "adjacent", "batches", "skip", "cold", "start", "spec", "infrarules", "default" ] }, { "id": "kubernetes/function-crd#selection", "kind": "section", "title": "Function CRD", "heading": "Selection", "group": "Operations", "url": "/docs/kubernetes/function-crd#selection", "summary": "Explicit target namespaces or a label-based index selector choose which namespaces a Function sweeps. The filter field preserves arbitrary JSON including array-form turbopuffer filters, which the gateway ANDs with the generated completion-marker predicate during discovery — never include a version-marker predicate yourself, since the gateway builds it from the declared version.", "facts": [ { "kind": "code", "literal": "targetNamespaces", "chunkId": "kubernetes/function-crd#selection" }, { "kind": "code", "literal": "indexSelector", "chunkId": "kubernetes/function-crd#selection" }, { "kind": "code", "literal": "Index", "chunkId": "kubernetes/function-crd#selection" }, { "kind": "code", "literal": "filter", "chunkId": "kubernetes/function-crd#selection" }, { "kind": "code", "literal": "spec.version", "chunkId": "kubernetes/function-crd#selection" } ], "sources": [ { "chunkId": "kubernetes/function-crd#selection", "url": "/docs/kubernetes/function-crd#selection", "anchor": "selection" } ], "mode": "agent-primary", "terms": [ "selection", "explicit", "target", "namespaces", "label", "based", "index", "selector", "choose", "function", "sweeps", "filter", "field", "preserves", "arbitrary", "json", "including", "array", "form", "turbopuffer", "filters", "gateway", "ands", "generated", "completion", "marker", "predicate", "during", "discovery", "never", "include", "version", "yourself", "since", "builds", "declared", "targetnamespaces", "indexselector", "spec", "labels" ] }, { "id": "kubernetes/function-crd#simple-classifier", "kind": "section", "title": "Function CRD", "heading": "Simple classifier", "group": "Operations", "url": "/docs/kubernetes/function-crd#simple-classifier", "summary": "The Python client turns a normal function into the claim/process/complete loop via a decorator, with the declared output name being client-side metadata that becomes a completion attributes patch alongside the gateway-stamped completion marker. Go and TypeScript clients drive the same worker protocol directly — claim a batch, process rows, report completions and failures — with the transient-versus-permanent error split expressed through exceptions in Python and a failure kind field elsewhere.", "facts": [ { "kind": "code", "literal": "output=\"tags\"", "chunkId": "kubernetes/function-crd#simple-classifier" }, { "kind": "code", "literal": "run_udf_worker", "chunkId": "kubernetes/function-crd#simple-classifier" }, { "kind": "code", "literal": "attributes.tags", "chunkId": "kubernetes/function-crd#simple-classifier" }, { "kind": "code", "literal": "inputs", "chunkId": "kubernetes/function-crd#simple-classifier" }, { "kind": "code", "literal": "TransientError", "chunkId": "kubernetes/function-crd#simple-classifier" }, { "kind": "code", "literal": "PermanentError", "chunkId": "kubernetes/function-crd#simple-classifier" }, { "kind": "code", "literal": "FailUdfItems", "chunkId": "kubernetes/function-crd#simple-classifier" }, { "kind": "code", "literal": "failUdfItems", "chunkId": "kubernetes/function-crd#simple-classifier" }, { "kind": "code", "literal": "kind: \"transient\"", "chunkId": "kubernetes/function-crd#simple-classifier" }, { "kind": "code", "literal": "kind: \"permanent\"", "chunkId": "kubernetes/function-crd#simple-classifier" } ], "sources": [ { "chunkId": "kubernetes/function-crd#simple-classifier", "url": "/docs/kubernetes/function-crd#simple-classifier", "anchor": "simple-classifier" } ], "mode": "agent-primary", "terms": [ "simple", "classifier", "python", "client", "turns", "normal", "function", "claim", "process", "complete", "loop", "decorator", "declared", "output", "name", "being", "side", "metadata", "becomes", "completion", "attributes", "patch", "alongside", "gateway", "stamped", "marker", "typescript", "clients", "drive", "same", "worker", "protocol", "directly", "batch", "rows", "report", "completions", "failures", "transient", "versus" ] }, { "id": "kubernetes/function-crd#tuning-knobs", "kind": "section", "title": "Function CRD", "heading": "Tuning knobs", "group": "Operations", "url": "/docs/kubernetes/function-crd#tuning-knobs", "summary": "The tuning knobs bound rows per worker batch, worker call timeout, claim lease duration before reissue, time between discovery scans, concurrent in-flight batches per UDF, concurrent namespace discovery jobs, and retry attempts before a row lands in the failed queue.", "facts": [ { "kind": "code", "literal": "worker.batchSize", "chunkId": "kubernetes/function-crd#tuning-knobs" }, { "kind": "code", "literal": "worker.timeoutSeconds", "chunkId": "kubernetes/function-crd#tuning-knobs" }, { "kind": "code", "literal": "schedule.leaseSeconds", "chunkId": "kubernetes/function-crd#tuning-knobs" }, { "kind": "code", "literal": "schedule.discoveryIntervalSeconds", "chunkId": "kubernetes/function-crd#tuning-knobs" }, { "kind": "code", "literal": "schedule.maxInFlightBatches", "chunkId": "kubernetes/function-crd#tuning-knobs" }, { "kind": "code", "literal": "schedule.maxConcurrentScans", "chunkId": "kubernetes/function-crd#tuning-knobs" }, { "kind": "code", "literal": "retry.maxAttempts", "chunkId": "kubernetes/function-crd#tuning-knobs" }, { "kind": "code", "literal": "failed", "chunkId": "kubernetes/function-crd#tuning-knobs" } ], "sources": [ { "chunkId": "kubernetes/function-crd#tuning-knobs", "url": "/docs/kubernetes/function-crd#tuning-knobs", "anchor": "tuning-knobs" } ], "mode": "agent-primary", "terms": [ "tuning", "knobs", "bound", "rows", "worker", "batch", "call", "timeout", "claim", "lease", "duration", "before", "reissue", "time", "between", "discovery", "scans", "concurrent", "flight", "batches", "namespace", "jobs", "retry", "attempts", "lands", "failed", "queue", "batchsize", "timeoutseconds", "schedule", "leaseseconds", "discoveryintervalseconds", "maxinflightbatches", "maxconcurrentscans", "maxattempts", "knob", "bounds", "long", "held", "scan" ] }, { "id": "kubernetes/function-crd#version-markers", "kind": "section", "title": "Function CRD", "heading": "Version markers", "group": "Operations", "url": "/docs/kubernetes/function-crd#version-markers", "summary": "The declared version is the re-run safety rail: on completion the gateway stamps a per-Function version marker attribute (with hyphens in the name normalized to underscores), and discovery targets rows whose marker is missing, differs, or has expired staleness. Bump the version when a model, taxonomy, or prompt changes to trigger a re-sweep.", "facts": [ { "kind": "code", "literal": "spec.version", "chunkId": "kubernetes/function-crd#version-markers" }, { "kind": "code", "literal": "v1", "chunkId": "kubernetes/function-crd#version-markers" }, { "kind": "code", "literal": "_hevlayer_udf__v", "chunkId": "kubernetes/function-crd#version-markers" }, { "kind": "code", "literal": "metadata.name: product-color", "chunkId": "kubernetes/function-crd#version-markers" }, { "kind": "code", "literal": "_hevlayer_udf_product_color_v", "chunkId": "kubernetes/function-crd#version-markers" }, { "kind": "code", "literal": "_hevlayer_udf__stale_after", "chunkId": "kubernetes/function-crd#version-markers" } ], "sources": [ { "chunkId": "kubernetes/function-crd#version-markers", "url": "/docs/kubernetes/function-crd#version-markers", "anchor": "version-markers" } ], "mode": "agent-primary", "terms": [ "version", "markers", "declared", "safety", "rail", "completion", "gateway", "stamps", "function", "marker", "attribute", "hyphens", "name", "normalized", "underscores", "discovery", "targets", "rows", "whose", "missing", "differs", "expired", "staleness", "bump", "model", "taxonomy", "prompt", "changes", "trigger", "sweep", "spec", "hevlayer", "metadata", "product", "color", "stale", "after", "defaults", "hevlayerudf", "normalizing" ] }, { "id": "kubernetes/function-crd#worker", "kind": "section", "title": "Function CRD", "heading": "Worker", "group": "Operations", "url": "/docs/kubernetes/function-crd#worker", "summary": "The worker block sets the image, dispatch style (pull for SDK claim/poll workers, push for HTTP workers), compute class, batch size, timeout, and an optional pod-level merge patch. The worker pod receives environment variables for UDF id, gateway base URL, batching parameters, and the gateway bearer, which is sourced from the default store credential in deriveFromStore mode or the configured inbound worker key in keys mode.", "facts": [ { "kind": "code", "literal": "image", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "dispatch", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "pull", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "push", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "/run", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "computeClass", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "cpu", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "gpu", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "scaling.pool", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "port", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "batchSize", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "timeoutSeconds", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "podSpec", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "layer run -f", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_UDF_ID", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_BASE_URL", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_UDF_BATCH_SIZE", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_UDF_TIMEOUT_SECONDS", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_UDF_LEASE_SECONDS", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "LAYER_GATEWAY_API_KEY", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "VectorStore", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "deriveFromStore", "chunkId": "kubernetes/function-crd#worker" }, { "kind": "code", "literal": "keys", "chunkId": "kubernetes/function-crd#worker" } ], "sources": [ { "chunkId": "kubernetes/function-crd#worker", "url": "/docs/kubernetes/function-crd#worker", "anchor": "worker" } ], "mode": "agent-primary", "terms": [ "worker", "block", "sets", "image", "dispatch", "style", "pull", "claim", "poll", "workers", "push", "http", "compute", "class", "batch", "size", "timeout", "optional", "level", "merge", "patch", "receives", "environment", "variables", "gateway", "base", "batching", "parameters", "bearer", "sourced", "default", "store", "credential", "derivefromstore", "mode", "configured", "inbound", "keys", "computeclass", "scaling" ] }, { "id": "kubernetes/function-crd#writeback", "kind": "section", "title": "Function CRD", "heading": "Writeback", "group": "Operations", "url": "/docs/kubernetes/function-crd#writeback", "summary": "Workers own data writes: the common case sends returned values as completion attributes that the gateway applies with the reserved completion marker in one column-patch write, while embedding Functions include vectors on completion items and the gateway re-upserts the full row for search-backed stores that lack a column-patch primitive. Workers needing more control can write through the client directly and return nothing, completion then stamps only the marker; use deterministic IDs for row-creating Functions so at-least-once retries stay idempotent.", "facts": [ { "kind": "code", "literal": "@udf(output=\"tags\")", "chunkId": "kubernetes/function-crd#writeback" }, { "kind": "code", "literal": "run_udf_worker", "chunkId": "kubernetes/function-crd#writeback" }, { "kind": "code", "literal": "attributes.tags", "chunkId": "kubernetes/function-crd#writeback" }, { "kind": "code", "literal": "attributes", "chunkId": "kubernetes/function-crd#writeback" }, { "kind": "code", "literal": "patch_columns", "chunkId": "kubernetes/function-crd#writeback" }, { "kind": "code", "literal": "_hevlayer_*", "chunkId": "kubernetes/function-crd#writeback" }, { "kind": "code", "literal": "vector", "chunkId": "kubernetes/function-crd#writeback" }, { "kind": "code", "literal": "vectors", "chunkId": "kubernetes/function-crd#writeback" }, { "kind": "code", "literal": "@udf(kind=\"embedding\")", "chunkId": "kubernetes/function-crd#writeback" }, { "kind": "code", "literal": "tpuf", "chunkId": "kubernetes/function-crd#writeback" }, { "kind": "code", "literal": "None", "chunkId": "kubernetes/function-crd#writeback" } ], "sources": [ { "chunkId": "kubernetes/function-crd#writeback", "url": "/docs/kubernetes/function-crd#writeback", "anchor": "writeback" } ], "mode": "agent-primary", "terms": [ "writeback", "workers", "data", "writes", "common", "case", "sends", "returned", "values", "completion", "attributes", "gateway", "applies", "reserved", "marker", "column", "patch", "write", "while", "embedding", "functions", "include", "vectors", "items", "upserts", "full", "search", "backed", "stores", "lack", "primitive", "needing", "more", "control", "through", "client", "directly", "return", "nothing", "stamps" ] }, { "id": "kubernetes/index-crd", "kind": "section", "title": "Index CRD", "heading": null, "group": "Operations", "url": "/docs/kubernetes/index-crd", "summary": "An Index represents one namespace exposed through the gateway, declaring which upstream namespace to use, snapshot policy, cache posture, consistency mode, embedding identity, search backend policy, and scan fan-out. The backend connection itself lives in a VectorStore that the Index references.", "facts": [ { "kind": "code", "literal": "Index", "chunkId": "kubernetes/index-crd" } ], "sources": [ { "chunkId": "kubernetes/index-crd", "url": "/docs/kubernetes/index-crd", "anchor": null } ], "mode": "agent-primary", "terms": [ "index", "represents", "namespace", "exposed", "through", "gateway", "declaring", "upstream", "snapshot", "policy", "cache", "posture", "consistency", "mode", "embedding", "identity", "search", "backend", "scan", "connection", "itself", "lives", "vectorstore", "references", "declarative", "representation", "managed", "layer", "declares", "apiversion", "hevlayer", "kind", "metadata", "name", "products", "spec", "storeref", "turbopuffer", "default", "distancemetric" ] }, { "id": "kubernetes/index-crd#backend", "kind": "section", "title": "Index CRD", "heading": "Backend", "group": "Operations", "url": "/docs/kubernetes/index-crd#backend", "summary": "The backend block optionally names a VectorStore in the same namespace (defaulting to the default store), overrides the upstream namespace (defaulting to the Index name), and sets the distance metric. For search-kind stores the operator accepts only metrics the backend serves directly, marking the Index not-ready with a metric-mismatch reason otherwise.", "facts": [ { "kind": "code", "literal": "backend.storeRef", "chunkId": "kubernetes/index-crd#backend" }, { "kind": "code", "literal": "VectorStore", "chunkId": "kubernetes/index-crd#backend" }, { "kind": "code", "literal": "backend.namespace", "chunkId": "kubernetes/index-crd#backend" }, { "kind": "code", "literal": "backend.distanceMetric", "chunkId": "kubernetes/index-crd#backend" }, { "kind": "code", "literal": "cosine_distance", "chunkId": "kubernetes/index-crd#backend" }, { "kind": "code", "literal": "kind: search", "chunkId": "kubernetes/index-crd#backend" }, { "kind": "code", "literal": "Ready=False", "chunkId": "kubernetes/index-crd#backend" }, { "kind": "code", "literal": "MetricMismatch", "chunkId": "kubernetes/index-crd#backend" } ], "sources": [ { "chunkId": "kubernetes/index-crd#backend", "url": "/docs/kubernetes/index-crd#backend", "anchor": "backend" } ], "mode": "agent-primary", "terms": [ "backend", "block", "optionally", "names", "vectorstore", "same", "namespace", "defaulting", "default", "store", "overrides", "upstream", "index", "name", "sets", "distance", "metric", "search", "kind", "stores", "operator", "accepts", "only", "metrics", "serves", "directly", "marking", "ready", "mismatch", "reason", "otherwise", "storeref", "distancemetric", "cosine", "false", "metricmismatch", "field", "purpose", "optional", "gateway" ] }, { "id": "kubernetes/index-crd#cache-policy", "kind": "section", "title": "Index CRD", "heading": "Cache policy", "group": "Operations", "url": "/docs/kubernetes/index-crd#cache-policy", "summary": "Aerospike remains an ephemeral cache while durable snapshot history stays in S3. Cache warming uses the same scan fan-out policy as other origin scans.", "facts": [], "sources": [ { "chunkId": "kubernetes/index-crd#cache-policy", "url": "/docs/kubernetes/index-crd#cache-policy", "anchor": "cache-policy" } ], "mode": "agent-primary", "terms": [ "cache", "policy", "aerospike", "remains", "ephemeral", "while", "durable", "snapshot", "history", "stays", "warming", "uses", "same", "scan", "other", "origin", "scans" ] }, { "id": "kubernetes/index-crd#chunking", "kind": "section", "title": "Index CRD", "heading": "Chunking", "group": "Operations", "url": "/docs/kubernetes/index-crd#chunking", "summary": "Scalar-text embedding reuses the Pipeline chunk shape (strategy, unit, size, overlap, tokenizer), keeping the original document row while writing each embedded chunk as a child row with parent and chunk-index attributes. Structured fields use a two-level section composition where each configured JSON field becomes its own section, optionally windowed by a nested split; chunked writes require string document ids and row-based upserts, with columnar writes rejected.", "facts": [ { "kind": "code", "literal": "embed.chunk", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "strategy", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "none", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "fixed", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "recursive", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "sentence", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "markdown", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "unit", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "characters", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "tokens", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "size", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "overlap", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "tokenizer", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "{id}#{i}", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "_hevlayer_parent_id", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "_hevlayer_chunk_index", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "split", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "{id}#{section}#{i}", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "upsert_rows", "chunkId": "kubernetes/index-crd#chunking" }, { "kind": "code", "literal": "422", "chunkId": "kubernetes/index-crd#chunking" } ], "sources": [ { "chunkId": "kubernetes/index-crd#chunking", "url": "/docs/kubernetes/index-crd#chunking", "anchor": "chunking" } ], "mode": "agent-primary", "terms": [ "chunking", "scalar", "text", "embedding", "reuses", "pipeline", "chunk", "shape", "strategy", "unit", "size", "overlap", "tokenizer", "keeping", "original", "document", "while", "writing", "embedded", "child", "parent", "index", "attributes", "structured", "fields", "level", "section", "composition", "configured", "json", "field", "becomes", "optionally", "windowed", "nested", "split", "chunked", "writes", "require", "string" ] }, { "id": "kubernetes/index-crd#embedding", "kind": "section", "title": "Index CRD", "heading": "Embedding", "group": "Operations", "url": "/docs/kubernetes/index-crd#embedding", "summary": "The embedding block declares the identity of a namespace's already-computed vectors — model, output dimensionality, and normalization — which together with the distance metric form the embedding profile the gateway compares across federated fan-outs. It is optional for single-namespace queries but required to include a namespace in a federated vector query; mismatched profiles fall back to rank-interleave or are rejected under strict mode, and this block computes nothing (gateway-computed embedding is declared on schema attributes instead).", "facts": [ { "kind": "code", "literal": "spec.embedding", "chunkId": "kubernetes/index-crd#embedding" }, { "kind": "code", "literal": "embedding.model", "chunkId": "kubernetes/index-crd#embedding" }, { "kind": "code", "literal": "voyage-3-large@v1", "chunkId": "kubernetes/index-crd#embedding" }, { "kind": "code", "literal": "embedding.outputDim", "chunkId": "kubernetes/index-crd#embedding" }, { "kind": "code", "literal": "embedding.normalization", "chunkId": "kubernetes/index-crd#embedding" }, { "kind": "code", "literal": "l2", "chunkId": "kubernetes/index-crd#embedding" }, { "kind": "code", "literal": "none", "chunkId": "kubernetes/index-crd#embedding" }, { "kind": "code", "literal": "backend.distanceMetric", "chunkId": "kubernetes/index-crd#embedding" }, { "kind": "code", "literal": "strict", "chunkId": "kubernetes/index-crd#embedding" }, { "kind": "code", "literal": "embed:", "chunkId": "kubernetes/index-crd#embedding" }, { "kind": "value", "literal": "e.g", "chunkId": "kubernetes/index-crd#embedding" } ], "sources": [ { "chunkId": "kubernetes/index-crd#embedding", "url": "/docs/kubernetes/index-crd#embedding", "anchor": "embedding" } ], "mode": "agent-primary", "terms": [ "embedding", "block", "declares", "identity", "namespace", "already", "computed", "vectors", "model", "output", "dimensionality", "normalization", "together", "distance", "metric", "form", "profile", "gateway", "compares", "across", "federated", "outs", "optional", "single", "queries", "required", "include", "vector", "query", "mismatched", "profiles", "fall", "back", "rank", "interleave", "rejected", "under", "strict", "mode", "computes" ] }, { "id": "kubernetes/index-crd#scan-policy", "kind": "section", "title": "Index CRD", "heading": "Scan policy", "group": "Operations", "url": "/docs/kubernetes/index-crd#scan-policy", "summary": "The scan-threads setting is the per-namespace default for origin scan fan-out — the maximum concurrent upstream requests one scan may issue — defaulting to 8 and clamped by the gateway's server cap and shard count. A request-level override applies to a single scan.", "facts": [ { "kind": "code", "literal": "scan.threads", "chunkId": "kubernetes/index-crd#scan-policy" }, { "kind": "code", "literal": "threads", "chunkId": "kubernetes/index-crd#scan-policy" } ], "sources": [ { "chunkId": "kubernetes/index-crd#scan-policy", "url": "/docs/kubernetes/index-crd#scan-policy", "anchor": "scan-policy" } ], "mode": "agent-primary", "terms": [ "scan", "policy", "threads", "setting", "namespace", "default", "origin", "maximum", "concurrent", "upstream", "requests", "issue", "defaulting", "clamped", "gateway", "server", "shard", "count", "request", "level", "override", "applies", "single", "sets", "during", "scatter", "gather", "defaults", "active", "overrides" ] }, { "id": "kubernetes/index-crd#schema-attribute-embedding", "kind": "section", "title": "Index CRD", "heading": "Schema-attribute embedding", "group": "Operations", "url": "/docs/kubernetes/index-crd#schema-attribute-embedding", "summary": "A schema attribute can declare embedding so its source value is embedded on write — Turbopuffer's native-embeddings wire, which Layer validates and forwards for native serving or resolves itself for autoscaler and local serving, writing only concrete vectors to the store. The simple form is a provider-namespaced model id; the extended form adds Layer extensions (revision pinning, asymmetric instructions, image modality, chunking, serving preference) which generally require autoscaler serving, with image modality on CLIP-family models also allowed under local serving. The first row write establishing an embedded schema must include the distance metric.", "facts": [ { "kind": "code", "literal": "embed:", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "embed", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "voyage/voyage-4-lite", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "embed.model", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "embed.dims", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "embed.attribute", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "embed_", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "embed.serving.prefer", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "native", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "autoscaler", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "local", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "lattice", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "embed.revision", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "embed.instructions", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "document", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "query", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "embed.modality", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "text", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "image", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "Embed", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "embed.chunk", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "prefer: autoscaler", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "modality: image", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" }, { "kind": "code", "literal": "prefer: local", "chunkId": "kubernetes/index-crd#schema-attribute-embedding" } ], "sources": [ { "chunkId": "kubernetes/index-crd#schema-attribute-embedding", "url": "/docs/kubernetes/index-crd#schema-attribute-embedding", "anchor": "schema-attribute-embedding" } ], "mode": "agent-primary", "terms": [ "schema", "attribute", "embedding", "declare", "source", "value", "embedded", "write", "turbopuffer", "native", "embeddings", "wire", "layer", "validates", "forwards", "serving", "resolves", "itself", "autoscaler", "local", "writing", "only", "concrete", "vectors", "store", "simple", "form", "provider", "namespaced", "model", "extended", "adds", "extensions", "revision", "pinning", "asymmetric", "instructions", "image", "modality", "chunking" ] }, { "id": "kubernetes/index-crd#search-backend-policy", "kind": "section", "title": "Index CRD", "heading": "Search backend policy", "group": "Operations", "url": "/docs/kubernetes/index-crd#search-backend-policy", "summary": "The search block applies when the Index targets a search-kind VectorStore, driving the backend's explicit index lifecycle. Its full-text flag (default off) builds the backend's BM25 index for the namespace's text column, needed for lexical, FTS, or hybrid-text namespaces.", "facts": [ { "kind": "code", "literal": "spec.search", "chunkId": "kubernetes/index-crd#search-backend-policy" }, { "kind": "code", "literal": "kind: search", "chunkId": "kubernetes/index-crd#search-backend-policy" }, { "kind": "code", "literal": "search.fullText", "chunkId": "kubernetes/index-crd#search-backend-policy" }, { "kind": "code", "literal": "false", "chunkId": "kubernetes/index-crd#search-backend-policy" }, { "kind": "code", "literal": "text", "chunkId": "kubernetes/index-crd#search-backend-policy" } ], "sources": [ { "chunkId": "kubernetes/index-crd#search-backend-policy", "url": "/docs/kubernetes/index-crd#search-backend-policy", "anchor": "search-backend-policy" } ], "mode": "agent-primary", "terms": [ "search", "backend", "policy", "block", "applies", "index", "targets", "kind", "vectorstore", "driving", "explicit", "lifecycle", "full", "text", "flag", "default", "builds", "bm25", "namespace", "column", "needed", "lexical", "hybrid", "namespaces", "spec", "fulltext", "false", "operator", "uses", "drive", "field", "purpose", "build", "enable" ] }, { "id": "kubernetes/index-crd#serving-mode", "kind": "section", "title": "Index CRD", "heading": "Serving mode", "group": "Operations", "url": "/docs/kubernetes/index-crd#serving-mode", "summary": "Per model, the serving preference chooses how vectors are computed: native forwards to Turbopuffer's managed service (or resolves through the configured provider on hev search), autoscaler uses the configured inference provider for BYO checkpoints and extensions, and local resolves in the gateway process with a Lattice or CLIP artifact — Layer never switches modes after a provider failure. Gateway-served attribute profiles are durably recorded in S3, patching an embedded source attribute is rejected because a vector cannot be recomputed from a partial row, and a hev search deployment needs a credentialed turbopuffer store alongside it to serve as the managed embedding provider.", "facts": [ { "kind": "code", "literal": "embed.serving.prefer", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "prefer", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "native", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "autoscaler", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "local", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "lattice", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "erikkaum/lattice-retrieval", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "model.safetensors", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "tokenizer.json", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "LAYER_LATTICE_MODEL_PATH", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "LAYER_LOCAL_CLIP_MODEL_PATH", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "schema", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "422", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "kind: turbopuffer", "chunkId": "kubernetes/index-crd#serving-mode" }, { "kind": "code", "literal": "503 service_unavailable", "chunkId": "kubernetes/index-crd#serving-mode" } ], "sources": [ { "chunkId": "kubernetes/index-crd#serving-mode", "url": "/docs/kubernetes/index-crd#serving-mode", "anchor": "serving-mode" } ], "mode": "agent-primary", "terms": [ "serving", "mode", "model", "preference", "chooses", "vectors", "computed", "native", "forwards", "turbopuffer", "managed", "service", "resolves", "through", "configured", "provider", "search", "autoscaler", "uses", "inference", "checkpoints", "extensions", "local", "gateway", "process", "lattice", "clip", "artifact", "layer", "never", "switches", "modes", "after", "failure", "served", "attribute", "profiles", "durably", "recorded", "patching" ] }, { "id": "kubernetes/index-crd#snapshot-policy", "kind": "section", "title": "Index CRD", "heading": "Snapshot policy", "group": "Operations", "url": "/docs/kubernetes/index-crd#snapshot-policy", "summary": "Snapshot policy declares which fields the gateway materializes into durable facet snapshots (empty disables the writer), the minimum interval between automatic snapshot writes after upstream-stable advances (default five minutes), and retention — keep everything forever or prune older bodies past a duration while keeping the latest.", "facts": [ { "kind": "code", "literal": "snapshot.facetFields", "chunkId": "kubernetes/index-crd#snapshot-policy" }, { "kind": "code", "literal": "[]", "chunkId": "kubernetes/index-crd#snapshot-policy" }, { "kind": "code", "literal": "snapshot.interval", "chunkId": "kubernetes/index-crd#snapshot-policy" }, { "kind": "code", "literal": "5m", "chunkId": "kubernetes/index-crd#snapshot-policy" }, { "kind": "code", "literal": "snapshot.retention", "chunkId": "kubernetes/index-crd#snapshot-policy" }, { "kind": "code", "literal": "never", "chunkId": "kubernetes/index-crd#snapshot-policy" }, { "kind": "code", "literal": "30d", "chunkId": "kubernetes/index-crd#snapshot-policy" } ], "sources": [ { "chunkId": "kubernetes/index-crd#snapshot-policy", "url": "/docs/kubernetes/index-crd#snapshot-policy", "anchor": "snapshot-policy" } ], "mode": "agent-primary", "terms": [ "snapshot", "policy", "declares", "fields", "gateway", "materializes", "durable", "facet", "snapshots", "empty", "disables", "writer", "minimum", "interval", "between", "automatic", "writes", "after", "upstream", "stable", "advances", "default", "five", "minutes", "retention", "keep", "everything", "forever", "prune", "older", "bodies", "past", "duration", "while", "keeping", "latest", "facetfields", "never", "field", "purpose" ] }, { "id": "kubernetes/index-crd#status", "kind": "section", "title": "Index CRD", "heading": "Status", "group": "Operations", "url": "/docs/kubernetes/index-crd#status", "summary": "The operator reports observed generation, metadata sync state, and conditions on Index status. The snapshot last-run and last-success fields are reserved for the gateway history bridge.", "facts": [ { "kind": "code", "literal": "status.snapshot.lastRun", "chunkId": "kubernetes/index-crd#status" }, { "kind": "code", "literal": "lastSuccess", "chunkId": "kubernetes/index-crd#status" } ], "sources": [ { "chunkId": "kubernetes/index-crd#status", "url": "/docs/kubernetes/index-crd#status", "anchor": "status" } ], "mode": "agent-primary", "terms": [ "status", "operator", "reports", "observed", "generation", "metadata", "sync", "state", "conditions", "index", "snapshot", "last", "success", "fields", "reserved", "gateway", "history", "bridge", "lastrun", "lastsuccess" ] }, { "id": "kubernetes/operator", "kind": "section", "title": "Operator Overview", "heading": null, "group": "Operations", "url": "/docs/kubernetes/operator", "summary": "layer-operator manages declarative state for a hev layer deployment through CRDs: which vector store the gateway fronts, which indexes exist, how worker pools scale, and which stateless functions run against which indexes. The gateway handles the read and write path; the operator handles everything expressed as desired state in the cluster.", "facts": [ { "kind": "code", "literal": "layer-operator", "chunkId": "kubernetes/operator" } ], "sources": [ { "chunkId": "kubernetes/operator", "url": "/docs/kubernetes/operator", "anchor": null } ], "mode": "agent-primary", "terms": [ "layer", "operator", "manages", "declarative", "state", "deployment", "through", "crds", "vector", "store", "gateway", "fronts", "indexes", "exist", "worker", "pools", "scale", "stateless", "functions", "against", "handles", "read", "write", "path", "everything", "expressed", "desired", "cluster", "reconciles", "relates", "serves", "crucial", "monitoring", "changes", "managing", "scaling", "does", "abstractions", "known", "custom" ] }, { "id": "kubernetes/operator#crds", "kind": "section", "title": "Operator Overview", "heading": "CRDs", "group": "Operations", "url": "/docs/kubernetes/operator#crds", "summary": "The operator reconciles five resource kinds, each with its own docs page: VectorStore (upstream endpoint, credential, inbound auth), Index (one per managed namespace), InfraRules (compute pools and shared scaling policy), Pipeline (staged row-changing work), and Function (stateless UDFs over an index).", "facts": [], "sources": [ { "chunkId": "kubernetes/operator#crds", "url": "/docs/kubernetes/operator#crds", "anchor": "crds" } ], "mode": "agent-primary", "terms": [ "crds", "operator", "reconciles", "five", "resource", "kinds", "docs", "page", "vectorstore", "upstream", "endpoint", "credential", "inbound", "auth", "index", "managed", "namespace", "infrarules", "compute", "pools", "shared", "scaling", "policy", "pipeline", "staged", "changing", "work", "function", "stateless", "udfs", "documented", "store", "reference", "gateway", "turbopuffer", "should", "manage", "cluster", "wide", "document" ] }, { "id": "kubernetes/operator#relationship-to-the-gateway", "kind": "section", "title": "Operator Overview", "heading": "Relationship to the gateway", "group": "Operations", "url": "/docs/kubernetes/operator#relationship-to-the-gateway", "summary": "The gateway and operator are decoupled — neither sits in the other's hot path, so the gateway keeps serving if the operator restarts or lags. The link is one-directional and read-only: the gateway reads CRD status to inform what it serves but never writes to CRDs; declarative state is authored by users and reconciled by the operator.", "facts": [], "sources": [ { "chunkId": "kubernetes/operator#relationship-to-the-gateway", "url": "/docs/kubernetes/operator#relationship-to-the-gateway", "anchor": "relationship-to-the-gateway" } ], "mode": "agent-primary", "terms": [ "relationship", "gateway", "operator", "decoupled", "neither", "sits", "other", "path", "keeps", "serving", "restarts", "lags", "link", "directional", "read", "only", "reads", "status", "inform", "serves", "never", "writes", "crds", "declarative", "state", "authored", "users", "reconciled", "reconciles", "write", "even", "restarted", "lagging", "between", "some", "features", "such", "indexes", "exist", "worker" ] }, { "id": "kubernetes/operator#scheduling-and-node-pools", "kind": "section", "title": "Operator Overview", "heading": "Scheduling and node pools", "group": "Operations", "url": "/docs/kubernetes/operator#scheduling-and-node-pools", "summary": "The operator applies the compute pool each Pipeline and Function chooses; a pool sets container resources, node selection, and tolerations so work is pinned to the right capacity. Built-in cpu, cpu-large, and gpu pools exist when no InfraRules/default is applied, selecting the chart-rendered Karpenter worker pools, with custom placement configured once on InfraRules/default rather than per workload.", "facts": [ { "kind": "code", "literal": "nodeSelector", "chunkId": "kubernetes/operator#scheduling-and-node-pools" }, { "kind": "code", "literal": "tolerations", "chunkId": "kubernetes/operator#scheduling-and-node-pools" }, { "kind": "code", "literal": "cpu", "chunkId": "kubernetes/operator#scheduling-and-node-pools" }, { "kind": "code", "literal": "cpu-large", "chunkId": "kubernetes/operator#scheduling-and-node-pools" }, { "kind": "code", "literal": "gpu", "chunkId": "kubernetes/operator#scheduling-and-node-pools" }, { "kind": "code", "literal": "InfraRules/default", "chunkId": "kubernetes/operator#scheduling-and-node-pools" }, { "kind": "code", "literal": "layer.hev.dev/node-role=worker-cpu", "chunkId": "kubernetes/operator#scheduling-and-node-pools" }, { "kind": "code", "literal": "layer.hev.dev/node-role=worker-gpu", "chunkId": "kubernetes/operator#scheduling-and-node-pools" }, { "kind": "code", "literal": "nvidia.com/gpu: \"1\"", "chunkId": "kubernetes/operator#scheduling-and-node-pools" } ], "sources": [ { "chunkId": "kubernetes/operator#scheduling-and-node-pools", "url": "/docs/kubernetes/operator#scheduling-and-node-pools", "anchor": "scheduling-and-node-pools" } ], "mode": "agent-primary", "terms": [ "scheduling", "node", "pools", "operator", "applies", "compute", "pool", "pipeline", "function", "chooses", "sets", "container", "resources", "selection", "tolerations", "work", "pinned", "right", "capacity", "built", "large", "exist", "infrarules", "default", "applied", "selecting", "chart", "rendered", "karpenter", "worker", "custom", "placement", "configured", "once", "rather", "workload", "nodeselector", "layer", "role", "nvidia" ] }, { "id": "kubernetes/pipeline-crd", "kind": "section", "title": "Pipeline CRD", "heading": null, "group": "Operations", "url": "/docs/kubernetes/pipeline-crd", "summary": "The Pipeline CRD declares the scaling characteristics for ingesting data, which typically runs in stages — a CPU stage for chunking and extraction, then a GPU stage for embedding. Specs can be authored in YAML, from code through the pipeline API, or both (scaling in YAML with the namespace set via client is recommended), and the source reference hands upstream details to the worker as configuration instead of hardcoding.", "facts": [ { "kind": "code", "literal": "Pipeline", "chunkId": "kubernetes/pipeline-crd" }, { "kind": "code", "literal": "spec.sourceRef", "chunkId": "kubernetes/pipeline-crd" } ], "sources": [ { "chunkId": "kubernetes/pipeline-crd", "url": "/docs/kubernetes/pipeline-crd", "anchor": null } ], "mode": "agent-primary", "terms": [ "pipeline", "declares", "scaling", "characteristics", "ingesting", "data", "typically", "runs", "stages", "stage", "chunking", "extraction", "embedding", "specs", "authored", "yaml", "code", "through", "both", "namespace", "client", "recommended", "source", "reference", "hands", "upstream", "details", "worker", "configuration", "instead", "hardcoding", "spec", "sourceref", "staged", "changing", "work", "declared", "kubernetes", "resource", "want" ] }, { "id": "kubernetes/pipeline-crd#chunking", "kind": "section", "title": "Pipeline CRD", "heading": "Chunking", "group": "Operations", "url": "/docs/kubernetes/pipeline-crd#chunking", "summary": "An optional chunk block declares how a source's whole-document text column is split before embedding, with stock strategies (none, fixed, recursive, sentence, markdown), a unit of tokens or characters, size and overlap, and a pinned tokenizer for reproducible boundaries. Each chunk is the unit indexed and embedded, written as a child row carrying the document's attributes plus reserved parent-id and chunk-index attributes; unsupported splits call for a custom chunker image.", "facts": [ { "kind": "code", "literal": "chunk", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "strategy", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "none", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "fixed", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "recursive", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "size", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "sentence", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "markdown", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "unit", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "tokens", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "characters", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "overlap", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "tokenizer", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "unit: tokens", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "text", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "{documentId}#{i}", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "_hevlayer_parent_id", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "_hevlayer_chunk_index", "chunkId": "kubernetes/pipeline-crd#chunking" }, { "kind": "code", "literal": "spec.worker.image", "chunkId": "kubernetes/pipeline-crd#chunking" } ], "sources": [ { "chunkId": "kubernetes/pipeline-crd#chunking", "url": "/docs/kubernetes/pipeline-crd#chunking", "anchor": "chunking" } ], "mode": "agent-primary", "terms": [ "chunking", "optional", "chunk", "block", "declares", "source", "whole", "document", "text", "column", "split", "before", "embedding", "stock", "strategies", "none", "fixed", "recursive", "sentence", "markdown", "unit", "tokens", "characters", "size", "overlap", "pinned", "tokenizer", "reproducible", "boundaries", "indexed", "embedded", "written", "child", "carrying", "attributes", "plus", "reserved", "parent", "index", "unsupported" ] }, { "id": "kubernetes/pipeline-crd#pipeline-id", "kind": "section", "title": "Pipeline CRD", "heading": "Pipeline id", "group": "Operations", "url": "/docs/kubernetes/pipeline-crd#pipeline-id", "summary": "The pipeline id names the gateway queue the worker stages into and scales on, defaulting to the resource name. Set it explicitly when multiple worker resources share one queue, such as the extract and embed stages of a two-stage pipeline.", "facts": [ { "kind": "code", "literal": "spec.pipelineId", "chunkId": "kubernetes/pipeline-crd#pipeline-id" }, { "kind": "code", "literal": "pipelineId: products", "chunkId": "kubernetes/pipeline-crd#pipeline-id" } ], "sources": [ { "chunkId": "kubernetes/pipeline-crd#pipeline-id", "url": "/docs/kubernetes/pipeline-crd#pipeline-id", "anchor": "pipeline-id" } ], "mode": "agent-primary", "terms": [ "pipeline", "names", "gateway", "queue", "worker", "stages", "scales", "defaulting", "resource", "name", "explicitly", "multiple", "resources", "share", "such", "extract", "embed", "stage", "spec", "pipelineid", "products", "defaults", "both" ] }, { "id": "kubernetes/pipeline-crd#scaling", "kind": "section", "title": "Pipeline CRD", "heading": "Scaling", "group": "Operations", "url": "/docs/kubernetes/pipeline-crd#scaling", "summary": "Pipeline scaling names a pool from the built-in set or InfraRules/default (falling back to the compute-class mapping when omitted), with autoscale mode creating a KEDA ScaledObject on queue depth or cron window, fixed pinning replicas, and disabled scaling to zero. A warm-window cooldown holds the worker warm after the queue drains, and pausing the spec also scales the worker to zero.", "facts": [ { "kind": "code", "literal": "scaling:\n pool: cpu\n mode: autoscale\n replicas:\n min: 0\n max: 8", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "spec.scaling.pool", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "InfraRules/default", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "worker.computeClass", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "cpu", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "gpu", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "cpu-large", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "InfraRules", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "mode: autoscale", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "ScaledObject", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "spec.schedule", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "mode: fixed", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "replicas.min", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "mode: disabled", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "spec.scaling.warmWindowSeconds", "chunkId": "kubernetes/pipeline-crd#scaling" }, { "kind": "code", "literal": "spec.paused: true", "chunkId": "kubernetes/pipeline-crd#scaling" } ], "sources": [ { "chunkId": "kubernetes/pipeline-crd#scaling", "url": "/docs/kubernetes/pipeline-crd#scaling", "anchor": "scaling" } ], "mode": "agent-primary", "terms": [ "scaling", "pipeline", "names", "pool", "built", "infrarules", "default", "falling", "back", "compute", "class", "mapping", "omitted", "autoscale", "mode", "creating", "keda", "scaledobject", "queue", "depth", "cron", "window", "fixed", "pinning", "replicas", "disabled", "zero", "warm", "cooldown", "holds", "worker", "after", "drains", "pausing", "spec", "also", "scales", "computeclass", "large", "schedule" ] }, { "id": "kubernetes/pipeline-crd#schedule", "kind": "section", "title": "Pipeline CRD", "heading": "Schedule", "group": "Operations", "url": "/docs/kubernetes/pipeline-crd#schedule", "summary": "An optional schedule wakes the Pipeline worker on a KEDA cron window instead of scaling on queue depth, with a lease duration sizing the window. The worker still owns source semantics (what to pull, cursor advancement, staging); scheduled Pipelines must set minimum replicas to zero since the cron window is the wake trigger.", "facts": [ { "kind": "code", "literal": "schedule:\n cron: \"0 2 * * *\" # 5-field UTC cron; minute must be a single integer\n leaseSeconds: 600 # sizes the cron window", "chunkId": "kubernetes/pipeline-crd#schedule" }, { "kind": "code", "literal": "spec.schedule", "chunkId": "kubernetes/pipeline-crd#schedule" }, { "kind": "code", "literal": "scaling.replicas.min: 0", "chunkId": "kubernetes/pipeline-crd#schedule" } ], "sources": [ { "chunkId": "kubernetes/pipeline-crd#schedule", "url": "/docs/kubernetes/pipeline-crd#schedule", "anchor": "schedule" } ], "mode": "agent-primary", "terms": [ "schedule", "optional", "wakes", "pipeline", "worker", "keda", "cron", "window", "instead", "scaling", "queue", "depth", "lease", "duration", "sizing", "still", "owns", "source", "semantics", "pull", "cursor", "advancement", "staging", "scheduled", "pipelines", "must", "minimum", "replicas", "zero", "since", "wake", "trigger", "field", "minute", "single", "integer", "leaseseconds", "sizes", "spec", "operator" ] }, { "id": "kubernetes/pipeline-crd#source", "kind": "section", "title": "Pipeline CRD", "heading": "Source", "group": "Operations", "url": "/docs/kubernetes/pipeline-crd#source", "summary": "The source reference declares the external source feeding the worker, with its kind selecting operator treatment. For open kinds — SQS, Kafka, S3 events, a partner API, a one-off migration — it is arbitrary JSON injected verbatim into the worker pod as an environment variable, and the worker image owns source-specific behavior.", "facts": [ { "kind": "code", "literal": "spec.sourceRef", "chunkId": "kubernetes/pipeline-crd#source" }, { "kind": "code", "literal": "kind", "chunkId": "kubernetes/pipeline-crd#source" }, { "kind": "code", "literal": "sourceRef", "chunkId": "kubernetes/pipeline-crd#source" }, { "kind": "code", "literal": "HEVLAYER_SOURCE_REF", "chunkId": "kubernetes/pipeline-crd#source" } ], "sources": [ { "chunkId": "kubernetes/pipeline-crd#source", "url": "/docs/kubernetes/pipeline-crd#source", "anchor": "source" } ], "mode": "agent-primary", "terms": [ "source", "reference", "declares", "external", "feeding", "worker", "kind", "selecting", "operator", "treatment", "open", "kinds", "kafka", "events", "partner", "migration", "arbitrary", "json", "injected", "verbatim", "environment", "variable", "image", "owns", "specific", "behavior", "spec", "sourceref", "hevlayer", "feeds", "selects", "treats", "hevlayersourceref", "extract", "chunk", "reading" ] }, { "id": "kubernetes/pipeline-crd#status", "kind": "section", "title": "Pipeline CRD", "heading": "Status", "group": "Operations", "url": "/docs/kubernetes/pipeline-crd#status", "summary": "Use the pipeline status API for queue counts, stage progress, and worker state. The resource itself reports only managed object references and readiness conditions.", "facts": [], "sources": [ { "chunkId": "kubernetes/pipeline-crd#status", "url": "/docs/kubernetes/pipeline-crd#status", "anchor": "status" } ], "mode": "agent-primary", "terms": [ "status", "pipeline", "queue", "counts", "stage", "progress", "worker", "state", "resource", "itself", "reports", "only", "managed", "object", "references", "readiness", "conditions" ] }, { "id": "kubernetes/pipeline-crd#target", "kind": "section", "title": "Pipeline CRD", "heading": "Target", "group": "Operations", "url": "/docs/kubernetes/pipeline-crd#target", "summary": "The target namespace is the turbopuffer namespace the pipeline writes. The gateway pipeline API owns document state, chunks, and vector writes for that target.", "facts": [ { "kind": "code", "literal": "spec.target.namespace", "chunkId": "kubernetes/pipeline-crd#target" } ], "sources": [ { "chunkId": "kubernetes/pipeline-crd#target", "url": "/docs/kubernetes/pipeline-crd#target", "anchor": "target" } ], "mode": "agent-primary", "terms": [ "target", "namespace", "turbopuffer", "pipeline", "writes", "gateway", "owns", "document", "state", "chunks", "vector", "spec" ] }, { "id": "kubernetes/pipeline-crd#typed-sources", "kind": "section", "title": "Pipeline CRD", "heading": "Typed sources", "group": "Operations", "url": "/docs/kubernetes/pipeline-crd#typed-sources", "summary": "For warehouse-backed kinds (snowflake, huggingface, rest) the source kind selects a typed shape the operator validates: it resolves the named Warehouse (which must be Verified), mounts its credential Secret, and injects connection details without credential material. The worker image becomes optional, defaulting to the stock worker for that kind from the mesh-account registry, so a typed source needs no custom image.", "facts": [ { "kind": "code", "literal": "snowflake", "chunkId": "kubernetes/pipeline-crd#typed-sources" }, { "kind": "code", "literal": "huggingface", "chunkId": "kubernetes/pipeline-crd#typed-sources" }, { "kind": "code", "literal": "rest", "chunkId": "kubernetes/pipeline-crd#typed-sources" }, { "kind": "code", "literal": "kind", "chunkId": "kubernetes/pipeline-crd#typed-sources" }, { "kind": "code", "literal": "Warehouse", "chunkId": "kubernetes/pipeline-crd#typed-sources" }, { "kind": "code", "literal": "warehouseRef", "chunkId": "kubernetes/pipeline-crd#typed-sources" }, { "kind": "code", "literal": "Verified", "chunkId": "kubernetes/pipeline-crd#typed-sources" }, { "kind": "code", "literal": "HEVLAYER_WAREHOUSE", "chunkId": "kubernetes/pipeline-crd#typed-sources" }, { "kind": "code", "literal": "spec.worker.image", "chunkId": "kubernetes/pipeline-crd#typed-sources" }, { "kind": "code", "literal": ".dkr.ecr.us-east-1.amazonaws.com/hev-huggingface-source", "chunkId": "kubernetes/pipeline-crd#typed-sources" }, { "kind": "code", "literal": ".dkr.ecr.us-east-1.amazonaws.com/hev-rest-source", "chunkId": "kubernetes/pipeline-crd#typed-sources" }, { "kind": "code", "literal": "worker.image", "chunkId": "kubernetes/pipeline-crd#typed-sources" } ], "sources": [ { "chunkId": "kubernetes/pipeline-crd#typed-sources", "url": "/docs/kubernetes/pipeline-crd#typed-sources", "anchor": "typed-sources" } ], "mode": "agent-primary", "terms": [ "typed", "sources", "warehouse", "backed", "kinds", "snowflake", "huggingface", "rest", "source", "kind", "selects", "shape", "operator", "validates", "resolves", "named", "must", "verified", "mounts", "credential", "secret", "injects", "connection", "details", "without", "material", "worker", "image", "becomes", "optional", "defaulting", "stock", "mesh", "account", "registry", "needs", "custom", "warehouseref", "hevlayer", "spec" ] }, { "id": "kubernetes/pipeline-crd#worker", "kind": "section", "title": "Pipeline CRD", "heading": "Worker", "group": "Operations", "url": "/docs/kubernetes/pipeline-crd#worker", "summary": "The worker block sets the image, compute class, batch size, timeout, and an optional pod merge patch. The operator creates one Deployment per Pipeline and injects pipeline, gateway, source, warehouse, and schedule environment; enumerated REST sources also receive a read-only ConfigMap values mount and snapshot object-store settings.", "facts": [ { "kind": "code", "literal": "image", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "computeClass", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "cpu", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "gpu", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "scaling.pool", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "batchSize", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "timeoutSeconds", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "podSpec", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_PIPELINE_ID", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "spec.pipelineId", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_TARGET_NAMESPACE", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "spec.target.namespace", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_BASE_URL", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_SOURCE_REF", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "spec.sourceRef", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_FOR_EACH_VALUES_PATH", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "request.forEach.values.ref", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "from: configMap", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_SNAPSHOT_BUCKET", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_SNAPSHOT_REGION", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_SNAPSHOT_ENDPOINT", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_PIPELINE_SCHEDULE", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "spec.schedule", "chunkId": "kubernetes/pipeline-crd#worker" }, { "kind": "code", "literal": "HEVLAYER_WAREHOUSE", "chunkId": "kubernetes/pipeline-crd#worker" } ], "sources": [ { "chunkId": "kubernetes/pipeline-crd#worker", "url": "/docs/kubernetes/pipeline-crd#worker", "anchor": "worker" } ], "mode": "agent-primary", "terms": [ "worker", "block", "sets", "image", "compute", "class", "batch", "size", "timeout", "optional", "merge", "patch", "operator", "creates", "deployment", "pipeline", "injects", "gateway", "source", "warehouse", "schedule", "environment", "enumerated", "rest", "sources", "also", "receive", "read", "only", "configmap", "values", "mount", "snapshot", "object", "store", "settings", "computeclass", "scaling", "pool", "batchsize" ] }, { "id": "kubernetes/scaling-crd", "kind": "section", "title": "InfraRules CRD", "heading": null, "group": "Operations", "url": "/docs/kubernetes/scaling-crd", "summary": "InfraRules is the cluster-scoped policy object for Layer-managed runtime infrastructure: Pipelines and Functions run on the built-in cpu, cpu-large, and gpu pools when no InfraRules/default exists, so a fresh install needs no infrastructure policy object. Apply the singleton to replace the built-in pools or declare document-cache policy; workloads set scaling inline and choose a pool by name.", "facts": [ { "kind": "code", "literal": "cpu", "chunkId": "kubernetes/scaling-crd" }, { "kind": "code", "literal": "cpu-large", "chunkId": "kubernetes/scaling-crd" }, { "kind": "code", "literal": "gpu", "chunkId": "kubernetes/scaling-crd" }, { "kind": "code", "literal": "InfraRules/default", "chunkId": "kubernetes/scaling-crd" }, { "kind": "code", "literal": "InfraRules", "chunkId": "kubernetes/scaling-crd" }, { "kind": "code", "literal": "spec.scaling", "chunkId": "kubernetes/scaling-crd" }, { "kind": "code", "literal": "InfraRules/default.spec.computePools", "chunkId": "kubernetes/scaling-crd" }, { "kind": "value", "literal": "Callout.astro", "chunkId": "kubernetes/scaling-crd" }, { "kind": "value", "literal": "FeatureGate.astro", "chunkId": "kubernetes/scaling-crd" } ], "sources": [ { "chunkId": "kubernetes/scaling-crd", "url": "/docs/kubernetes/scaling-crd", "anchor": null } ], "mode": "agent-primary", "terms": [ "infrarules", "cluster", "scoped", "policy", "object", "layer", "managed", "runtime", "infrastructure", "pipelines", "functions", "built", "large", "pools", "default", "exists", "fresh", "install", "needs", "apply", "singleton", "replace", "declare", "document", "cache", "workloads", "scaling", "inline", "choose", "pool", "name", "spec", "computepools", "callout", "astro", "featuregate", "wide", "compute", "rules", "workload" ] }, { "id": "kubernetes/scaling-crd#compute-pools", "kind": "section", "title": "InfraRules CRD", "heading": "Compute pools", "group": "Operations", "url": "/docs/kubernetes/scaling-crd#compute-pools", "summary": "The three built-in pools (cpu, cpu-large, gpu) select the Karpenter-backed worker nodes by node-role label, with the GPU pool requesting one NVIDIA GPU and carrying the standard toleration. Pool fields cover the referenced name, kind, optional GPU type, node selector, tolerations, container resources, and a hard per-workload replica ceiling; naming an unknown pool or exceeding the ceiling leaves the workload unready with a recorded condition.", "facts": [ { "kind": "code", "literal": "cpu", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "cpu-large", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "gpu", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "layer.hev.dev/node-role=worker-cpu", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "worker-gpu", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "nvidia.com/gpu: \"1\"", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "InfraRules/default", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "nodeSelector", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "gpuType", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "name", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "spec.scaling.pool", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "kind", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "tolerations", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "resources", "chunkId": "kubernetes/scaling-crd#compute-pools" }, { "kind": "code", "literal": "maxReplicasPerWorkload", "chunkId": "kubernetes/scaling-crd#compute-pools" } ], "sources": [ { "chunkId": "kubernetes/scaling-crd#compute-pools", "url": "/docs/kubernetes/scaling-crd#compute-pools", "anchor": "compute-pools" } ], "mode": "agent-primary", "terms": [ "compute", "pools", "three", "built", "large", "select", "karpenter", "backed", "worker", "nodes", "node", "role", "label", "pool", "requesting", "nvidia", "carrying", "standard", "toleration", "fields", "cover", "referenced", "name", "kind", "optional", "type", "selector", "tolerations", "container", "resources", "hard", "workload", "replica", "ceiling", "naming", "unknown", "exceeding", "leaves", "unready", "recorded" ] }, { "id": "kubernetes/scaling-crd#document-cache-rules", "kind": "section", "title": "InfraRules CRD", "heading": "Document cache rules", "group": "Operations", "url": "/docs/kubernetes/scaling-crd#document-cache-rules", "summary": "The document-cache block captures operator-owned cache settings: capacity, replication factor, and node count. Helm still renders the document-cache KEDA object directly; InfraRules is the declared policy shape the operator reports and validates against.", "facts": [ { "kind": "code", "literal": "documentCache", "chunkId": "kubernetes/scaling-crd#document-cache-rules" }, { "kind": "code", "literal": "InfraRules", "chunkId": "kubernetes/scaling-crd#document-cache-rules" } ], "sources": [ { "chunkId": "kubernetes/scaling-crd#document-cache-rules", "url": "/docs/kubernetes/scaling-crd#document-cache-rules", "anchor": "document-cache-rules" } ], "mode": "agent-primary", "terms": [ "document", "cache", "rules", "block", "captures", "operator", "owned", "settings", "capacity", "replication", "factor", "node", "count", "helm", "still", "renders", "keda", "object", "directly", "infrarules", "declared", "policy", "shape", "reports", "validates", "against", "documentcache" ] }, { "id": "kubernetes/scaling-crd#infrarules", "kind": "section", "title": "InfraRules CRD", "heading": "InfraRules", "group": "Operations", "url": "/docs/kubernetes/scaling-crd#infrarules", "summary": "The InfraRules example shows the full default pool set — cpu, cpu-large with ephemeral-storage headroom, and gpu with NVIDIA resources and tolerations — plus document-cache capacity and scaling policy. The operator validates that the object is named default.", "facts": [ { "kind": "code", "literal": "default", "chunkId": "kubernetes/scaling-crd#infrarules" } ], "sources": [ { "chunkId": "kubernetes/scaling-crd#infrarules", "url": "/docs/kubernetes/scaling-crd#infrarules", "anchor": "infrarules" } ], "mode": "agent-primary", "terms": [ "infrarules", "example", "shows", "full", "default", "pool", "large", "ephemeral", "storage", "headroom", "nvidia", "resources", "tolerations", "plus", "document", "cache", "capacity", "scaling", "policy", "operator", "validates", "object", "named", "apiversion", "hevlayer", "v1alpha1", "kind", "metadata", "name", "spec", "computepools", "nodeselector", "layer", "node", "role", "worker", "compute", "equal", "value", "effect" ] }, { "id": "kubernetes/scaling-crd#warm-window", "kind": "section", "title": "InfraRules CRD", "heading": "Warm window", "group": "Operations", "url": "/docs/kubernetes/scaling-crd#warm-window", "summary": "The warm window maps to the KEDA cooldown period (default 60 seconds) and additionally annotates worker pods so Karpenter retains the node, not just the replica, for the window. It targets scale-to-zero GPU pools where each wake otherwise pays a full cold start (fresh node claim, multi-GB image pull, model load), letting adjacent batches reuse one warm node before genuine scale-to-zero resumes; it requires autoscale mode and a non-negative value.", "facts": [ { "kind": "code", "literal": "warmWindowSeconds", "chunkId": "kubernetes/scaling-crd#warm-window" }, { "kind": "code", "literal": "ScaledObject", "chunkId": "kubernetes/scaling-crd#warm-window" }, { "kind": "code", "literal": "cooldownPeriod", "chunkId": "kubernetes/scaling-crd#warm-window" }, { "kind": "code", "literal": "replicas.min", "chunkId": "kubernetes/scaling-crd#warm-window" }, { "kind": "code", "literal": "60", "chunkId": "kubernetes/scaling-crd#warm-window" }, { "kind": "code", "literal": "karpenter.sh/do-not-disrupt", "chunkId": "kubernetes/scaling-crd#warm-window" }, { "kind": "code", "literal": ">= 0", "chunkId": "kubernetes/scaling-crd#warm-window" }, { "kind": "code", "literal": "mode: autoscale", "chunkId": "kubernetes/scaling-crd#warm-window" } ], "sources": [ { "chunkId": "kubernetes/scaling-crd#warm-window", "url": "/docs/kubernetes/scaling-crd#warm-window", "anchor": "warm-window" } ], "mode": "agent-primary", "terms": [ "warm", "window", "maps", "keda", "cooldown", "period", "default", "seconds", "additionally", "annotates", "worker", "pods", "karpenter", "retains", "node", "just", "replica", "targets", "scale", "zero", "pools", "wake", "otherwise", "pays", "full", "cold", "start", "fresh", "claim", "multi", "image", "pull", "model", "load", "letting", "adjacent", "batches", "reuse", "before", "genuine" ] }, { "id": "kubernetes/scaling-crd#workload-scaling", "kind": "section", "title": "InfraRules CRD", "heading": "Workload scaling", "group": "Operations", "url": "/docs/kubernetes/scaling-crd#workload-scaling", "summary": "Workload scaling modes are autoscale (KEDA ScaledObject driven by queue depth between min and max replicas), fixed (pin to min, no KEDA object), and disabled (scale to zero). The pool names a built-in or custom pool with the compute class as fallback, max replicas may not exceed the pool ceiling, paused workloads scale to zero, and cold-start-heavy workers stay warm with autoscale mode and a minimum of one replica.", "facts": [ { "kind": "code", "literal": "scaling:\n pool: gpu\n mode: autoscale\n warmWindowSeconds: 300\n replicas:\n min: 0\n max: 4", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "autoscale", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "ScaledObject", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "min", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "max", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "fixed", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "replicas.min", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "disabled", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "pool", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "InfraRules/default.spec.computePools", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "worker.computeClass", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "cpu", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "gpu", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "mode", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "replicas", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "maxReplicasPerWorkload", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "warmWindowSeconds", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "mode: autoscale", "chunkId": "kubernetes/scaling-crd#workload-scaling" }, { "kind": "code", "literal": "replicas.min: 1", "chunkId": "kubernetes/scaling-crd#workload-scaling" } ], "sources": [ { "chunkId": "kubernetes/scaling-crd#workload-scaling", "url": "/docs/kubernetes/scaling-crd#workload-scaling", "anchor": "workload-scaling" } ], "mode": "agent-primary", "terms": [ "workload", "scaling", "modes", "autoscale", "keda", "scaledobject", "driven", "queue", "depth", "between", "replicas", "fixed", "object", "disabled", "scale", "zero", "pool", "names", "built", "custom", "compute", "class", "fallback", "exceed", "ceiling", "paused", "workloads", "cold", "start", "heavy", "workers", "stay", "warm", "mode", "minimum", "replica", "warmwindowseconds", "infrarules", "default", "spec" ] }, { "id": "kubernetes/vectorstore-crd", "kind": "section", "title": "VectorStore CRD", "heading": null, "group": "Operations", "url": "/docs/kubernetes/vectorstore-crd", "summary": "A VectorStore is the gateway's upstream connection, naming the store kind, endpoint, credential Secret, and the inbound auth policy the gateway applies to client requests. An install may define multiple VectorStores, with each Index selecting which store serves its upstream namespace via a store reference.", "facts": [ { "kind": "code", "literal": "apiVersion: hevlayer.com/v1alpha1\nkind: VectorStore\nmetadata:\n name: turbopuffer-default\n namespace: layer\nspec:\n kind: turbopuffer\n default: true\n endpoint:\n url: https://aws-us-east-1.turbopuffer.com\n region: aws-us-east-1\n turbopuffer:\n orgId: org_123\n credential:\n secretRef:\n name: layer\n key: turbopuffer-api-key\n inboundAuth:\n mode: deriveFromStore", "chunkId": "kubernetes/vectorstore-crd" }, { "kind": "code", "literal": "VectorStore", "chunkId": "kubernetes/vectorstore-crd" }, { "kind": "code", "literal": "Index.spec.backend.storeRef", "chunkId": "kubernetes/vectorstore-crd" }, { "kind": "value", "literal": "FeatureGate.astro", "chunkId": "kubernetes/vectorstore-crd" }, { "kind": "value", "literal": "Callout.astro", "chunkId": "kubernetes/vectorstore-crd" } ], "sources": [ { "chunkId": "kubernetes/vectorstore-crd", "url": "/docs/kubernetes/vectorstore-crd", "anchor": null } ], "mode": "agent-primary", "terms": [ "vectorstore", "gateway", "upstream", "connection", "naming", "store", "kind", "endpoint", "credential", "secret", "inbound", "auth", "policy", "applies", "client", "requests", "install", "define", "multiple", "vectorstores", "index", "selecting", "serves", "namespace", "reference", "apiversion", "hevlayer", "v1alpha1", "metadata", "name", "turbopuffer", "default", "layer", "spec", "true", "https", "east", "region", "orgid", "secretref" ] }, { "id": "kubernetes/vectorstore-crd#connection", "kind": "section", "title": "VectorStore CRD", "heading": "Connection", "group": "Operations", "url": "/docs/kubernetes/vectorstore-crd#connection", "summary": "Connection fields cover the backend kind (turbopuffer today, with pinecone schema-reserved but rejected until implemented), a default flag for the store used when an Index omits a store reference, the endpoint URL and region label, an optional turbopuffer org id for dashboard deep links, and a credential Secret reference — the credential is never stored in the CRD.", "facts": [ { "kind": "code", "literal": "kind", "chunkId": "kubernetes/vectorstore-crd#connection" }, { "kind": "code", "literal": "turbopuffer", "chunkId": "kubernetes/vectorstore-crd#connection" }, { "kind": "code", "literal": "pinecone", "chunkId": "kubernetes/vectorstore-crd#connection" }, { "kind": "code", "literal": "default", "chunkId": "kubernetes/vectorstore-crd#connection" }, { "kind": "code", "literal": "Index", "chunkId": "kubernetes/vectorstore-crd#connection" }, { "kind": "code", "literal": "spec.backend.storeRef", "chunkId": "kubernetes/vectorstore-crd#connection" }, { "kind": "code", "literal": "endpoint.url", "chunkId": "kubernetes/vectorstore-crd#connection" }, { "kind": "code", "literal": "endpoint.region", "chunkId": "kubernetes/vectorstore-crd#connection" }, { "kind": "code", "literal": "turbopuffer.orgId", "chunkId": "kubernetes/vectorstore-crd#connection" }, { "kind": "code", "literal": "credential.secretRef", "chunkId": "kubernetes/vectorstore-crd#connection" }, { "kind": "code", "literal": "VectorStore", "chunkId": "kubernetes/vectorstore-crd#connection" } ], "sources": [ { "chunkId": "kubernetes/vectorstore-crd#connection", "url": "/docs/kubernetes/vectorstore-crd#connection", "anchor": "connection" } ], "mode": "agent-primary", "terms": [ "connection", "fields", "cover", "backend", "kind", "turbopuffer", "today", "pinecone", "schema", "reserved", "rejected", "until", "implemented", "default", "flag", "store", "index", "omits", "reference", "endpoint", "region", "label", "optional", "dashboard", "deep", "links", "credential", "secret", "never", "stored", "spec", "storeref", "orgid", "secretref", "vectorstore", "field", "purpose", "engine", "operator", "marks" ] }, { "id": "kubernetes/vectorstore-crd#inbound-auth", "kind": "section", "title": "VectorStore CRD", "heading": "Inbound auth", "group": "Operations", "url": "/docs/kubernetes/vectorstore-crd#inbound-auth", "summary": "Inbound auth modes are deriveFromStore (default: the store's own credential is the accepted bearer, the single-tenant BYOC shape), keys (independent key Secrets with read, write, and admin scopes enforced), and open (no inbound auth, for explicitly open environments). Read covers queries, fetches, scans, and metrics; write covers namespace writes and worker queue routes; admin covers Pipeline/Function control and satisfies the others; in every mode the gateway also accepts minted ApiKey tokens whose entitlement names the store.", "facts": [ { "kind": "code", "literal": "spec:\n inboundAuth:\n mode: keys\n keys:\n - name: shop-rw\n scopes: [read, write]\n secretRef:\n name: layer\n key: layer-inbound-shop-rw-api-key", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" }, { "kind": "code", "literal": "inboundAuth.mode", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" }, { "kind": "code", "literal": "deriveFromStore", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" }, { "kind": "code", "literal": "keys", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" }, { "kind": "code", "literal": "read", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" }, { "kind": "code", "literal": "write", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" }, { "kind": "code", "literal": "admin", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" }, { "kind": "code", "literal": "open", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" }, { "kind": "code", "literal": "Authorization: Bearer ", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" }, { "kind": "code", "literal": "LAYER_GATEWAY_API_KEY", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" }, { "kind": "code", "literal": "ApiKey", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" }, { "kind": "code", "literal": "vectorstore.", "chunkId": "kubernetes/vectorstore-crd#inbound-auth" } ], "sources": [ { "chunkId": "kubernetes/vectorstore-crd#inbound-auth", "url": "/docs/kubernetes/vectorstore-crd#inbound-auth", "anchor": "inbound-auth" } ], "mode": "agent-primary", "terms": [ "inbound", "auth", "modes", "derivefromstore", "default", "store", "credential", "accepted", "bearer", "single", "tenant", "byoc", "shape", "keys", "independent", "secrets", "read", "write", "admin", "scopes", "enforced", "open", "explicitly", "environments", "covers", "queries", "fetches", "scans", "metrics", "namespace", "writes", "worker", "queue", "routes", "pipeline", "function", "control", "satisfies", "others", "every" ] }, { "id": "kubernetes/vectorstore-crd#routing", "kind": "section", "title": "VectorStore CRD", "heading": "Routing", "group": "Operations", "url": "/docs/kubernetes/vectorstore-crd#routing", "summary": "The gateway builds one upstream client per VectorStore in the namespace: requests whose namespace has an Index with a store reference use that store, and other namespaces use the default. Two Index objects cannot resolve to the same upstream namespace.", "facts": [ { "kind": "code", "literal": "VectorStore", "chunkId": "kubernetes/vectorstore-crd#routing" }, { "kind": "code", "literal": "Index", "chunkId": "kubernetes/vectorstore-crd#routing" }, { "kind": "code", "literal": "spec.backend.storeRef", "chunkId": "kubernetes/vectorstore-crd#routing" } ], "sources": [ { "chunkId": "kubernetes/vectorstore-crd#routing", "url": "/docs/kubernetes/vectorstore-crd#routing", "anchor": "routing" } ], "mode": "agent-primary", "terms": [ "routing", "gateway", "builds", "upstream", "client", "vectorstore", "namespace", "requests", "whose", "index", "store", "reference", "other", "namespaces", "default", "objects", "cannot", "resolve", "same", "spec", "backend", "storeref" ] }, { "id": "kubernetes/vectorstore-crd#standalone-config", "kind": "section", "title": "VectorStore CRD", "heading": "Standalone config", "group": "Operations", "url": "/docs/kubernetes/vectorstore-crd#standalone-config", "summary": "Standalone and compose runs resolve VectorStores from a config file (or inline JSON environment variable) carrying the exact same resource shape as the CR, so a standalone config promotes to a cluster resource and back without rewrites — this exists because Layer CE runs without a Kubernetes control plane. Secret references resolve from environment variables named after the Secret name and key; with neither variable set, the gateway uses a single default turbopuffer store deriving inbound auth from the request bearer.", "facts": [ { "kind": "code", "literal": "apiVersion: hevlayer.com/v1alpha1\nkind: VectorStore\nmetadata:\n name: turbopuffer-default\nspec:\n kind: turbopuffer\n default: true\n endpoint:\n url: https://api.turbopuffer.com\n region: aws-us-east-1\n credential:\n secretRef:\n name: layer\n key: turbopuffer-api-key\n inboundAuth:\n mode: deriveFromStore", "chunkId": "kubernetes/vectorstore-crd#standalone-config" }, { "kind": "code", "literal": "layer", "chunkId": "kubernetes/vectorstore-crd#standalone-config" }, { "kind": "code", "literal": "VectorStore", "chunkId": "kubernetes/vectorstore-crd#standalone-config" }, { "kind": "code", "literal": "LAYER_STORE_FILE", "chunkId": "kubernetes/vectorstore-crd#standalone-config" }, { "kind": "code", "literal": "LAYER_SECRET_LAYER_TURBOPUFFER_API_KEY=tpuf_...", "chunkId": "kubernetes/vectorstore-crd#standalone-config" }, { "kind": "code", "literal": "LAYER_SECRET_", "chunkId": "kubernetes/vectorstore-crd#standalone-config" }, { "kind": "code", "literal": "credential.secretRef", "chunkId": "kubernetes/vectorstore-crd#standalone-config" }, { "kind": "code", "literal": "secretRef", "chunkId": "kubernetes/vectorstore-crd#standalone-config" }, { "kind": "code", "literal": "LAYER_STORE_JSON", "chunkId": "kubernetes/vectorstore-crd#standalone-config" }, { "kind": "value", "literal": "github.com", "chunkId": "kubernetes/vectorstore-crd#standalone-config" } ], "sources": [ { "chunkId": "kubernetes/vectorstore-crd#standalone-config", "url": "/docs/kubernetes/vectorstore-crd#standalone-config", "anchor": "standalone-config" } ], "mode": "agent-primary", "terms": [ "standalone", "config", "compose", "runs", "resolve", "vectorstores", "file", "inline", "json", "environment", "variable", "carrying", "exact", "same", "resource", "shape", "promotes", "cluster", "back", "without", "rewrites", "exists", "because", "layer", "kubernetes", "control", "plane", "secret", "references", "variables", "named", "after", "name", "neither", "gateway", "uses", "single", "default", "turbopuffer", "store" ] }, { "id": "kubernetes/vectorstore-crd#status", "kind": "section", "title": "VectorStore CRD", "heading": "Status", "group": "Operations", "url": "/docs/kubernetes/vectorstore-crd#status", "summary": "The operator sets a reachable flag and a Ready condition after validating the Secret references and probing the namespaces listing endpoint on the store.", "facts": [ { "kind": "code", "literal": "status.reachable", "chunkId": "kubernetes/vectorstore-crd#status" }, { "kind": "code", "literal": "Ready", "chunkId": "kubernetes/vectorstore-crd#status" }, { "kind": "code", "literal": "GET /v1/namespaces", "chunkId": "kubernetes/vectorstore-crd#status" } ], "sources": [ { "chunkId": "kubernetes/vectorstore-crd#status", "url": "/docs/kubernetes/vectorstore-crd#status", "anchor": "status" } ], "mode": "agent-primary", "terms": [ "status", "operator", "sets", "reachable", "flag", "ready", "condition", "after", "validating", "secret", "references", "probing", "namespaces", "listing", "endpoint", "store" ] }, { "id": "kubernetes/warehouse-crd", "kind": "section", "title": "Warehouse CRD", "heading": null, "group": "Operations", "url": "/docs/kubernetes/warehouse-crd", "summary": "A Warehouse declares an upstream source system — the system of record pipelines extract rows from — plus the verified reachability and credential shape needed to reach it. Data in Layer is derived from a warehouse and reconstructible from it; the serving side is the VectorStore, sitting on the opposite side of the gateway.", "facts": [ { "kind": "code", "literal": "Warehouse", "chunkId": "kubernetes/warehouse-crd" }, { "kind": "code", "literal": "VectorStore", "chunkId": "kubernetes/warehouse-crd" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd", "url": "/docs/kubernetes/warehouse-crd", "anchor": null } ], "mode": "agent-primary", "terms": [ "warehouse", "declares", "upstream", "source", "system", "record", "pipelines", "extract", "rows", "plus", "verified", "reachability", "credential", "shape", "needed", "reach", "data", "layer", "derived", "reconstructible", "serving", "side", "vectorstore", "sitting", "opposite", "gateway", "declared", "identity", "sides" ] }, { "id": "kubernetes/warehouse-crd#connection", "kind": "section", "title": "Warehouse CRD", "heading": "Connection", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#connection", "summary": "The kind selects the source system (snowflake, huggingface, rest shipped; databricks and iceberg schema-reserved but rejected), with exactly one matching kind block required and a verify interval (default one hour) setting probe cadence. A warehouse is identity and credential, not a catalog — what to read belongs to the pipeline source, so one credential reaches many tables or datasets.", "facts": [ { "kind": "code", "literal": "spec.kind", "chunkId": "kubernetes/warehouse-crd#connection" }, { "kind": "code", "literal": "snowflake", "chunkId": "kubernetes/warehouse-crd#connection" }, { "kind": "code", "literal": "huggingface", "chunkId": "kubernetes/warehouse-crd#connection" }, { "kind": "code", "literal": "rest", "chunkId": "kubernetes/warehouse-crd#connection" }, { "kind": "code", "literal": "databricks", "chunkId": "kubernetes/warehouse-crd#connection" }, { "kind": "code", "literal": "iceberg", "chunkId": "kubernetes/warehouse-crd#connection" }, { "kind": "code", "literal": "verifyInterval", "chunkId": "kubernetes/warehouse-crd#connection" }, { "kind": "code", "literal": "1h", "chunkId": "kubernetes/warehouse-crd#connection" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#connection", "url": "/docs/kubernetes/warehouse-crd#connection", "anchor": "connection" } ], "mode": "agent-primary", "terms": [ "connection", "kind", "selects", "source", "system", "snowflake", "huggingface", "rest", "shipped", "databricks", "iceberg", "schema", "reserved", "rejected", "exactly", "matching", "block", "required", "verify", "interval", "default", "hour", "setting", "probe", "cadence", "warehouse", "identity", "credential", "catalog", "read", "belongs", "pipeline", "reaches", "many", "tables", "datasets", "spec", "verifyinterval", "select", "operator" ] }, { "id": "kubernetes/warehouse-crd#deletion", "kind": "section", "title": "Warehouse CRD", "heading": "Deletion", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#deletion", "summary": "Deleting a warehouse fences everything drawing from it: a finalizer blocks deletion while consumers (extracting pipelines or entitled keys) are non-zero. A force-delete annotation overrides the finalizer.", "facts": [ { "kind": "code", "literal": "status.consumers", "chunkId": "kubernetes/warehouse-crd#deletion" }, { "kind": "code", "literal": "hevlayer.com/force-delete: \"true\"", "chunkId": "kubernetes/warehouse-crd#deletion" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#deletion", "url": "/docs/kubernetes/warehouse-crd#deletion", "anchor": "deletion" } ], "mode": "agent-primary", "terms": [ "deletion", "deleting", "warehouse", "fences", "everything", "drawing", "finalizer", "blocks", "while", "consumers", "extracting", "pipelines", "entitled", "keys", "zero", "force", "delete", "annotation", "overrides", "status", "hevlayer", "true", "annotate", "override" ] }, { "id": "kubernetes/warehouse-crd#hugging-face", "kind": "section", "title": "Warehouse CRD", "heading": "Hugging Face", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#hugging-face", "summary": "A Hugging Face pipeline source names the dataset repo, config, split, and an optional pinned revision, with a mapping choosing the indexed text column, document id column, and carried attributes; the stock source image streams rows from the dataset's Parquet conversion with the row offset as cursor. Omitting the revision pins to the current parquet-ref commit that the operator resolves and records, so long runs never drift onto a newer version mid-flight, and chunking is available as for any source.", "facts": [ { "kind": "code", "literal": "spec:\n sourceRef:\n kind: huggingface\n warehouseRef: huggingface-hub\n repo: McAuley-Lab/Amazon-Reviews-2023\n config: raw_meta_Electronics\n split: train\n revision: main\n cursor:\n field: parent_asin", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "mapping", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "text", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "id", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "{config}/{split}#{offset}", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "attributes", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "[]", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "spec.worker.image", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "HEVLAYER_WAREHOUSE", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "revision", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "chunk", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "sourceRef.kind", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "huggingface", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "warehouseRef", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "Verified", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "repo", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "tokenSecretRef", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "/var/run/hevlayer/warehouse/token", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "tokenPath", "chunkId": "kubernetes/warehouse-crd#hugging-face" }, { "kind": "code", "literal": "HEVLAYER_SOURCE_REF", "chunkId": "kubernetes/warehouse-crd#hugging-face" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#hugging-face", "url": "/docs/kubernetes/warehouse-crd#hugging-face", "anchor": "hugging-face" } ], "mode": "agent-primary", "terms": [ "hugging", "face", "pipeline", "source", "names", "dataset", "repo", "config", "split", "optional", "pinned", "revision", "mapping", "choosing", "indexed", "text", "column", "document", "carried", "attributes", "stock", "image", "streams", "rows", "parquet", "conversion", "offset", "cursor", "omitting", "pins", "current", "commit", "operator", "resolves", "records", "long", "runs", "never", "drift", "onto" ] }, { "id": "kubernetes/warehouse-crd#keys", "kind": "section", "title": "Warehouse CRD", "heading": "Keys", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#keys", "summary": "An ApiKey binds to a warehouse through a warehouse entitlement carrying opaque claims strings that Layer stores and echoes while the application routes on them. No client route reaches a source system — clients touch indexes, not warehouses — so the entitlement grants nothing in Layer and inerts when the warehouse is deleted.", "facts": [ { "kind": "code", "literal": "ApiKey", "chunkId": "kubernetes/warehouse-crd#keys" }, { "kind": "code", "literal": "warehouse.", "chunkId": "kubernetes/warehouse-crd#keys" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#keys", "url": "/docs/kubernetes/warehouse-crd#keys", "anchor": "keys" } ], "mode": "agent-primary", "terms": [ "keys", "apikey", "binds", "warehouse", "through", "entitlement", "carrying", "opaque", "claims", "strings", "layer", "stores", "echoes", "while", "application", "routes", "client", "route", "reaches", "source", "system", "clients", "touch", "indexes", "warehouses", "grants", "nothing", "inerts", "deleted", "name", "list" ] }, { "id": "kubernetes/warehouse-crd#pipeline-source", "kind": "section", "title": "Warehouse CRD", "heading": "Pipeline source", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#pipeline-source", "summary": "A pipeline extracting from a warehouse names it in its source reference: the source block owns the what (database and query, or dataset and mapping) while the warehouse owns the where and who. The operator requires the reference to name a Verified warehouse of matching kind in the same namespace and carries the source block verbatim to the worker.", "facts": [ { "kind": "code", "literal": "spec.sourceRef", "chunkId": "kubernetes/warehouse-crd#pipeline-source" }, { "kind": "code", "literal": "warehouseRef", "chunkId": "kubernetes/warehouse-crd#pipeline-source" }, { "kind": "code", "literal": "Verified", "chunkId": "kubernetes/warehouse-crd#pipeline-source" }, { "kind": "code", "literal": "HEVLAYER_SOURCE_REF", "chunkId": "kubernetes/warehouse-crd#pipeline-source" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#pipeline-source", "url": "/docs/kubernetes/warehouse-crd#pipeline-source", "anchor": "pipeline-source" } ], "mode": "agent-primary", "terms": [ "pipeline", "source", "extracting", "warehouse", "names", "reference", "block", "owns", "database", "query", "dataset", "mapping", "while", "operator", "requires", "name", "verified", "matching", "kind", "same", "namespace", "carries", "verbatim", "worker", "spec", "sourceref", "warehouseref", "hevlayer", "snowflake", "cursor", "hugging", "face", "split", "field", "hevlayersourceref", "other" ] }, { "id": "kubernetes/warehouse-crd#rest", "kind": "section", "title": "Warehouse CRD", "heading": "REST", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#rest", "summary": "A REST pipeline source declares a request path, static query params, offset pagination, a response JSONPath, a cursor field, and row mappings. request.forEach can enumerate ConfigMap-selected values into path, query, and mapping templates; full snapshots diff each forEach value independently, write configured tombstones only after a complete non-empty read, and preserve prior manifests on failed or empty partitions.", "facts": [ { "kind": "code", "literal": "request.path", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "request.query", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "baseUrl", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "pagination", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "kind: offset", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "pageSizeParam", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "offsetParam", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "pageSize", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "searchAfter", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "link", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "Link", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "response.items", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "cursor.field", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "mapping", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "id", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "text", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "attributes", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "request.forEach", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "select", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "ref.key", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "as", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "mapping.attributes", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "{{token}}", "chunkId": "kubernetes/warehouse-crd#rest" }, { "kind": "code", "literal": "forEach", "chunkId": "kubernetes/warehouse-crd#rest" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#rest", "url": "/docs/kubernetes/warehouse-crd#rest", "anchor": "rest" } ], "mode": "agent-primary", "terms": [ "rest", "pipeline", "source", "declares", "request", "path", "static", "query", "params", "offset", "pagination", "response", "jsonpath", "cursor", "field", "mappings", "foreach", "enumerate", "configmap", "selected", "values", "mapping", "templates", "full", "snapshots", "diff", "value", "independently", "write", "configured", "tombstones", "only", "after", "complete", "empty", "read", "preserve", "prior", "manifests", "failed" ] }, { "id": "kubernetes/warehouse-crd#rest--http-json-api", "kind": "section", "title": "Warehouse CRD", "heading": "REST / HTTP JSON API", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#rest--http-json-api", "summary": "A rest warehouse declares a JSON HTTP API: a required base URL, a contactable User-Agent, optional auth, an optional client-side rate cap, and a required reachability probe. The warehouse holds access only; request enumeration, endpoint, query, pagination, snapshot behavior, and field mapping belong to the pipeline source.", "facts": [ { "kind": "code", "literal": "rest", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "auth", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "Verified", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "rest.baseUrl", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "request.path", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "rest.userAgent", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "hevlayer/rest-source (+https://hevlayer.com/contact)", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "rest.auth", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "in", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "query", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "header", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "name", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "secretRef", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "token", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "rest.rateLimit", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "requestsPerSecond", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "rest.verify", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "path", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "code", "literal": "GET", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" }, { "kind": "flag", "literal": "-with-no-Secret", "chunkId": "kubernetes/warehouse-crd#rest--http-json-api" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#rest--http-json-api", "url": "/docs/kubernetes/warehouse-crd#rest--http-json-api", "anchor": "rest--http-json-api" } ], "mode": "agent-primary", "terms": [ "rest", "http", "json", "warehouse", "declares", "required", "base", "contactable", "user", "agent", "optional", "auth", "client", "side", "rate", "reachability", "probe", "holds", "access", "only", "request", "enumeration", "endpoint", "query", "pagination", "snapshot", "behavior", "field", "mapping", "belong", "pipeline", "source", "verified", "baseurl", "path", "useragent", "hevlayer", "https", "contact", "header" ] }, { "id": "kubernetes/warehouse-crd#rotation", "kind": "section", "title": "Warehouse CRD", "heading": "Rotation", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#rotation", "summary": "Rotate a credential by swapping the referenced Secret's content: the operator re-verifies, the verified timestamp advances, and consumers resolve credentials at connection-build time so new connections pick up the new key with no redeploy. Pointing the spec at a different Secret name, or adding/removing a token reference to move between anonymous and authenticated access, follows the same re-verify flow.", "facts": [ { "kind": "code", "literal": "status.verifiedAt", "chunkId": "kubernetes/warehouse-crd#rotation" }, { "kind": "code", "literal": "keyPairSecretRef", "chunkId": "kubernetes/warehouse-crd#rotation" }, { "kind": "code", "literal": "tokenSecretRef", "chunkId": "kubernetes/warehouse-crd#rotation" }, { "kind": "code", "literal": "huggingface", "chunkId": "kubernetes/warehouse-crd#rotation" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#rotation", "url": "/docs/kubernetes/warehouse-crd#rotation", "anchor": "rotation" } ], "mode": "agent-primary", "terms": [ "rotation", "rotate", "credential", "swapping", "referenced", "secret", "content", "operator", "verifies", "verified", "timestamp", "advances", "consumers", "resolve", "credentials", "connection", "build", "time", "connections", "pick", "redeploy", "pointing", "spec", "different", "name", "adding", "removing", "token", "reference", "move", "between", "anonymous", "authenticated", "access", "follows", "same", "verify", "flow", "status", "verifiedat" ] }, { "id": "kubernetes/warehouse-crd#snowflake", "kind": "section", "title": "Warehouse CRD", "heading": "Snowflake", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#snowflake", "summary": "The Snowflake warehouse fields cover the account identifier, service user, optional role, the compute warehouse extraction queries run on, a key-pair Secret reference (credential never stored in the CRD), and connection pool tuning; Hugging Face fields cover the Hub endpoint and an optional token Secret for gated datasets. A warehouse is source identity and credential, not a catalog — which database, table, or dataset to read belongs to the pipeline source.", "facts": [ { "kind": "code", "literal": "kind", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "snowflake", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "huggingface", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "rest", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "databricks", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "iceberg", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "snowflake.account", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "snowflake.user", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "snowflake.role", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "snowflake.warehouse", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "snowflake.keyPairSecretRef", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "private-key.pem", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "passphrase", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "snowflake.pool", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "size", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "timeout", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "huggingface.endpoint", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "https://huggingface.co", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "huggingface.tokenSecretRef", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "token", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "verifyInterval", "chunkId": "kubernetes/warehouse-crd#snowflake" }, { "kind": "code", "literal": "1h", "chunkId": "kubernetes/warehouse-crd#snowflake" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#snowflake", "url": "/docs/kubernetes/warehouse-crd#snowflake", "anchor": "snowflake" } ], "mode": "agent-primary", "terms": [ "snowflake", "warehouse", "fields", "cover", "account", "identifier", "service", "user", "optional", "role", "compute", "extraction", "queries", "pair", "secret", "reference", "credential", "never", "stored", "connection", "pool", "tuning", "hugging", "face", "endpoint", "token", "gated", "datasets", "source", "identity", "catalog", "database", "table", "dataset", "read", "belongs", "pipeline", "kind", "huggingface", "rest" ] }, { "id": "kubernetes/warehouse-crd#snowflake-1", "kind": "section", "title": "Warehouse CRD", "heading": "Snowflake", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#snowflake-1", "summary": "A Snowflake pipeline source names the warehouse, database, an extraction query with a cursor placeholder, and the cursor column. The operator mounts the warehouse's key-pair Secret into the worker pod and injects connection JSON resolved from the warehouse spec with no credential material; the worker builds its own connection from the two.", "facts": [ { "kind": "code", "literal": "spec:\n sourceRef:\n kind: snowflake\n warehouseRef: prod-snowflake\n database: ANALYTICS\n query: >-\n SELECT ID, TITLE, BODY, REFRESH_ID FROM PUBLIC.NOTES\n WHERE REFRESH_ID > :cursor\n cursor:\n column: REFRESH_ID", "chunkId": "kubernetes/warehouse-crd#snowflake-1" }, { "kind": "code", "literal": "/var/run/hevlayer/warehouse/", "chunkId": "kubernetes/warehouse-crd#snowflake-1" }, { "kind": "code", "literal": "HEVLAYER_WAREHOUSE", "chunkId": "kubernetes/warehouse-crd#snowflake-1" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#snowflake-1", "url": "/docs/kubernetes/warehouse-crd#snowflake-1", "anchor": "snowflake-1" } ], "mode": "agent-primary", "terms": [ "snowflake", "pipeline", "source", "names", "warehouse", "database", "extraction", "query", "cursor", "placeholder", "column", "operator", "mounts", "pair", "secret", "worker", "injects", "connection", "json", "resolved", "spec", "credential", "material", "builds", "sourceref", "kind", "warehouseref", "prod", "analytics", "select", "title", "body", "refresh", "public", "notes", "hevlayer", "refreshid", "hevlayerwarehouse", "account", "user" ] }, { "id": "kubernetes/warehouse-crd#status", "kind": "section", "title": "Warehouse CRD", "heading": "Status", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#status", "summary": "Warehouse status reports the phase, verification timestamp, failure reason, resolved source revisions (dataset commit SHAs), and consumer counts for pipelines and API keys. The operator emits Kubernetes Events on phase transitions.", "facts": [ { "kind": "code", "literal": "status:\n phase: Verified\n verifiedAt: \"2026-06-10T00:00:00Z\"\n failureReason: null\n sourceRevisions:\n McAuley-Lab/Amazon-Reviews-2023@main: 2b6d039ed471f2ba5fd2acb718bf33b0a7e5598e\n consumers:\n pipelines: 2\n apiKeys: 1", "chunkId": "kubernetes/warehouse-crd#status" }, { "kind": "code", "literal": "status.consumers", "chunkId": "kubernetes/warehouse-crd#status" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#status", "url": "/docs/kubernetes/warehouse-crd#status", "anchor": "status" } ], "mode": "agent-primary", "terms": [ "status", "warehouse", "reports", "phase", "verification", "timestamp", "failure", "reason", "resolved", "source", "revisions", "dataset", "commit", "shas", "consumer", "counts", "pipelines", "keys", "operator", "emits", "kubernetes", "events", "transitions", "verified", "verifiedat", "2026", "10t00", "failurereason", "null", "sourcerevisions", "mcauley", "amazon", "reviews", "2023", "main", "2b6d039ed471f2ba5fd2acb718bf33b0a7e5598e", "consumers", "apikeys", "observed", "references" ] }, { "id": "kubernetes/warehouse-crd#supported-warehouses", "kind": "section", "title": "Warehouse CRD", "heading": "Supported Warehouses", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#supported-warehouses", "summary": "Shipped kinds are snowflake (databases and tables with a key-pair credential), huggingface (Hub datasets, public or token-gated), and rest (any paginated JSON HTTP API via base URL, optional auth, and a pagination rule); databricks and iceberg are schema-reserved but rejected until implemented. Example resources show each shipped kind's shape.", "facts": [ { "kind": "code", "literal": "apiVersion: hevlayer.com/v1alpha1\nkind: Warehouse\nmetadata:\n name: prod-snowflake\n namespace: layer\nspec:\n kind: snowflake\n snowflake:\n account: acme-xy12345\n user: SVC_LAYER\n role: SVC_LAYER_ROLE\n warehouse: EXTRACT_WH\n keyPairSecretRef:\n name: snowflake-rsa\n pool:\n size: 5\n timeout: 30s\n verifyInterval: 1h", "chunkId": "kubernetes/warehouse-crd#supported-warehouses" }, { "kind": "code", "literal": "apiVersion: hevlayer.com/v1alpha1\nkind: Warehouse\nmetadata:\n name: huggingface-hub\n namespace: hev-shop\nspec:\n kind: huggingface\n huggingface:\n endpoint: https://huggingface.co\n # tokenSecretRef is optional for public datasets.\n # tokenSecretRef:\n # name: hf-read # key: token\n verifyInterval: 1h", "chunkId": "kubernetes/warehouse-crd#supported-warehouses" }, { "kind": "code", "literal": "snowflake", "chunkId": "kubernetes/warehouse-crd#supported-warehouses" }, { "kind": "code", "literal": "huggingface", "chunkId": "kubernetes/warehouse-crd#supported-warehouses" }, { "kind": "code", "literal": "rest", "chunkId": "kubernetes/warehouse-crd#supported-warehouses" }, { "kind": "code", "literal": "databricks", "chunkId": "kubernetes/warehouse-crd#supported-warehouses" }, { "kind": "code", "literal": "iceberg", "chunkId": "kubernetes/warehouse-crd#supported-warehouses" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#supported-warehouses", "url": "/docs/kubernetes/warehouse-crd#supported-warehouses", "anchor": "supported-warehouses" } ], "mode": "agent-primary", "terms": [ "supported", "warehouses", "shipped", "kinds", "snowflake", "databases", "tables", "pair", "credential", "huggingface", "datasets", "public", "token", "gated", "rest", "paginated", "json", "http", "base", "optional", "auth", "pagination", "rule", "databricks", "iceberg", "schema", "reserved", "rejected", "until", "implemented", "example", "resources", "show", "kind", "shape", "apiversion", "hevlayer", "v1alpha1", "warehouse", "metadata" ] }, { "id": "kubernetes/warehouse-crd#verification", "kind": "section", "title": "Warehouse CRD", "heading": "Verification", "group": "Operations", "url": "/docs/kubernetes/warehouse-crd#verification", "summary": "The operator probes a warehouse on apply, on referenced-Secret content change, and every verify interval: Snowflake opens a key-pair session and runs a trivial query, Hugging Face calls the Hub dataset API and resolves pinned revisions for consuming pipelines, and REST issues a single GET of the declared verify path where any 2xx confirms reachability and credential acceptance. Phases are Pending, Verified, and Failed; Failed is a loud signal, not an outage — in-flight runs keep their connections while new runs refuse to start, and pipelines start only against a Verified warehouse.", "facts": [ { "kind": "code", "literal": "verifyInterval", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "snowflake", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "SELECT 1", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "huggingface", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "repo@revision", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "status.sourceRevisions", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "rest", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "GET", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "rest.verify.path", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "query", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "auth", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "2xx", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "Verified", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "Pending", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "status.verifiedAt", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "Failed", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "status.failureReason", "chunkId": "kubernetes/warehouse-crd#verification" }, { "kind": "code", "literal": "kubectl get warehouse", "chunkId": "kubernetes/warehouse-crd#verification" } ], "sources": [ { "chunkId": "kubernetes/warehouse-crd#verification", "url": "/docs/kubernetes/warehouse-crd#verification", "anchor": "verification" } ], "mode": "agent-primary", "terms": [ "verification", "operator", "probes", "warehouse", "apply", "referenced", "secret", "content", "change", "every", "verify", "interval", "snowflake", "opens", "pair", "session", "runs", "trivial", "query", "hugging", "face", "calls", "dataset", "resolves", "pinned", "revisions", "consuming", "pipelines", "rest", "issues", "single", "declared", "path", "confirms", "reachability", "credential", "acceptance", "phases", "pending", "verified" ] }, { "id": "licensing", "kind": "section", "title": "Licensing", "heading": null, "group": "Overview", "url": "/docs/licensing", "summary": "hev layer is licensed per environment, with design-partner and trial installs using the same path as production: sign up, receive a key by email, install it with Helm, and monitor the gateway's local license state. Tier prices and inclusions live on the pricing page.", "facts": [ { "kind": "value", "literal": "FeatureGate.astro", "chunkId": "licensing" } ], "sources": [ { "chunkId": "licensing", "url": "/docs/licensing", "anchor": null } ], "mode": "agent-primary", "terms": [ "layer", "licensed", "environment", "design", "partner", "trial", "installs", "same", "path", "production", "sign", "receive", "email", "install", "helm", "monitor", "gateway", "local", "license", "state", "tier", "prices", "inclusions", "live", "pricing", "page", "featuregate", "astro", "commercial", "includes" ] }, { "id": "licensing#client-sdks", "kind": "section", "title": "Licensing", "heading": "Client SDKs", "group": "Overview", "url": "/docs/licensing#client-sdks", "summary": "The generated client SDKs stay Apache-2.0 — pure onramps to the public API surface, intended to be easy to adopt in any application.", "facts": [ { "kind": "value", "literal": "Apache-2", "chunkId": "licensing#client-sdks" }, { "kind": "value", "literal": "Apache-2.0", "chunkId": "licensing#client-sdks" }, { "kind": "value", "literal": "2.0", "chunkId": "licensing#client-sdks" } ], "sources": [ { "chunkId": "licensing#client-sdks", "url": "/docs/licensing#client-sdks", "anchor": "client-sdks" } ], "mode": "agent-primary", "terms": [ "client", "sdks", "generated", "stay", "apache", "pure", "onramps", "public", "surface", "intended", "easy", "adopt", "application" ] }, { "id": "licensing#end-to-end-runbook", "kind": "section", "title": "Licensing", "heading": "End-to-End Runbook", "group": "Overview", "url": "/docs/licensing#end-to-end-runbook", "summary": "The full-fidelity license acceptance run is scripted: it uses a per-run plus-addressed inbox, stands up a disposable kind cluster, installs the extracted key, checks the license endpoint, and gates each leg independently, never running the grace/floor walk against the shared cluster. No-key and trial installs get no post-expiration grace — the trial period is the evaluation window — while commercial grace is a lapse cushion measured from the real expiration.", "facts": [ { "kind": "code", "literal": "scripts/license-e2e-real.sh", "chunkId": "licensing#end-to-end-runbook" }, { "kind": "code", "literal": "infra/helm/layer", "chunkId": "licensing#end-to-end-runbook" }, { "kind": "code", "literal": "license.token", "chunkId": "licensing#end-to-end-runbook" }, { "kind": "code", "literal": "GET /v2/license", "chunkId": "licensing#end-to-end-runbook" }, { "kind": "code", "literal": "exp", "chunkId": "licensing#end-to-end-runbook" } ], "sources": [ { "chunkId": "licensing#end-to-end-runbook", "url": "/docs/licensing#end-to-end-runbook", "anchor": "end-to-end-runbook" } ], "mode": "agent-primary", "terms": [ "runbook", "full", "fidelity", "license", "acceptance", "scripted", "uses", "plus", "addressed", "inbox", "stands", "disposable", "kind", "cluster", "installs", "extracted", "checks", "endpoint", "gates", "independently", "never", "running", "grace", "floor", "walk", "against", "shared", "trial", "post", "expiration", "period", "evaluation", "window", "while", "commercial", "lapse", "cushion", "measured", "real", "scripts" ] }, { "id": "licensing#governing-terms", "kind": "section", "title": "Licensing", "heading": "Governing Terms", "group": "Overview", "url": "/docs/licensing#governing-terms", "summary": "The trademark and brand policy governs use of the Layer names and marks, the hosted-service terms govern the operated service, and the privacy policy governs data handling. Self-hosting is governed by the gateway license and trademark policy; the hosted service by the hosted terms and privacy policy.", "facts": [ { "kind": "code", "literal": "TRADEMARKS.md", "chunkId": "licensing#governing-terms" }, { "kind": "value", "literal": "github.com", "chunkId": "licensing#governing-terms" }, { "kind": "value", "literal": "hevlayer.com", "chunkId": "licensing#governing-terms" } ], "sources": [ { "chunkId": "licensing#governing-terms", "url": "/docs/licensing#governing-terms", "anchor": "governing-terms" } ], "mode": "agent-primary", "terms": [ "governing", "terms", "trademark", "brand", "policy", "governs", "layer", "names", "marks", "hosted", "service", "govern", "operated", "privacy", "data", "handling", "self", "hosting", "governed", "gateway", "license", "trademarks", "github", "hevlayer", "document", "product", "identity", "including", "acceptable", "billing", "liability", "support", "site", "telemetry" ] }, { "id": "licensing#install-the-key", "kind": "section", "title": "Licensing", "heading": "Install the Key", "group": "Overview", "url": "/docs/licensing#install-the-key", "summary": "For Helm installs, pass the emailed key as the license token value; clusters requiring pre-created Secrets can point Helm at an existing Secret name and key instead. The install guide has the full values table.", "facts": [ { "kind": "code", "literal": "helm upgrade --install layer ./infra/helm/layer \\\n --namespace layer --create-namespace \\\n --set license.token=\"$HEVLAYER_LICENSE\" \\\n -f values.customer.yaml", "chunkId": "licensing#install-the-key" }, { "kind": "code", "literal": "license.token", "chunkId": "licensing#install-the-key" }, { "kind": "code", "literal": "license.existingSecret", "chunkId": "licensing#install-the-key" }, { "kind": "code", "literal": "license.secretKey", "chunkId": "licensing#install-the-key" } ], "sources": [ { "chunkId": "licensing#install-the-key", "url": "/docs/licensing#install-the-key", "anchor": "install-the-key" } ], "mode": "agent-primary", "terms": [ "install", "helm", "installs", "pass", "emailed", "license", "token", "value", "clusters", "requiring", "created", "secrets", "point", "existing", "secret", "name", "instead", "guide", "full", "values", "table", "upgrade", "layer", "infra", "namespace", "create", "hevlayer", "customer", "yaml", "existingsecret", "secretkey", "hevlayerlicense", "cluster", "policy", "requires", "kubernetes", "through", "normal", "workflow" ] }, { "id": "licensing#license-claims", "kind": "section", "title": "Licensing", "heading": "License Claims", "group": "Overview", "url": "/docs/licensing#license-claims", "summary": "The license key is a signed PASETO v4.public value verified offline by the gateway, operator, and dashboard — no license-server callback. It answers what the deployment is entitled to (subject, tier, features, limits, issue and expiry times) while API keys answer who the caller is; feature strings gate the transform runtime, agents, RBAC, warehouses, document cache, history, and cost surfaces, but core gateway routes (queries, reads, writes, scans, snapshots, routing) remain available in every license state.", "facts": [ { "kind": "code", "literal": "v4.public", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "sub", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "tier", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "features", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "limits", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "iat", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "exp", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "transform-runtime", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "agents", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "rbac", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "warehouses", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "doc-cache", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "history", "chunkId": "licensing#license-claims" }, { "kind": "code", "literal": "cost", "chunkId": "licensing#license-claims" } ], "sources": [ { "chunkId": "licensing#license-claims", "url": "/docs/licensing#license-claims", "anchor": "license-claims" } ], "mode": "agent-primary", "terms": [ "license", "claims", "signed", "paseto", "public", "value", "verified", "offline", "gateway", "operator", "dashboard", "server", "callback", "answers", "deployment", "entitled", "subject", "tier", "features", "limits", "issue", "expiry", "times", "while", "keys", "answer", "caller", "feature", "strings", "gate", "transform", "runtime", "agents", "rbac", "warehouses", "document", "cache", "history", "cost", "surfaces" ] }, { "id": "licensing#license-states", "kind": "section", "title": "Licensing", "heading": "License States", "group": "Overview", "url": "/docs/licensing#license-states", "summary": "The gateway evaluates license state locally: licensed means a valid unexpired key, grace means a commercial key expired but still inside its grace window (trial keys never enter grace), and floor means no valid key so licensed surfaces refuse or degrade to CE behavior. A gateway endpoint reports validity, subject, tier, features, limits, expiration, and the current projection, with operator and dashboard enforcement using the same model.", "facts": [ { "kind": "code", "literal": "curl -H \"Authorization: Bearer $LAYER_API_KEY\" \\\n https:///v2/license", "chunkId": "licensing#license-states" }, { "kind": "code", "literal": "licensed", "chunkId": "licensing#license-states" }, { "kind": "code", "literal": "grace", "chunkId": "licensing#license-states" }, { "kind": "code", "literal": "floor", "chunkId": "licensing#license-states" } ], "sources": [ { "chunkId": "licensing#license-states", "url": "/docs/licensing#license-states", "anchor": "license-states" } ], "mode": "agent-primary", "terms": [ "license", "states", "gateway", "evaluates", "state", "locally", "licensed", "means", "valid", "unexpired", "grace", "commercial", "expired", "still", "inside", "window", "trial", "keys", "never", "enter", "floor", "surfaces", "refuse", "degrade", "behavior", "endpoint", "reports", "validity", "subject", "tier", "features", "limits", "expiration", "current", "projection", "operator", "dashboard", "enforcement", "same", "model" ] }, { "id": "licensing#metrics", "kind": "section", "title": "Licensing", "heading": "Metrics", "group": "Overview", "url": "/docs/licensing#metrics", "summary": "License health exports with the gateway metrics: gauges for key validity, seconds to expiry, grace seconds remaining, and degraded state, plus a counter of requests allowed during grace, labeled by tier and subject. Useful alerts are expiry within 14 days, any grace requests, and the degraded gauge going to one.", "facts": [ { "kind": "code", "literal": "license_tier", "chunkId": "licensing#metrics" }, { "kind": "code", "literal": "license_sub", "chunkId": "licensing#metrics" }, { "kind": "code", "literal": "surface", "chunkId": "licensing#metrics" }, { "kind": "code", "literal": "surface=\"gateway\"", "chunkId": "licensing#metrics" }, { "kind": "code", "literal": "hevlayer_license_valid", "chunkId": "licensing#metrics" }, { "kind": "code", "literal": "hevlayer_license_expiry_seconds", "chunkId": "licensing#metrics" }, { "kind": "code", "literal": "exp", "chunkId": "licensing#metrics" }, { "kind": "code", "literal": "hevlayer_license_grace_seconds_remaining", "chunkId": "licensing#metrics" }, { "kind": "code", "literal": "hevlayer_license_degraded", "chunkId": "licensing#metrics" }, { "kind": "code", "literal": "hevlayer_license_grace_requests_total", "chunkId": "licensing#metrics" }, { "kind": "code", "literal": "hevlayer_license_degraded == 1", "chunkId": "licensing#metrics" } ], "sources": [ { "chunkId": "licensing#metrics", "url": "/docs/licensing#metrics", "anchor": "metrics" } ], "mode": "agent-primary", "terms": [ "metrics", "license", "health", "exports", "gateway", "gauges", "validity", "seconds", "expiry", "grace", "remaining", "degraded", "state", "plus", "counter", "requests", "allowed", "during", "labeled", "tier", "subject", "useful", "alerts", "within", "days", "gauge", "going", "surface", "hevlayer", "valid", "total", "exported", "uses", "licensetier", "licensesub", "labels", "also", "include", "phase", "emits" ] }, { "id": "licensing#renewals", "kind": "section", "title": "Licensing", "heading": "Renewals", "group": "Overview", "url": "/docs/licensing#renewals", "summary": "Near expiration, request a renewal through your hev layer contact or by replying to the license email. Replace the key in Helm or the referenced Secret and roll the gateway release so it reads the new value.", "facts": [], "sources": [ { "chunkId": "licensing#renewals", "url": "/docs/licensing#renewals", "anchor": "renewals" } ], "mode": "agent-primary", "terms": [ "renewals", "near", "expiration", "request", "renewal", "through", "layer", "contact", "replying", "license", "email", "replace", "helm", "referenced", "secret", "roll", "gateway", "release", "reads", "value", "trial", "commercial", "close", "reply" ] }, { "id": "licensing#start-a-trial", "kind": "section", "title": "Licensing", "heading": "Start a Trial", "group": "Overview", "url": "/docs/licensing#start-a-trial", "summary": "Trial signup on the hev layer site emails back a signed trial key and install instructions — the only license material a standard self-hosted install needs, to be treated as a deployment secret. Self-hosted installs pull the pro images from Docker Hub, which are only useful with a valid key; without one, licensed surfaces project to the license floor.", "facts": [ { "kind": "code", "literal": "hevlayer/layer-gateway-pro:", "chunkId": "licensing#start-a-trial" }, { "kind": "code", "literal": "hevlayer/layer-operator:", "chunkId": "licensing#start-a-trial" }, { "kind": "code", "literal": "hevlayer/layer-dashboard:", "chunkId": "licensing#start-a-trial" } ], "sources": [ { "chunkId": "licensing#start-a-trial", "url": "/docs/licensing#start-a-trial", "anchor": "start-a-trial" } ], "mode": "agent-primary", "terms": [ "start", "trial", "signup", "layer", "site", "emails", "back", "signed", "install", "instructions", "only", "license", "material", "standard", "self", "hosted", "needs", "treated", "deployment", "secret", "installs", "pull", "images", "docker", "useful", "valid", "without", "licensed", "surfaces", "project", "floor", "hevlayer", "gateway", "version", "operator", "dashboard", "submit", "email", "address", "want" ] }, { "id": "limits", "kind": "section", "title": "Limits", "heading": null, "group": "Overview", "url": "/docs/limits", "summary": "Current ceilings inherited from shipped components: a single-node document cache, 4,090 turbopuffer namespaces and a 3 TB cache size (both Aerospike Community Edition license limits), 10,000 distinct values per snapshot-scan facet field (over-cap fields are listed as skipped so emitted fields are always complete), and 1,000,000 distinct values per on-demand values scan (over-cap jobs complete truncated, keeping top values by exact count).", "facts": [ { "kind": "code", "literal": "fields_skipped[]", "chunkId": "limits" }, { "kind": "code", "literal": "fields[]", "chunkId": "limits" }, { "kind": "code", "literal": "truncated: true", "chunkId": "limits" } ], "sources": [ { "chunkId": "limits", "url": "/docs/limits", "anchor": null } ], "mode": "agent-primary", "terms": [ "current", "ceilings", "inherited", "shipped", "components", "single", "node", "document", "cache", "turbopuffer", "namespaces", "size", "both", "aerospike", "community", "edition", "license", "limits", "distinct", "values", "snapshot", "scan", "facet", "field", "fields", "listed", "skipped", "emitted", "always", "complete", "demand", "jobs", "truncated", "keeping", "exact", "count", "true", "ship", "layer", "limited" ] }, { "id": "limits#no-limits", "kind": "section", "title": "Limits", "heading": "No limits", "group": "Overview", "url": "/docs/limits#no-limits", "summary": "No enforced ceiling exists on CRD instance counts (bounded by etcd and operator throughput), snapshot and search history and clickstream volume in S3, UDF concurrency (KEDA-scaled to cluster capacity), pipeline queue depth (manifests in S3, only segment state in Postgres), or document size and attribute count (bounded by turbopuffer and Aerospike record limits). Practical limits show up under load; the failure-modes page covers how each surface degrades.", "facts": [ { "kind": "code", "literal": "Index", "chunkId": "limits#no-limits" }, { "kind": "code", "literal": "Function", "chunkId": "limits#no-limits" }, { "kind": "code", "literal": "Pipeline", "chunkId": "limits#no-limits" }, { "kind": "code", "literal": "Scaling", "chunkId": "limits#no-limits" }, { "kind": "code", "literal": "spec.snapshot.retention", "chunkId": "limits#no-limits" }, { "kind": "code", "literal": "retention: never", "chunkId": "limits#no-limits" } ], "sources": [ { "chunkId": "limits#no-limits", "url": "/docs/limits#no-limits", "anchor": "no-limits" } ], "mode": "agent-primary", "terms": [ "limits", "enforced", "ceiling", "exists", "instance", "counts", "bounded", "etcd", "operator", "throughput", "snapshot", "search", "history", "clickstream", "volume", "concurrency", "keda", "scaled", "cluster", "capacity", "pipeline", "queue", "depth", "manifests", "only", "segment", "state", "postgres", "document", "size", "attribute", "count", "turbopuffer", "aerospike", "record", "practical", "show", "under", "load", "failure" ] }, { "id": "quickstart", "kind": "section", "title": "Quickstart", "heading": null, "group": "Operations", "url": "/docs/quickstart", "summary": "Run the community gateway in front of your existing turbopuffer account with Docker Compose — no license key or signup, your turbopuffer API key is the gateway bearer, and your data stays where it is. Prerequisites are Docker, curl, and a turbopuffer namespace with rows in it.", "facts": [ { "kind": "code", "literal": "curl", "chunkId": "quickstart" } ], "sources": [ { "chunkId": "quickstart", "url": "/docs/quickstart", "anchor": null } ], "mode": "agent-primary", "terms": [ "community", "gateway", "front", "existing", "turbopuffer", "account", "docker", "compose", "license", "signup", "bearer", "data", "stays", "prerequisites", "curl", "namespace", "rows", "clone", "repo", "initialize", "query", "needed", "token", "need" ] }, { "id": "quickstart#1-clone-the-repo", "kind": "section", "title": "Quickstart", "heading": "1. Clone the repo", "group": "Operations", "url": "/docs/quickstart#1-clone-the-repo", "summary": "Step one clones the public hev/layer repository and changes into the checkout.", "facts": [ { "kind": "code", "literal": "git clone https://github.com/hev/layer.git\ncd layer", "chunkId": "quickstart#1-clone-the-repo" } ], "sources": [ { "chunkId": "quickstart#1-clone-the-repo", "url": "/docs/quickstart#1-clone-the-repo", "anchor": "1-clone-the-repo" } ], "mode": "agent-primary", "terms": [ "clone", "repo", "step", "clones", "public", "layer", "repository", "changes", "checkout", "https", "github" ] }, { "id": "quickstart#2-configure-and-start-the-gateway", "kind": "section", "title": "Quickstart", "heading": "2. Configure and start the gateway", "group": "Operations", "url": "/docs/quickstart#2-configure-and-start-the-gateway", "summary": "Copy the example env file, set your turbopuffer API key in it, and start the gateway with docker compose. The bundled compose file runs the community gateway image against the turbopuffer API with a restart policy and health check, verifiable via a local health endpoint.", "facts": [ { "kind": "code", "literal": "cp .env.example .env\n# edit .env and set TURBOPUFFER_API_KEY=tpuf_...", "chunkId": "quickstart#2-configure-and-start-the-gateway" }, { "kind": "code", "literal": "docker compose up", "chunkId": "quickstart#2-configure-and-start-the-gateway" }, { "kind": "code", "literal": "curl http://localhost:8080/health", "chunkId": "quickstart#2-configure-and-start-the-gateway" }, { "kind": "code", "literal": "export TURBOPUFFER_API_KEY=\"tpuf_...\"\nexport LAYER_NAMESPACE=\"products\"\nexport LAYER_GATEWAY_URL=\"http://localhost:8080\"", "chunkId": "quickstart#2-configure-and-start-the-gateway" }, { "kind": "code", "literal": "docker-compose.yml", "chunkId": "quickstart#2-configure-and-start-the-gateway" }, { "kind": "code", "literal": "hevlayer/layer-gateway", "chunkId": "quickstart#2-configure-and-start-the-gateway" }, { "kind": "code", "literal": "api.turbopuffer.com", "chunkId": "quickstart#2-configure-and-start-the-gateway" } ], "sources": [ { "chunkId": "quickstart#2-configure-and-start-the-gateway", "url": "/docs/quickstart#2-configure-and-start-the-gateway", "anchor": "2-configure-and-start-the-gateway" } ], "mode": "agent-primary", "terms": [ "configure", "start", "gateway", "copy", "example", "file", "turbopuffer", "docker", "compose", "bundled", "runs", "community", "image", "against", "restart", "policy", "health", "check", "verifiable", "local", "endpoint", "edit", "tpuf", "curl", "http", "localhost", "8080", "export", "layer", "namespace", "products", "hevlayer", "fill", "turbopufferapikey", "another", "shell", "rest", "steps", "these" ] }, { "id": "quickstart#3-initialize-the-namespace", "kind": "section", "title": "Quickstart", "heading": "3. Initialize the namespace", "group": "Operations", "url": "/docs/quickstart#3-initialize-the-namespace", "summary": "Initialize the namespace via the gateway's init route, which stamps existing rows with Layer-reserved shard metadata — idempotent and self-throttled, with queries continuing to work while the backfill drains. Watch the init state reach ready on the namespace metadata endpoint.", "facts": [ { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/$LAYER_NAMESPACE/init\" \\\n -H \"Authorization: Bearer $TURBOPUFFER_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"schema_version\": 1, \"shard_count\": 8}'", "chunkId": "quickstart#3-initialize-the-namespace" }, { "kind": "code", "literal": "init_state", "chunkId": "quickstart#3-initialize-the-namespace" }, { "kind": "code", "literal": "ready", "chunkId": "quickstart#3-initialize-the-namespace" }, { "kind": "code", "literal": "GET /v2/namespaces/$LAYER_NAMESPACE/metadata", "chunkId": "quickstart#3-initialize-the-namespace" } ], "sources": [ { "chunkId": "quickstart#3-initialize-the-namespace", "url": "/docs/quickstart#3-initialize-the-namespace", "anchor": "3-initialize-the-namespace" } ], "mode": "agent-primary", "terms": [ "initialize", "namespace", "gateway", "init", "route", "stamps", "existing", "rows", "layer", "reserved", "shard", "metadata", "idempotent", "self", "throttled", "queries", "continuing", "work", "while", "backfill", "drains", "watch", "state", "reach", "ready", "endpoint", "curl", "post", "namespaces", "authorization", "bearer", "turbopuffer", "content", "type", "application", "json", "schema", "version", "count", "layergatewayurl" ] }, { "id": "quickstart#4-run-a-query", "kind": "section", "title": "Quickstart", "heading": "4. Run a query", "group": "Operations", "url": "/docs/quickstart#4-run-a-query", "summary": "Run a BM25 query through the gateway's turbopuffer-compatible query route — the same routes your application already calls now go through the gateway. Follow-ups point to query and fetch, scans, and the install guide for a real cluster.", "facts": [ { "kind": "code", "literal": "curl -X POST \"$LAYER_GATEWAY_URL/v2/namespaces/$LAYER_NAMESPACE/query\" \\\n -H \"Authorization: Bearer $TURBOPUFFER_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"rank_by\": [\"title\", \"BM25\", \"wireless earbuds\"],\n \"top_k\": 10\n }'", "chunkId": "quickstart#4-run-a-query" } ], "sources": [ { "chunkId": "quickstart#4-run-a-query", "url": "/docs/quickstart#4-run-a-query", "anchor": "4-run-a-query" } ], "mode": "agent-primary", "terms": [ "query", "bm25", "through", "gateway", "turbopuffer", "compatible", "route", "same", "routes", "application", "already", "calls", "follow", "point", "fetch", "scans", "install", "guide", "real", "cluster", "curl", "post", "layer", "namespaces", "namespace", "authorization", "bearer", "content", "type", "json", "rank", "title", "wireless", "earbuds", "layergatewayurl", "layernamespace", "turbopufferapikey", "rankby", "topk", "here" ] }, { "id": "roadmap", "kind": "section", "title": "Changelog", "heading": null, "group": "Overview", "url": "/docs/roadmap", "summary": "The changelog page tracks what has shipped in hev layer and what is coming next.", "facts": [ { "kind": "value", "literal": "FeatureGate.astro", "chunkId": "roadmap" } ], "sources": [ { "chunkId": "roadmap", "url": "/docs/roadmap", "anchor": null } ], "mode": "agent-primary", "terms": [ "changelog", "page", "tracks", "shipped", "layer", "coming", "next", "featuregate", "astro" ] }, { "id": "roadmap#04", "kind": "section", "title": "Changelog", "heading": "0.4", "group": "Overview", "url": "/docs/roadmap#04", "summary": "A heading marking the 0.4 release section of the changelog, with its contents in the sibling sections.", "facts": [ { "kind": "value", "literal": "0.4", "chunkId": "roadmap#04" } ], "sources": [ { "chunkId": "roadmap#04", "url": "/docs/roadmap#04", "anchor": "04" } ], "mode": "agent-primary", "terms": [ "heading", "marking", "release", "section", "changelog", "contents", "sibling", "sections" ] }, { "id": "roadmap#api-hardening", "kind": "section", "title": "Changelog", "heading": "API hardening", "group": "Overview", "url": "/docs/roadmap#api-hardening", "summary": "The API-hardening changelog group covers finalized CRDs, wire-compatible pass-through reads and writes, and naming work.", "facts": [], "sources": [ { "chunkId": "roadmap#api-hardening", "url": "/docs/roadmap#api-hardening", "anchor": "api-hardening" } ], "mode": "agent-primary", "terms": [ "hardening", "changelog", "group", "covers", "finalized", "crds", "wire", "compatible", "pass", "through", "reads", "writes", "naming", "work", "finalize", "things" ] }, { "id": "roadmap#lifecycle-and-operability", "kind": "section", "title": "Changelog", "heading": "Lifecycle and operability", "group": "Overview", "url": "/docs/roadmap#lifecycle-and-operability", "summary": "Shipped lifecycle and operability work: autoscaling compute for pipelines and UDFs, the document cache endpoint, index snapshot history, coordinated delete, Helm and Terraform install scripts, scoped minted API keys, license key validation, key audit logs to S3, the Warehouse CRD for Snowflake and Hugging Face sources, and the production cluster cutover.", "facts": [ { "kind": "code", "literal": "ApiKey", "chunkId": "roadmap#lifecycle-and-operability" } ], "sources": [ { "chunkId": "roadmap#lifecycle-and-operability", "url": "/docs/roadmap#lifecycle-and-operability", "anchor": "lifecycle-and-operability" } ], "mode": "agent-primary", "terms": [ "lifecycle", "operability", "shipped", "work", "autoscaling", "compute", "pipelines", "udfs", "document", "cache", "endpoint", "index", "snapshot", "history", "coordinated", "delete", "helm", "terraform", "install", "scripts", "scoped", "minted", "keys", "license", "validation", "audit", "logs", "warehouse", "snowflake", "hugging", "face", "sources", "production", "cluster", "cutover", "apikey", "multi", "stage", "resources", "events" ] }, { "id": "roadmap#polish", "kind": "section", "title": "Changelog", "heading": "Polish", "group": "Overview", "url": "/docs/roadmap#polish", "summary": "What stands between today and the 1.0 cut: documentation polish, cost UAT, and dashboard UAT.", "facts": [ { "kind": "value", "literal": "1.0", "chunkId": "roadmap#polish" } ], "sources": [ { "chunkId": "roadmap#polish", "url": "/docs/roadmap#polish", "anchor": "polish" } ], "mode": "agent-primary", "terms": [ "polish", "stands", "between", "today", "documentation", "cost", "dashboard" ] }, { "id": "roadmap#search", "kind": "section", "title": "Changelog", "heading": "Search", "group": "Overview", "url": "/docs/roadmap#search", "summary": "Shipped search work: stable reads during heavy writes, temporal queries, a namespace ready signal, precomputed facet listings and counts in snapshots, scans by filter/FTS/hybrid-text/ANN, search by id via document-cached vector, hybrid text fusion (fuzzy plus BM25 fused by RRF), Auto query routing, agentic search above Auto, search history to S3, trending searches, and enhanced namespace metadata.", "facts": [ { "kind": "code", "literal": "as_of", "chunkId": "roadmap#search" }, { "kind": "code", "literal": "between", "chunkId": "roadmap#search" }, { "kind": "code", "literal": "fts", "chunkId": "roadmap#search" }, { "kind": "code", "literal": "hybrid_text", "chunkId": "roadmap#search" }, { "kind": "code", "literal": "ann", "chunkId": "roadmap#search" }, { "kind": "code", "literal": "Auto", "chunkId": "roadmap#search" }, { "kind": "code", "literal": "Agent", "chunkId": "roadmap#search" } ], "sources": [ { "chunkId": "roadmap#search", "url": "/docs/roadmap#search", "anchor": "search" } ], "mode": "agent-primary", "terms": [ "search", "shipped", "work", "stable", "reads", "during", "heavy", "writes", "temporal", "queries", "namespace", "ready", "signal", "precomputed", "facet", "listings", "counts", "snapshots", "scans", "filter", "hybrid", "text", "document", "cached", "vector", "fusion", "fuzzy", "plus", "bm25", "fused", "auto", "query", "routing", "agentic", "above", "history", "trending", "searches", "enhanced", "metadata" ] }, { "id": "roadmap#surfaces", "kind": "section", "title": "Changelog", "heading": "Surfaces", "group": "Overview", "url": "/docs/roadmap#surfaces", "summary": "Shipped surfaces: the dashboard MVP for CRD management and observability, the documentation site, and official Python, Go, and TypeScript clients.", "facts": [], "sources": [ { "chunkId": "roadmap#surfaces", "url": "/docs/roadmap#surfaces", "anchor": "surfaces" } ], "mode": "agent-primary", "terms": [ "surfaces", "shipped", "dashboard", "management", "observability", "documentation", "site", "official", "python", "typescript", "clients" ] }, { "id": "roadmap#up-next", "kind": "section", "title": "Changelog", "heading": "Up Next", "group": "Overview", "url": "/docs/roadmap#up-next", "summary": "Planned next: more VectorStore backends, declarative ingestion with typed pipeline sources and chunking requiring no worker code, published pass-through performance benchmarks, and A/B variant indexes.", "facts": [ { "kind": "code", "literal": "VectorStore", "chunkId": "roadmap#up-next" }, { "kind": "value", "literal": "e.g", "chunkId": "roadmap#up-next" } ], "sources": [ { "chunkId": "roadmap#up-next", "url": "/docs/roadmap#up-next", "anchor": "up-next" } ], "mode": "agent-primary", "terms": [ "next", "planned", "more", "vectorstore", "backends", "declarative", "ingestion", "typed", "pipeline", "sources", "chunking", "requiring", "worker", "code", "published", "pass", "through", "performance", "benchmarks", "variant", "indexes", "hugging", "face", "overhead" ] }, { "id": "tradeoffs", "kind": "section", "title": "Tradeoffs", "heading": null, "group": "Overview", "url": "/docs/tradeoffs", "summary": "Layer makes its design tradeoffs explicit and will offer configuration where possible. It adds query-path latency via an additional network hop (not configurable), a query plan enabling stable reads during heavy writes (index configurable), and per-request RBAC entitlement checks (not configurable); on turbopuffer-backed namespaces it also increases index storage through secondary indexing for upsert-time filtering and scatter-gather sharding (neither configurable).", "facts": [ { "kind": "flag", "literal": "--muted", "chunkId": "tradeoffs" }, { "kind": "flag", "literal": "--signal", "chunkId": "tradeoffs" } ], "sources": [ { "chunkId": "tradeoffs", "url": "/docs/tradeoffs", "anchor": null } ], "mode": "agent-primary", "terms": [ "layer", "makes", "design", "tradeoffs", "explicit", "offer", "configuration", "possible", "adds", "query", "path", "latency", "additional", "network", "configurable", "plan", "enabling", "stable", "reads", "during", "heavy", "writes", "index", "request", "rbac", "entitlement", "checks", "turbopuffer", "backed", "namespaces", "also", "increases", "storage", "through", "secondary", "indexing", "upsert", "time", "filtering", "scatter" ] } ], "edges": [] } ``` --- # Introduction Source: https://hevlayer.com/docs import Diagram from "../../components/docs/Diagram.astro"; import { layerMapDiagram } from "../../lib/diagrams"; Layer provides a set of drop-in enhancements to your favorite retrieval systems. One install gives you two products: a **retrieval gateway** you adopt without changing client code, and a **function runtime** that runs your own code across every row of your index. {layerMapDiagram} You run two server components in your own cluster: a Rust **gateway** and a Kubernetes **operator**. The **gateway** is a transparent proxy in front of turbopuffer. It extends native clients with [fetch](/docs/api/query#fetch), [scans](/docs/api/scans), [snapshots](/docs/api/snapshots), and operator-facing semantics around the cache, write path, and [pipelines](/docs/api/pipelines) — you swap in Layer's drop-in client and change nothing else. It also lets you scale your own compute over multi-stage pipelines, reason about the [state of your index](/docs/api/namespace-metadata), observe [clickstream](/docs/api/search-history), and track [cost](/docs/dashboard). The **function runtime** is one primitive for every per-row job over an index. Embedding, classification, tagging, and attribute migration are all the same thing: a stateless [UDF](/docs/kubernetes/function-crd) declared as a Kubernetes-native `Function`. The gateway discovers the work, leases it to worker pools, retries, and writes results back, with KEDA scaling each pool to zero between bursts. You write and declare the function, and Layer runs the worker fleet for you. You call the gateway four ways: the [Python client](/docs/api/introduction#install), the [Go client](/docs/api/introduction#install), the [TypeScript client](/docs/api/introduction#install), or the REST API directly — the clients are generated from the same OpenAPI spec, and every endpoint page shows them side by side. Layer also ships an optional GUI [dashboard](/docs/dashboard). The dashboard manages cluster configuration through CRDs; all other state is persisted in object storage (S3). No durable state lives in a Layer process, so the compute tier is stateless and fully elastic. Because indexing is bursty, especially GPU-bound work, our [Terraform](/docs/install#terraform) installs [Karpenter](https://karpenter.sh) as a cluster autoscaler to provision and scale the nodes Layer's compute runs on. The remaining backing services are the document cache, the indexing-state store, and the metrics store. Every component Layer runs alongside is open source: - **[Karpenter](https://karpenter.sh)** — cluster autoscaler that provisions and scales nodes for Layer's bursty, GPU-bound compute (Apache-2.0). - **[Aerospike](https://aerospike.com)** — ephemeral document cache (AGPL-3.0). - **[PostgreSQL](https://www.postgresql.org)** — indexing-state store for the pipeline and embed queue (PostgreSQL License). - **[VictoriaMetrics](https://victoriametrics.com)** — metrics store (Apache-2.0). To get started, see the [install guide](/docs/install). For more technical detail, see [Concepts](/docs/concepts), [Guarantees](/docs/guarantees), and [Tradeoffs](/docs/tradeoffs). --- # Concepts Source: https://hevlayer.com/docs/concepts import Callout from "../../components/docs/Callout.astro"; ## Control loops Layer uses a control loop as a core primitive for managing your indexes. It reconciles index state against metrics emitted by the search system, which is how Layer applies row-level transformations ([UDFs](/docs/kubernetes/function-crd)) and keeps an index's stable view current. Related: [UDFs](/docs/kubernetes/function-crd), [snapshots](/docs/api/snapshots), stable watermark. ## Kubernetes autoscaling Because Layer is stateless, you can autoscale every tier independently. Karpenter handles node-level scaling, and KEDA scales pods against signals from an embedded PostgreSQL queue. The data in that queue is used for scaling decisions only — it carries no non-recoverable system state. ## Gateway enhancements Where helpful, the gateway extends your search system with common query patterns and filtering primitives. Layer's enhancements use reserved `_hevlayer_*` attributes; changing the schema on those attributes breaks Layer's guarantees but should degrade gracefully. All functionality is exposed through one API surface, the [Python, Go, or TypeScript client, or plain REST](/docs/api/introduction#install), so applications can route every call through the gateway. Layer works best when traffic flows through it consistently, even for requests that need no extra behavior. ## Scatter/gather Layer can partition a single namespace into hash buckets, called shards, by assigning each row a reserved `_hevlayer_shard` attribute (xxh64 of its id, modulo the shard count). The gateway then scatters a query to every bucket in parallel, one `_hevlayer_shard`-filtered query per shard, and gathers the results: it merges and re-ranks the combined rows down to your requested `top_k` before returning them. Sharding stays invisible to the client — you issue one query and get one ranked result set. The same scatter/gather path backs [scans](/docs/api/scans) (filter, full-text, and radius) and [UDF](/docs/kubernetes/function-crd) discovery scans. For an existing turbopuffer namespace adopted by Layer, initialize sharding with `POST /v2/namespaces/{namespace}/init` and a `shard_count`. The gateway writes a reserved namespace marker, stamps new writes immediately, and runs an embedded scan-and-patch backfill for rows that do not yet have `_hevlayer_shard`. Scatter/gather activates only after namespace metadata reports `layer.shard_lag_rows: 0`; until then queries and scans use the single-namespace path so unstamped rows are not missed. Run `layer init --shards N` to create the namespace shard marker, start the backfill, and watch `shard_lag_rows` drain until scatter/gather activates. ## Document cache The Layer document cache does two jobs. Document [reads](/docs/api/query#fetch) are served pull-through: the gateway checks the cache first, and on a miss reads through to turbopuffer (or S3 for snapshots), returns the row, and backfills the cache best-effort. [Pipeline](/docs/api/pipelines) chunk handoff uses the same store as the queue between CPU and GPU workers. Neither job makes it a hard dependency: document reads fall through to origin if the cache is unavailable, and chunk reads fall back to S3 backing (see [Failure modes](/docs/failure-modes)). One logical cache serves every path, with different uses (document fetch, pipeline chunks, snapshot field-values) separated into dedicated cache sets. ## Glossary | Concept | Current meaning | | --- | --- | | [Namespace](/docs/api/introduction) | A turbopuffer namespace addressed through `/v2/namespaces/{namespace}`. | | Document | A row id plus attributes, and optionally a vector when writing/searching. | | Document cache | Layer-managed hot records keyed by namespace and document id, plus cache sets for pipeline chunks and snapshots. | | Stable watermark | Epoch-ms cut tracked by the consistency watcher when turbopuffer reports up-to-date, or when a backing store without an index watermark settles its `row_count` across consecutive polls. | | Ready signal | Whether a namespace is fully indexed: `indexed` / `index_lag_rows` on [namespace metadata](/docs/api/namespace-metadata), reconciled from the latest snapshot when every row's vector is indexed. | | [Pipeline](/docs/api/pipelines) | A PostgreSQL-backed state machine for CPU extraction and GPU embedding work. | | [Snapshot](/docs/api/snapshots) | A content-addressed S3 facet histogram written after a namespace is observed stable. | | Facet listing | The distinct values for a field, precomputed in snapshots as `fields[].values[].v` or computed on demand by a values scan. | | Facet count | The document count for a facet value, returned as `fields[].values[].n` in snapshots and `values[].n` in values scan results. | | [Scan](/docs/api/scans) | On-demand row selection by filter, full-text (`fts`), or radius (`ann`) that returns matching IDs or field values asynchronously, or a row count synchronously. | | [UDF](/docs/kubernetes/function-crd) | A stateless worker the gateway coordinates over existing rows to enrich, fan out, or re-upsert data. | | Gateway | The Rust proxy fronting turbopuffer that serves the compatible API plus cache, scans, snapshots, pipelines, and the UDF runtime. | | [Operator](/docs/kubernetes/operator) | The Kubernetes operator that reconciles Layer's CRDs — functions, pipelines, scaling, and cluster config. | | Shard | A hash bucket within a single namespace. Each row carries a reserved `_hevlayer_shard` value (xxh64 of its id, modulo the shard count) so the gateway can scatter/gather a query across buckets. | | Leg | One subquery in a [hybrid text](/docs/api/query#hybrid-text-fusion) expansion: the full-input BM25 leg or one per-token fuzzy leg. Every leg of a query reads the same stable watermark cut. | | RRF | Reciprocal rank fusion — turbopuffer-native re-ranking (`rerank_by: ["RRF", ...]`) that merges legs into one list. Layer delegates all fusion math upstream. | | Tokenizer policy | The documented transform from a `HybridText` input to query tokens: UAX #29 word boundaries and lowercasing via turbopuffer's open-source `alyze` tokenizer (the production `word_v4` code), then drop tokens under 2 characters, dedupe, cap at 15. | | Route | The retrieval strategy the [query router](/docs/api/query#query-routing) picks for an `Auto` query (`hybrid_text`, `semantic`, or `fused`), chosen from the shape of the input text alone. Vector availability gates execution, not the choice. | | Routing policy | The deterministic, versioned decision function behind `Auto`. The version travels in the `routing` echo block and search history so threshold changes are visible. | | Deferral | The response to a vectorless `Auto` query routed `semantic` or `fused`: the routing decision with `executed: false` and no rows. The application embeds and re-issues with the route forced. | | CRD | Custom Resource Definition: the Kubernetes-native resources the operator reconciles — [functions](/docs/kubernetes/function-crd), [pipelines](/docs/kubernetes/pipeline-crd), [scaling](/docs/kubernetes/scaling-crd), and [indexes](/docs/kubernetes/index-crd). | | PromQL | The Prometheus query language. The gateway proxies it to the embedded VictoriaMetrics so you can query metrics without a separate scraper. | --- # Document model Source: https://hevlayer.com/docs/document-model import StoreSwitch from "../../components/docs/StoreSwitch.astro"; import FeatureGate from "../../components/docs/FeatureGate.astro"; Layer reserves the `_hevlayer_*` attribute prefix for its own bookkeeping. **These attributes are read-only.** The gateway stamps and maintains them; your writes and [UDF](/docs/kubernetes/function-crd) completion patches must not set or change them. The gateway rejects or overwrites any `_hevlayer_*` value you send, and editing one directly breaks Layer's guarantees. The reserved set depends on the engine backing the namespace.
| Attribute | Type | Purpose | | --- | --- | --- | | `_hevlayer_upserted_at` | integer (epoch ms) | Server-stamped on every write. The watermark Layer's [stable reads](/docs/api/query#stable-reads) are taken against. | | `_hevlayer_shard` | integer | Hash bucket assigned at write time (`xxh64(id) % shard_count`), present only on sharded namespaces. Lets the gateway [scatter/gather](/docs/concepts#scattergather) a query across the shards of one namespace. | | `_hevlayer_udf__v` | string | Function completion marker. The gateway stamps the Function's `spec.version` here when a worker completes a row. Hyphens in the Function id are normalized to underscores. | | `_hevlayer_udf__stale_after` | integer or null | Function invalidation marker. Discovery reclaims rows once this epoch-ms timestamp expires; completion clears the marker. |
The `_hevlayer_` prefix also namespaces internal cache sets (snapshot field-values and search-history clickstream), but those are cache keys, not part of your document schema. --- # No Guarantees Source: https://hevlayer.com/docs/guarantees import Callout from "../../components/docs/Callout.astro"; Layer can't offer guarantees. We try our best to provide secure, hands-off infrastructure that you are ultimately responsible for. While we can't offer guarantees, we make a set of promises in how we design, secure, and distribute our software that we believe make it easy to use and will stand the test of time. This page covers the specific status of those promises. ## Commitments - Your history is backed up to S3. Search history and namespace snapshots are written to the S3 bucket you specify. The format of this data may change. - Hot data served from cache. Customer document and chunk data is served from Layer's local document cache for price/performance. We try not to stray from this pattern, though some use cases may justify a smaller in-memory document cache. - This documentation is accurate and up to date. When it isn't, that's a bug in the software — report it. - Graceful degradation. We add graceful degradation support whenever possible — the gateway degrades rather than failing hard. The per-scenario behavior and recovery signals live in the [failure-mode runbook](/docs/failure-modes). - Client compatibility. We will (almost) always stay client-compatible with the search systems we front. Where we diverge, it's a feature making an explicit tradeoff we believe is an improvement. - One consistency cut per query. When the gateway expands a query into multiple legs ([hybrid text fusion](/docs/api/query#hybrid-text-fusion), [scatter/gather](/docs/concepts#scattergather)), every leg is filtered at the same stable watermark, injected from a single read. Legs never see different cuts. Layer was developed by a [single person](https://hevmind.com/about) orchestrating agentic coding tools, leveraging open source and building automation. Not a single line of code was hand-written. That said, it was made with ❤️ by a human as much as it is built by AI. --- # Tradeoffs Source: https://hevlayer.com/docs/tradeoffs Layer makes a set of design tradeoffs we believe improve functionality of your search system. This page makes those tradeoffs explicit. As this list grows, we will offer configuration where possible to allow users to configure their preference. Layer adds latency to the query path in the following ways. - An additional network hop (not configurable). - A query plan that allows for [stable reads](/docs/api/query#stable-reads) during heavy writes ([index configurable](/docs/kubernetes/index-crd)). - The same query plan also checks [RBAC entitlements](/docs/kubernetes/apikey-crd#entitlements) on every request (not configurable). On a turbopuffer-backed namespace, Layer also increases index storage requirements via. - A secondary indexing for filtering by upsert time (not configurable). - A secondary indexing used for scatter gather sharding (not configurable). --- # Limits Source: https://hevlayer.com/docs/limits Layer is limited by certain constraints of the underlying components we ship with. We will lift these as demand increases. - **Single-node document cache.** We enforce this for simplicity and also believe that a single large local drive offers enough storage for almost every dataset. - **~4,090 turbopuffer namespaces.** We use Aerospike sets for logical separation of data, which are limited by the Aerospike Community Edition AGPL license. - **~3 TB cache size.** Another limitation of the Aerospike license. - **10,000 distinct values per scan facet field.** Pre-computed snapshot scans cap each facet field's cardinality. If a field exceeds the cap, it is noted in `fields_skipped[]` rather than `fields[]`, so readers can treat every emitted field as complete. See [snapshots](/docs/api/snapshots). - **1,000,000 distinct values per values scan.** On-demand values scans accumulate their histogram in gateway memory. A job that crosses the cap completes with `truncated: true`: the cap applies after the full pass, keeping the top values by count — each with an exact count — and dropping the low-count tail. See [scans](/docs/api/scans#values-mode). ## No limits These have no enforced ceiling, but practical limits exist and will show up under load — see [failure modes](/docs/failure-modes) for how each surface degrades. - **CRD instances** (`Index`, `Function`, `Pipeline`, `Scaling`) — bounded only by the etcd and operator throughput of your Kubernetes cluster. - **Snapshot history per namespace** — durable in S3; bounded by `spec.snapshot.retention` when set, or by object storage cost under `retention: never`. - **Search history retention** — accumulates indefinitely in S3; no automatic expiry. - **Clickstream event volume** — accumulates indefinitely in S3; no automatic expiry. - **UDF concurrency per function** — KEDA scales replicas to match queue depth, bounded by your cluster's capacity. - **Pipeline queue depth** — pipeline queues, including chunked document queues, store document IDs and chunk ID lists in S3 manifests and keep only segment state and counters in Postgres. - **Document size and attribute count** — bounded by turbopuffer and Aerospike record limits, not by Layer. --- # Agents Source: https://hevlayer.com/docs/agents import Callout from "../../components/docs/Callout.astro"; These docs are queryable from the command line. The same engine behind the `⌘K` search on this site ships as a CLI, so your coding agent can search, read, and cite the Layer docs directly — no scraping, no MCP server, no API key. The `layer` CLI also lets agents operate environments, indexes, pipelines, UDFs, and Function runs. The skill bodies below are plain `SKILL.md` files. Use your agent harness' native skill directory when it has one, or paste the same Markdown into `AGENTS.md` or the harness equivalent. ## 1. Install the CLIs ```sh go install github.com/hev/ask/cmd/ask@latest ``` The `ask` binary is self-contained; any agent harness that can run a shell command can use it. From a Layer checkout, build the `layer` CLI when the agent should operate Layer environments instead of only searching docs: ```sh go build -o layer ./apps/layer-cli ``` ## 2. Add the docs skill Set `AGENT_SKILL_HOME` to your harness's skill directory, such as `~/.codex/skills` for Codex or `~/.claude/skills` for Claude Code. ```sh AGENT_SKILL_HOME="${AGENT_SKILL_HOME:-${CODEX_HOME:-$HOME/.codex}/skills}" mkdir -p "$AGENT_SKILL_HOME/hevlayer-docs" cat > "$AGENT_SKILL_HOME/hevlayer-docs/SKILL.md" <<'EOF' --- name: hevlayer-docs description: >- Query the hev layer docs. Use when the user asks about Layer — the turbopuffer gateway, stable reads, the stable watermark, the document cache, warm jobs, scans (filter, full-text, and radius), snapshots, pipelines, UDFs, the Index/InfraRules/Pipeline/Function CRDs, compute pools, install via Terraform or Helm, failure modes, or the dashboard. --- # hev layer docs Answer Layer questions from the docs, not from memory. Every verb is a keyless read: ask --endpoint https://hevlayer.com/api/ask search "" ask --endpoint https://hevlayer.com/api/ask section get "" ask --endpoint https://hevlayer.com/api/ask overview ask --endpoint https://hevlayer.com/api/ask glossary get "" Start with `search`; fetch sections for detail; use `overview` when you need the full map. Section ids look like `api/query#stable-reads`. Cite sections in your answer as https://hevlayer.com plus the returned `url` field. If `ask` is missing, install it: `go install github.com/hev/ask/cmd/ask@latest` EOF ``` ## 3. Add the layer CLI skill Use this skill when an agent should inspect or operate Layer through the `layer` CLI. The skill keeps read-only inspection, docs lookup, and mutating operations separate. ```sh AGENT_SKILL_HOME="${AGENT_SKILL_HOME:-${CODEX_HOME:-$HOME/.codex}/skills}" mkdir -p "$AGENT_SKILL_HOME/hevlayer-layer-cli" cat > "$AGENT_SKILL_HOME/hevlayer-layer-cli/SKILL.md" <<'EOF' --- name: hevlayer-layer-cli description: >- Use the hevlayer layer CLI. Use when the user asks an agent to inspect Layer environments, query docs through layer ask, list or get indexes, pipelines, or UDFs, open the operations TUI, delete indexes, or run Function manifests with the layer CLI. --- # hevlayer layer CLI Use `layer` to operate hevlayer from the terminal. In a Layer checkout, prefer a repo-local binary: go build -o layer ./apps/layer-cli ./layer --help Use `./layer ...` for a repo-local binary and `layer ...` for one on `PATH`. Prefer `-o json` for agent parsing and do not print API keys. For docs questions, start with `layer ask` against the committed digest: layer ask grep "" layer ask cat "" layer ask tree layer ask glossary get "" For read-only operational inspection, prefer: layer -o json env ls layer -o json env show [NAME] layer -o json index list layer -o json index get NAME layer -o json pipeline list layer -o json pipeline get ID layer -o json udf list layer -o json udf get UDF_ID Only `layer run` needs Kubernetes access by default. It applies a Function CR, registers the UDF spec with the gateway, triggers discovery, and optionally watches until the queue drains. Confirm the target environment, gateway URL, kube context, and Kubernetes namespace before mutating state. Mutating commands include `layer env add`, `layer env use`, `layer env rm`, `layer index delete`, `layer run`, and `layer run --rm`. Resolve configuration in this order: explicit flags, `LAYER_*` or `HEVLAYER_*` environment variables, `--env` or `LAYER_ENV`, the active `~/.hevlayer/config.toml` environment, then the built-in base URL. EOF ``` ## 4. Ask ```sh ask --endpoint https://hevlayer.com/api/ask search "cache is down" ``` ```json { "results": [ { "title": "Concepts", "heading": "Document cache", "url": "/docs/concepts#document-cache", "group": "Overview", "snippet": "The document cache does two jobs: pull-through document reads..." } ] } ``` From here your agent typically runs `section get` on the winning id and answers with the citation. ## The verbs | Verb | Returns | | --- | --- | | `overview` | Orientation context plus the full section map with stable ids | | `search ""` | Ranked sections with snippets and deep links | | `section get ""` | One section: summary, exact identifiers, source URL | | `glossary get ""` | A product term resolved through its aliases (`watermark` → stable watermark) | ## Why answers stay grounded Search runs over a committed, reviewable digest of these docs — the same corpus, heading by heading, that renders on this site. Every anchor in it is verified against the rendered pages in CI, so a cited deep link like [/docs/api/query#stable-reads](/docs/api/query#stable-reads) always resolves. When the docs change, the digest is rebuilt and recommitted with them. Every verb above is a read against the public docs. Nothing to sign up for, nothing to configure beyond the endpoint URL. The docs are also available as plain text for direct ingestion: [/llms.txt](/llms.txt) (index) and [/llms-full.txt](/llms-full.txt) (full corpus). The CLI is the better path for agents that can run commands — it ranks, resolves aliases, and costs a fraction of the tokens. --- # Demos Source: https://hevlayer.com/docs/demos Every demo below is a live app built on Layer that reimplements nothing. Each composes shipped gateway features — [routing](/docs/api/query#query-routing), [hybrid text fusion](/docs/api/query#hybrid-text-fusion), fuzzy matching, [local embedding serving](/docs/api/embed), [pipelines](/docs/api/pipelines), [snapshots](/docs/api/snapshots), and the [function runtime](/docs/kubernetes/function-crd) — over a different corpus, and makes the gateway's behavior legible in the UI. They are also the fastest way to see what the gateway does without standing up a cluster. | Demo | What it shows | Corpus | | --- | --- | --- | | [shelf](https://shelf.hevlayer.com) | The query router, made legible | Books | | [chart](https://chart.hevlayer.com) | Query routing on clinical search, with a number | PMC-Patients case reports | | [hybrid-text](https://hybrid-text.hevlayer.com) | Hybrid text fusion, proven with qrels | BEIR/SciFact abstracts | | [wiki](https://wiki.hevlayer.com) | Auto routing plus CPU-only Lattice embedding, at scale | Simple English Wikipedia | | [lens](https://lens.hevlayer.com) | Text-to-image search with zero GPUs | Wikimedia Commons Quality images | | [shop](https://shop.hevlayer.com) | Everything together — an end-to-end app | Amazon product catalog | ## shelf — book search that shows its routing **Live:** [shelf.hevlayer.com](https://shelf.hevlayer.com) · **Source:** [github.com/hev/shelf](https://github.com/hev/shelf) One search box, three routes. Type an author, a title, or a vibe; the gateway's `Auto` rank expression picks keyword (`hybrid_text`), `semantic`, or a `fused` blend from the shape of the query, and shelf renders that decision as a badge with the reason. The routing policy keys on token count, so the canned chips visibly change route as the query gets longer. This is the text-native routing showcase: it makes the [query router](/docs/api/query#query-routing) decision the hero, not a footnote. Built on the [query router](/docs/api/query#query-routing) (`Auto`), [hybrid text fusion](/docs/api/query#hybrid-text-fusion), and fuzzy matching. ## chart — clinical patient-notes search that shows its routing **Live:** [chart.hevlayer.com](https://chart.hevlayer.com) The same routing hero on the corpus with the sharpest bimodal query distribution there is: clinicians search both by exact token (`metformin 500mg`, `CABG`, `aspirn`) and by clinical picture (`elderly woman with progressive dyspnea and bilateral lower-extremity edema`). chart is the first Layer demo with real relevance judgments — PMC-Patients ReCDS qrels — so the routing and hybrid claims are measured, not asserted. Behind the search box, an open-weight Gemma cascade (vLLM, scale-to-zero on the GPU pool) reads each note once and extracts clinical events and facet labels: the [function runtime](/docs/kubernetes/function-crd) showcase. The corpus is published, de-identified case reports (PMC-Patients, CC-BY-NC-SA). It is a search demo — not raw EHR, and not clinical advice. Built on the [query router](/docs/api/query#query-routing), [hybrid text fusion](/docs/api/query#hybrid-text-fusion) with fuzzy matching, [pipelines](/docs/api/pipelines), the [function runtime](/docs/kubernetes/function-crd), and [snapshots](/docs/api/snapshots). ## hybrid-text — hybrid text fusion over SciFact **Live:** [hybrid-text.hevlayer.com](https://hybrid-text.hevlayer.com) · **Source:** [github.com/hev/hybrid-text-fusion-demo](https://github.com/hev/hybrid-text-fusion-demo) The eval-shaped sibling of the routing demos, over ~5,000 scientific abstracts from BEIR/SciFact. One query string fans out into a full-input BM25 leg plus one fuzzy leg per token, fused by reciprocal rank fusion — so results survive typos and morphological variants without losing BM25's signal. It is purely lexical: no embeddings, no GPU, no vector index. SciFact ships qrels, so the UI flags known-relevant abstracts and the demo scores nDCG@10 / recall@10; every search also shows its gateway round-trip time and a fusion inspector (tokens, legs, RRF constant). Built on [hybrid text fusion](/docs/api/query#hybrid-text-fusion) and fuzzy matching. ## wiki — all of Simple English Wikipedia, routed and embedded on CPU **Live:** [wiki.hevlayer.com](https://wiki.hevlayer.com) · **Source:** [github.com/hev/wiki](https://github.com/hev/wiki) The routing hero at corpus scale: one `Auto` query over all 283,997 Simple English Wikipedia articles (1.74M paragraph rows) routes each search to full-text, semantic, or a fused RRF blend, and the UI renders the gateway's `routing` echo beside every result. The semantic leg is the [Lattice](/docs/api/embed#lattice) showcase — the whole corpus embedded through `prefer: lattice`, an ~8 MB int4 lookup-table artifact served in-process on the gateway CPU, with `performance.embedding_ms` and `embedding_tokens` echoed whenever the chosen route embeds. No GPU anywhere in the write or query path. Built on the [query router](/docs/api/query#query-routing), [hybrid text fusion](/docs/api/query#hybrid-text-fusion), and [local embedding serving](/docs/api/embed#lattice) (`prefer: lattice`). ## lens — text-to-image search with zero GPUs **Live:** [lens.hevlayer.com](https://lens.hevlayer.com) · **Source:** [github.com/hev/lens](https://github.com/hev/lens) Cross-modal search over Wikimedia Commons [Quality images](https://commons.wikimedia.org/wiki/Commons:Quality_images): type `sunset over water`, get sunsets. The schema is two lines — a string `image_url` attribute with a [local CLIP](/docs/api/embed#local-clip) embedding profile. The gateway fetches each image and runs CLIP's image tower in-process on CPU at write time, then embeds query text with the same checkpoint's text tower at query time. The app posts writes and queries and renders the echo; it contains no embedding, tokenizer, or image-preprocessing code, and there is no GPU worker or autoscaler pool anywhere in the path. Every result pairs the fixed serving contract (`prefer: local`, gateway CPU) with the live `performance.embedding_ms` echo, and carries its Commons attribution and license. Built on [local CLIP serving](/docs/api/embed#local-clip) (`serving.prefer: local`, `modality: image`) and [schema-attribute embedding](/docs/kubernetes/index-crd#schema-attribute-embedding). ## shop — semantic shopping, everything together **Live:** [shop.hevlayer.com](https://shop.hevlayer.com) (formerly `hev-shop.com`, which redirects) · **Source:** [github.com/hev/shop](https://github.com/hev/shop) The end-to-end application workload: an indexing pipeline, semantic search, recommendations, facets, and observability in one app. shop embeds product images with CLIP ViT-L/14 and writes one vector per product through Layer [pipelines](/docs/api/pipelines) into turbopuffer; the storefront serves image-native semantic search, [`nearest_to_id`](/docs/api/query#query-by-id) recommendations, facet exploration from [namespace snapshots](/docs/api/snapshots), and Layer freshness signals. KEDA scales workers from pipeline metrics and Karpenter scales nodes next to the workload that creates the demand. Where shelf is the text-native routing showcase, shop is the image-native one — and the demo that exercises the most of Layer at once. Built on [pipelines](/docs/api/pipelines), the [write path](/docs/api/write), [query](/docs/api/query#query-by-id) (`nearest_to_id`), [snapshots](/docs/api/snapshots), [search history](/docs/api/search-history), and [autoscaling](/docs/kubernetes/scaling-crd). --- # Changelog Source: https://hevlayer.com/docs/roadmap import FeatureGate from "../../components/docs/FeatureGate.astro"; ## Polish What stands between today and the 1.0 cut: - 📚 Polish documentation - 💸 Cost UAT - 🪟 Dashboard UAT ## Up Next Planned next: - 🧭 More `VectorStore` backends - 🤗 Declarative ingestion — typed pipeline sources (e.g. Hugging Face) and chunking, no worker code - 📊 Performance benchmarks — published pass-through overhead - 🧪 A/B variant indexes ## 0.4 ### API hardening - 🧩 Finalize CRDs - 🚆 Wire-compatible pass-through reads and writes - 🏷️ Naming things ### Lifecycle and operability - 🎚️ [Autoscaling compute](/docs/kubernetes/scaling-crd) for pipelines and UDFs - 🗄️ [Document cache endpoint](/docs/api/query#fetch) for multi-stage pipelines - 📸 [Index snapshot history](/docs/api/snapshots) - 🧨 Coordinated delete - ⛵ [Helm and Terraform install](/docs/install) scripts - 🔐 [Scoped API keys](/docs/api/keys) — minted [`ApiKey` resources](/docs/kubernetes/apikey-crd) - 🔑 [License key validation](/docs/licensing) - 🧾 [Key audit logs](/docs/api/keys) — lifecycle events to S3- 🏭 [Warehouse CRD](/docs/kubernetes/warehouse-crd) — declared Snowflake and Hugging Face sources - 🏗️ Production cluster cutover — lean topology live in prod ### Surfaces - 🪟 [Dashboard MVP](/docs/dashboard) — CRD management and observability - 📚 Documentation site - 🧰 Official Python, Go, and TypeScript clients ### Search - 🎯 [Stable reads](/docs/api/query#stable-reads) during heavy writes - 🕰️ [Temporal queries](/docs/api/query) — `as_of` / `between` selector on reads - 🚦 [Ready signal](/docs/api/namespace-metadata) — namespace reports when every row is indexed - 📜 Precomputed facet listings in [snapshots](/docs/api/snapshots) - 🪙 Precomputed facet counts in [snapshots](/docs/api/snapshots) - 🪃 [Scans](/docs/api/scans) — row selection by filter, `fts`, `hybrid_text`, or `ann` - 🆔 Search by id via document-cached vector - 🪢 [Hybrid text fusion](/docs/api/query#hybrid-text-fusion) — fuzzy + BM25 fused by RRF - 🧭 [Query routing](/docs/api/query#query-routing) — `Auto` picks the search mode - 🤖 [Agentic search](/docs/api/agents) — an [`Agent`](/docs/kubernetes/agent-crd) runs a budgeted plan → recall → relevance-score loop above `Auto` - 📰 [Search history](/docs/api/search-history) saved to S3 - 🔥 Trending searches — reduce-shaped UDFs over search history - 🗂️ Enhanced [namespace metadata](/docs/api/namespace-metadata) --- # FAQ Source: https://hevlayer.com/docs/faq This page answers the questions the rest of the docs don't: licensing, pricing, trials, and where the project is headed. ## What is the licensing for hev layer? The standalone gateway is source-available under the Business Source License 1.1 in [hev/layer](https://github.com/hev/layer). It is free to self-host at any scale and converts to Apache-2.0 on the change date in its `LICENSE`. The operator, function runtime, and dashboard require a commercial license. Licensed installs use signed license keys for trial and commercial access. The key is supplied to Helm as `license.token` or through a referenced Kubernetes Secret. See [licensing](/docs/licensing) for the install and lifecycle details. ## How much will it cost? Layer is licensed per operator deployment: one license per operator install, so a production, staging, and DR cluster each carry their own. Replicas within an install do not count. Pricing is not final for the design-partner cut. Start with a trial license; commercial terms are handled directly with each design partner. ## How do I start a trial? Use the [trial signup](/#start-trial). Submit your work email and hev layer emails a signed trial key plus the current install instructions. ## How do I know whether my license is healthy? Call [`GET /v2/license`](/docs/licensing#license-states) on your gateway. The gateway reports `licensed`, `grace`, or `floor` from the key configured in the install. Operator and dashboard license surfaces use the same state model when their enforcement surfaces ship. ## Who built hev layer? [Adam Hevenor](https://hevmind.com/about). hev layer is a [hev mind](https://hevmind.com) product. --- # Quickstart Source: https://hevlayer.com/docs/quickstart Run the community gateway in front of your existing turbopuffer account. No license key or signup needed — your turbopuffer API key is the gateway bearer token, and your data stays where it is. You need Docker, `curl`, and a turbopuffer namespace with rows in it. ## 1. Clone the repo ```sh git clone https://github.com/hev/layer.git cd layer ``` ## 2. Configure and start the gateway Copy the example env file and fill in your turbopuffer API key: ```sh cp .env.example .env # edit .env and set TURBOPUFFER_API_KEY=tpuf_... ``` Then start the gateway: ```sh docker compose up ``` The bundled `docker-compose.yml` runs the `hevlayer/layer-gateway` image against `api.turbopuffer.com` with a restart policy and health check. Check it in another shell: ```sh curl http://localhost:8080/health ``` The rest of the steps use these in your shell: ```sh export TURBOPUFFER_API_KEY="tpuf_..." export LAYER_NAMESPACE="products" export LAYER_GATEWAY_URL="http://localhost:8080" ``` ## 3. Initialize the namespace ```sh curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/$LAYER_NAMESPACE/init" \ -H "Authorization: Bearer $TURBOPUFFER_API_KEY" \ -H "Content-Type: application/json" \ -d '{"schema_version": 1, "shard_count": 8}' ``` Init stamps existing rows with Layer-reserved shard metadata — it is idempotent and self-throttled, and queries keep working while the backfill drains. Watch `init_state` reach `ready` at `GET /v2/namespaces/$LAYER_NAMESPACE/metadata`. ## 4. Run a query ```sh curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/$LAYER_NAMESPACE/query" \ -H "Authorization: Bearer $TURBOPUFFER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "rank_by": ["title", "BM25", "wireless earbuds"], "top_k": 10 }' ``` The same turbopuffer-compatible routes your application already calls now go through the gateway. From here: - [Query & Fetch](/docs/api/query) — routing, hybrid text fusion, rank expressions - [Scans](/docs/api/scans) — scatter/gather counts across the shards you just initialized - [Install](/docs/install) — the Helm chart for a real cluster --- # Install Source: https://hevlayer.com/docs/install import Callout from "../../components/docs/Callout.astro"; import FeatureGate from "../../components/docs/FeatureGate.astro"; A hev layer install has two stages. **Terraform** provisions the required AWS resources: IAM, S3, ECR, networking, cost-read roles, and, for the recommended path, a fresh EKS cluster. **Helm** installs the gateway, operator, and document cache into that cluster and wires them to the AWS resources Terraform produced. One command runs both stages: `layer install`. It is the only install entrypoint. You can skip Terraform if you already have the AWS resources hev layer needs. At minimum, provide an S3 bucket and gateway IRSA role for snapshots and history. For the full feature set, also provide gateway cost-read IAM, image registry locations, and cluster-level components equivalent to the Terraform outputs. ## layer install Prerequisites: `aws` (with credentials configured), `terraform`, `helm`, and `kubectl` on your PATH, plus your upstream Turbopuffer API key. Clone the `hev/layer` repository — it carries the Terraform configuration and the Helm chart the command drives — and run `layer install` from the checkout: ```sh git clone https://github.com/hev/layer cd layer go build -o layer ./apps/layer-cli export TURBOPUFFER_API_KEY="tpuf_..." ./layer install ``` AWS credentials come from the ambient environment by default; pass `--aws-profile` (or set `AWS_PROFILE`) to select a named credentials profile. Missing required inputs prompt interactively on a TTY. The command prints the resolved plan — profile, region, cluster name, system node shape — and asks for confirmation before provisioning anything. Then it runs both stages in one shot: Terraform provisions the opinionated AWS footprint — VPC, EKS, IAM/IRSA, S3, ECR — and Helm installs the release wired to those outputs, waiting for the gateway rollout before printing next steps. | Flag | Environment | Default | | --- | --- | --- | | `--profile` | `LAYER_INSTALL_PROFILE` | `demo` | | `--aws-profile` | `AWS_PROFILE` | ambient credentials | | `--region` | `AWS_REGION` | `us-east-1` | | `--cluster-name` | `CLUSTER_NAME` | `layer` | | `--namespace` | `NAMESPACE` | `layer` | | `--helm-release` | `HELM_RELEASE` | `layer` | | `--node-type` | `SYSTEM_NODE_INSTANCE_TYPE` | profile default (`m8g.large`) | | `--turbopuffer-api-key` | `TURBOPUFFER_API_KEY` | none (required) | | `--version` | `LAYER_VERSION` | `latest` | | `--license-token` | `LICENSE_TOKEN` | none | | `--dashboard-user` | `DASHBOARD_USER` | `admin` | | `--dashboard-password` | `DASHBOARD_PASSWORD` | generated and printed | | `--source` | `LAYER_SRC` | walk up from the working directory | | `--skip-terraform` | none | off | | `--yes` | none | off | | `--dry-run` | none | off | Non-interactive installs (CI, scripts) pass every value as a flag or environment variable and must add `--yes`; without a terminal the command refuses to provision unconfirmed. `--dry-run` prints the resolved plan and the exact commands it would run, then exits. `--skip-terraform` reuses existing Terraform outputs and reruns only the cluster components and the Helm release; on that path, the saved cluster name and namespace are authoritative. ### Profiles `--profile` selects the install footprint. `demo` is the default. **`demo`** is the lean evaluation footprint. The always-on system node is one arm64 `m8g.large` with an 80 GiB gp3 data volume. The document cache runs on that system pool, backed by a gp3 PVC (80 Gi) instead of NVMe instance store; no dedicated cache node pool is created. Every Pro feature — gateway, operator, dashboard, extractions, snapshots, keys — runs the same as on the larger footprint. Always-on cost is roughly $40–70/mo on current us-east-1 on-demand pricing. **`indexing`** is the design-partner footprint for installs with heavy indexing duty cycles. It adds a dedicated document-cache Karpenter pool on arm64 `i4g.large` NVMe instance-store nodes, so the cache gets local flash instead of a gp3 volume. Pass `--node-type` to override the system node instance type on either profile. The cache is ephemeral on both profiles (`resetOnStart: true`); losing the volume or the node is a non-event. ### install status `layer install status` reports the installed release and workload health: Helm release status, pod readiness, and document-cache node age, type, and utilization — so a forgotten instance-store node shows up instead of billing quietly. ```sh ./layer install status ``` It takes `--aws-profile`, `--region`, `--cluster-name`, `--namespace`, and `--helm-release`, with the same environment variables and defaults as `layer install`. ### install uninstall `layer install uninstall` removes the Helm release and cluster components and destroys the Terraform footprint. It confirms before destroying (pass `--yes` to skip) and is idempotent. With `--skip-terraform` it removes only the in-cluster install and retains the AWS footprint. ```sh ./layer install uninstall ``` The rest of this page is the reference for what the install sets up and the bring-your-own-cluster alternative. ## Install shape An install is one Helm release per environment with one S3 bucket for snapshot and history data. The chart renders a default [`VectorStore`](/docs/kubernetes/vectorstore-crd) from the credential you provide; an install can define additional `VectorStore` resources, each with its own upstream credential and inbound auth policy, and route namespaces between them with `Index.spec.backend.storeRef`. Scoped gateway-only bearer keys are available through the `keys` inbound auth mode described below. ## Terraform The Terraform configuration in `infra/terraform/` provisions the AWS resources that the gateway and operator need. It is opinionated about the resources hev layer needs to behave correctly and conservative about resources around it. Route53 hosted zones and ACM certificates are opt-in; most installs bring existing DNS and TLS. ### What it sets up | Resource | Purpose | | --- | --- | | S3 bucket | Durable storage for namespace snapshots, search history, and clickstream events. | | IAM roles + IRSA policies | Gateway S3 and Cost Explorer access, plus worker/operator AWS access. | | ECR repositories | Registry space for customer-built function and pipeline worker images. Layer-owned gateway, operator, and dashboard images are pulled from Docker Hub. | | EKS + VPC + node pools | Recommended fresh-cluster runtime. | | Route53 + ACM | Optional DNS zones, records, and TLS certificates when `manage_public_dns=true`. | ### Cluster: recommended Installs should use a fresh EKS cluster unless there is a specific reason to bind hev layer to an existing one. The cluster path provisions: - a VPC with the subnets and endpoints hev layer expects - an EKS control plane and one always-on `system` node group, defaulting to an arm64 `m8g.large` with an 80 GiB gp3 data volume - public worker subnets by default, with no NAT Gateway in the fresh cluster path - Karpenter for scale-from-zero `worker-cpu` and `worker-gpu` indexing capacity - the AWS EBS CSI add-on and a `gp3` StorageClass for the demo-profile document-cache PVC - the AWS Load Balancer Controller for ingress - EFS for shared persistent volumes If you already operate an EKS cluster, you can disable the cluster modules and point hev layer at the existing cluster. You are still responsible for the functional prerequisites: an S3 bucket for snapshots/history, gateway IRSA that can read/write that bucket and call AWS Cost Explorer for tag-scoped cost reads, Docker Hub pull access for Layer-owned images, registry access for worker images, Karpenter or equivalent node autoscaling for workers, and the AWS Load Balancer Controller if you use public ingress. Deploy hev layer to a fresh cluster. The baseline is one always-on ARM node for the gateway, dashboard, control loops, and document cache. CPU and GPU indexing workers scale from zero, so embedding and extraction cost follows indexing duty cycle instead of becoming a standing line item. The fresh path also avoids a NAT Gateway: workers run in public subnets, so the large data volumes that flow during indexing skip NAT's per-GB processing charge. Existing clusters typically route worker egress through NAT already, which turns every pipeline run into a metered transfer. ### Cost notes The Terraform is designed to deploy a cost-efficient AWS footprint with autoscaling for on-demand indexing work. At rest, the fixed costs are EKS, one small ARM `system` node, the shared ALB, and small storage lines — roughly $40–70/mo on current us-east-1 on-demand pricing for the demo profile, before variable traffic, object storage, and upstream vector-store usage. The indexing profile adds the dedicated document-cache node while it runs. Indexing bursts scale CPU or GPU worker nodes up through Karpenter and back down when queues drain. If you switch workers to private subnets, enabling NAT adds a standing hourly and egress cost. Heavier search use cases may need more read-side infrastructure: additional gateway replicas, larger always-on nodes, or a dedicated document-cache pool for steady cache pressure. Contact hev layer for help sizing read-heavy deployments. ### Outputs Terraform emits the values the Helm chart needs to install: the S3 bucket name, gateway IRSA role ARN, and cluster metadata. Runtime images are pulled from Docker Hub for the Layer-owned gateway, operator, and dashboard containers. Pass these into the Helm values file described below. The Terraform provider tags managed resources with `Project=hevlayer`; activate that tag as a cost-allocation tag in AWS Billing so the gateway can scope Cost Explorer reads to the Layer stack. ## Helm The Helm chart at `infra/helm/layer/` installs the gateway, operator, and document cache into a cluster that already has the AWS resources from [Terraform](#terraform) or equivalent resources you manage. `layer install` applies the chart with the profile overlay (`values-demo.yaml` or `values-indexing.yaml`) plus a generated values file; a bring-your-own-cluster install runs Helm directly with the same values. ### Local gateway development `docker compose` starts the gateway's local dependencies, not a replacement control plane. The gateway still resolves [`VectorStore`](/docs/kubernetes/vectorstore-crd) and [`Index`](/docs/kubernetes/index-crd) resources from Kubernetes at startup, so a compose-based gateway run needs a current kube context with Layer CRDs installed and the matching `VectorStore`, `Index`, and Secret objects applied. For a local search backend running on the compose network, use a local-only `VectorStore.endpoint.url` that the gateway container can reach; cluster Service DNS names only work inside Kubernetes. ### Required values Most of the chart is opinionated defaults. In a typical install the credential you bring from outside the cluster becomes the default `VectorStore` credential. | Value | Required | Notes | | --- | --- | --- | | `vectorStore.credential.apiKey` | yes | Upstream store credential. With the default `deriveFromStore` auth mode, clients also send this as the gateway bearer key. | | `vectorStore.endpoint.url` | yes | Upstream store API base URL. Defaults to turbopuffer's AWS us-east-1 endpoint. | | `vectorStore.endpoint.region` | yes | Region label for the rendered `VectorStore`. | | `vectorStore.inboundAuth.mode` | no | `deriveFromStore`, `keys`, or `open`. Defaults to `deriveFromStore`. | | `vectorStore.inboundAuth.keys` | for `keys` mode | Gateway-only bearer keys with `read`, `write`, and `admin` scopes. | | `search.enabled` | no | Installs the Layer-operated `search` backend in-cluster and, when `vectorStore.kind=search` and `vectorStore.endpoint.url` is blank, points the default `VectorStore` at that Service. | | `search.image` | for `search.enabled` | Container image for the `search` backend, distributed separately from the Layer repo images. The chart rejects `ghcr.io/hev/*`. | | `gateway.image` | yes | Gateway image URL. Customer installs pull the pro image from Docker Hub, `hevlayer/layer-gateway-pro:`. | | `operator.image` | for `operator.enabled` | Operator image URL. Customer installs pull `hevlayer/layer-operator:` from Docker Hub. | | `dashboard.image` | for `dashboard.enabled` | Dashboard image URL. Customer installs pull `hevlayer/layer-dashboard:` from Docker Hub. | | `license.token` | trial/commercial installs | Signed hev layer license key from the trial or commercial license email. The chart writes it to a Kubernetes Secret and surfaces it to the gateway. | | `license.existingSecret` / `license.secretKey` | optional | Existing Secret name and key containing the license key, for clusters where secret material is managed outside Helm. | | `s3.bucket` | yes | S3 bucket Terraform created for snapshots and history. | | `serviceAccount.roleArn` | yes | IRSA role ARN that grants the gateway access to the S3 bucket and Cost Explorer. | | `dashboard.serviceAccount.name` | yes | Dashboard ServiceAccount name from Terraform output `layer_dashboard_service_account_name`. | | `dashboard.serviceAccount.roleArn` | yes | Dashboard IRSA role ARN from Terraform output `layer_dashboard_role_arn`; this renders the EKS role annotation on the dashboard ServiceAccount at first boot. | | `documentCache.storage.pvc.enabled` | no | Back the document cache with a PVC (`size`, `storageClass`) instead of node-local instance store. The demo profile sets an 80 Gi `gp3` PVC. | | `documentCache.nodeRole` | no | `system` schedules the cache on the always-on system pool (demo profile); `document-cache` targets a dedicated pool (indexing profile). | | `documentCache.karpenter.enabled` | no | Provision a dedicated document-cache Karpenter pool. The indexing profile enables it with `instanceType: i4g.large`. | | `gateway.indexNamespace` | no | Namespace containing `Index` CRs. Blank follows `operator.discovery.indexNamespace`, then the Helm release namespace. | | `gateway.indexConfig.enabled` | no | Enables gateway reads of `Index` CR routing and policy such as `spec.backend.storeRef`, `spec.snapshot.facetFields`, and `spec.scan.threads`. | | `gateway.indexGc.enabled` | no | Enables namespace hard-delete cleanup of operator-discovered `Index` CRs. | | `gateway.consistency.stablePollIntervalMs` | no | Slow polling cadence for namespaces last observed stable. Defaults to `60000`; cold and updating namespaces keep the fast gateway default. | | `gateway.cost.tagKey` / `gateway.cost.tagValue` | no | Cost-allocation tag filter for AWS Cost Explorer. Defaults to `Project=hevlayer`. | | `ingress.host` | optional | Set when you want a public ingress; use your DNS/TLS or enable Terraform-managed Route53/ACM. | Most other Helm inputs are wiring between resources the install process already produced. The store API key is the credential hev layer cannot generate for you. The chart stores it in a Kubernetes Secret, points the default `VectorStore` at that Secret, and the gateway derives its default inbound bearer from the same key. After you [start a trial](/#start-trial), hev layer emails a signed license key. Set it as `license.token` or store it in an existing Secret referenced by `license.existingSecret` and `license.secretKey`. The gateway exposes local license health at [`/v2/license`](/docs/licensing#license-states), with `licensed`, `grace`, and `floor` states. ### Image Coordinates The customer pull path for the Layer-owned runtime images is Docker Hub: ```yaml gateway: image: hevlayer/layer-gateway-pro: operator: enabled: true image: hevlayer/layer-operator: dashboard: enabled: true image: hevlayer/layer-dashboard: ``` These pro images are public to pull, but licensed surfaces are only useful with a valid hevlayer license key installed through `license.token` or `license.existingSecret`. Without a valid key, the gateway, operator, and dashboard project the install to the license floor described in [Licensing](/docs/licensing#license-states). The in-cluster `search` backend image is distributed separately from the Layer repo images. Demo worker images are not part of the first Docker Hub release lane; build customer Function and Pipeline images into the registry your cluster already pulls from. ### Layer-Operated Search Set `search.enabled=true` to run the `search` backend beside the gateway. The chart uses the existing service account/IRSA, stores backend data under `s3:///search` by default, mounts a node-local object cache, adds Prometheus scrape annotations for the bundled vmsingle, and restricts backend ingress to Layer's own components (gateway, operator, and metrics scraper). The in-chart backend takes no credential of its own and is reachable only through that internal connection. For a default `kind: search` VectorStore, set `vectorStore.inboundAuth.mode` to `keys` or `open`; `deriveFromStore` is only valid when the upstream store has its own credential. ### Gateway auth modes The default `deriveFromStore` mode is the single-tenant BYOC path: ```yaml vectorStore: credential: apiKey: tpuf_... inboundAuth: mode: deriveFromStore ``` For an install that needs a gateway-only bearer, use `keys` mode. The chart renders `apiKey` values into the release Secret and references them from the `VectorStore`; omit `apiKey` when pointing at a pre-created Secret. ```yaml vectorStore: credential: apiKey: tpuf_... inboundAuth: mode: keys workerSecretKey: layer-inbound-worker-api-key keys: - name: worker scopes: [read, write, admin] apiKey: layer_worker_... secretRef: key: layer-inbound-worker-api-key ``` In `keys` mode, operator workers, KEDA, and the dashboard use `workerSecretName` / `workerSecretKey` as their gateway bearer. Blank `workerSecretName` uses the release Secret; blank `workerSecretKey` uses `layer-inbound-worker-api-key`. ### Run the install On a bring-your-own cluster, run Helm directly with a profile overlay and your values: ```sh helm upgrade --install layer ./infra/helm/layer \ --namespace layer --create-namespace \ -f ./infra/helm/layer/values-demo.yaml \ -f values.customer.yaml ``` The chart is not published to a public Helm repository — install from the source path or from the chart artifact provided during onboarding. ### What gets installed - `layer-gateway` — Rust gateway for turbopuffer-compatible routes, fetch, scans, snapshots, warm jobs, and pipeline state. - `layer-operator` — reconciler for VectorStore, Index, InfraRules, Pipeline, and Function CRDs documented in [Kubernetes](/docs/kubernetes/operator). - `layer-document-cache` — Aerospike-backed document cache, scale-to-zero by default. On the demo profile it runs on the system pool over a gp3 PVC; on the indexing profile it gets a dedicated instance-store node pool. - Optional Karpenter `NodePool` / `EC2NodeClass` resources for `worker-cpu` and `worker-gpu` indexing capacity when `workerKarpenter.enabled=true`, and the dedicated `document-cache` pool when the indexing profile (or `documentCache.karpenter.enabled=true`) is in effect. - Supporting resources: service accounts, IRSA bindings, ingress, and CRDs. ### Built-in compute pools The operator supplies built-in compute pools when no cluster-scoped `InfraRules/default` object exists. Helm does not create that object. If a workload omits `scaling.pool`, the operator maps `worker.computeClass: cpu` or `gpu` to the stock `cpu` or `gpu` pool. The default compute pools are: | Pool | Use | | --- | --- | | `cpu` | General CPU workers such as extraction, ingestion, and lightweight Functions. | | `cpu-large` | CPU workers that need local ephemeral-storage headroom for per-pod source caches. | | `gpu` | One-NVIDIA-GPU workers for embedding and model inference. | The built-in pools select `layer.hev.dev/node-role=worker-cpu` or `worker-gpu`, matching the chart's `workerKarpenter` NodePools. Override them by applying `InfraRules/default` when you need to tune resource requests, limits, node selectors, tolerations, GPU SKU hints, or per-workload replica ceilings for your cluster. See [InfraRules CRD](/docs/kubernetes/scaling-crd) for the full field shape. --- # Operator Overview Source: https://hevlayer.com/docs/kubernetes/operator `layer-operator` manages declarative state for your hev layer deployment. It serves a few crucial functions — monitoring for changes to your indexes and managing scaling. It does this through a set of abstractions known as [custom resource definitions (CRDs)](/docs/concepts#glossary). The gateway handles the read and write path; the operator handles everything that wants to be expressed as desired state in the cluster: which vector store the gateway fronts, which indexes exist, how worker pools scale, and which stateless functions run against which indexes. ## CRDs The operator reconciles five resource kinds, each documented on its own page: - [VectorStore CRD](/docs/kubernetes/vectorstore-crd) — the upstream store endpoint, credential reference, and gateway inbound auth policy. - [Index CRD](/docs/kubernetes/index-crd) — one resource per turbopuffer namespace the gateway should manage. - [InfraRules CRD](/docs/kubernetes/scaling-crd) — cluster-wide compute pools, document cache rules, and shared scaling policy. - [Pipeline CRD](/docs/kubernetes/pipeline-crd) — staged work that changes row count. - [Function CRD](/docs/kubernetes/function-crd) — stateless user-defined functions that read and write attributes on an index. ## Relationship to the gateway The gateway and the operator are decoupled. The operator reconciles declarative state; the gateway serves the read and write path. Neither sits in the other's hot path, so the gateway keeps serving even if the operator is restarted or lagging. The link between them is one-directional and read-only. For some features the gateway reads CRD status, such as which indexes exist and which worker pools are ready, to inform what it serves. It never writes to the CRDs; declarative state is authored by you and reconciled by the operator, and the gateway is only ever a reader of it. ## Scheduling and node pools The operator applies the compute pool chosen by each Pipeline and Function. A pool can set container resources, `nodeSelector`, and `tolerations`, so operators can pin CPU, storage-heavy CPU, and GPU work to the right node capacity. The operator supplies built-in `cpu`, `cpu-large`, and `gpu` pools when no `InfraRules/default` exists. The stock pools select the chart-rendered Karpenter worker pools: `layer.hev.dev/node-role=worker-cpu` for CPU and `layer.hev.dev/node-role=worker-gpu` for GPU. The GPU pool also requests `nvidia.com/gpu: "1"` and carries the standard NVIDIA toleration. Custom placement is configured once on `InfraRules/default`, not per workload — see [InfraRules](/docs/kubernetes/scaling-crd) for the compute-pool fields and how Pipelines and Functions choose a pool. --- # VectorStore CRD Source: https://hevlayer.com/docs/kubernetes/vectorstore-crd import FeatureGate from "../../../components/docs/FeatureGate.astro"; import Callout from "../../../components/docs/Callout.astro"; A `VectorStore` is the gateway's upstream connection. It names the store kind, endpoint, credential Secret, and the inbound auth policy the gateway applies to client requests. An install may define more than one `VectorStore`; each `Index.spec.backend.storeRef` selects which store serves that upstream namespace. ```yaml apiVersion: hevlayer.com/v1alpha1 kind: VectorStore metadata: name: turbopuffer-default namespace: layer spec: kind: turbopuffer default: true endpoint: url: https://aws-us-east-1.turbopuffer.com region: aws-us-east-1 turbopuffer: orgId: org_123 credential: secretRef: name: layer key: turbopuffer-api-key inboundAuth: mode: deriveFromStore ``` ## Standalone config **Why this exists:** the open [`layer` (CE)](https://github.com/hev/layer) runs standalone — Docker, Compose, or a bare binary — with no Kubernetes control plane available. Config-derived resolution gives those deployments the exact same `VectorStore` vocabulary as a cluster install: one schema, two entry points, so a standalone config file promotes to a CR (and back) without rewrites. Standalone and compose runs do not need Kubernetes to resolve `VectorStore`s. Set `LAYER_STORE_FILE` to a YAML or JSON file containing the same resource shape as the CR: ```yaml apiVersion: hevlayer.com/v1alpha1 kind: VectorStore metadata: name: turbopuffer-default spec: kind: turbopuffer default: true endpoint: url: https://api.turbopuffer.com region: aws-us-east-1 credential: secretRef: name: layer key: turbopuffer-api-key inboundAuth: mode: deriveFromStore ``` For this example, set `LAYER_SECRET_LAYER_TURBOPUFFER_API_KEY=tpuf_...` before starting the gateway. The env var name is `LAYER_SECRET_` plus the Secret name and key uppercased, with punctuation replaced by underscores. For more than one store, use a Kubernetes-style list: ```yaml apiVersion: v1 kind: List items: - apiVersion: hevlayer.com/v1alpha1 kind: VectorStore metadata: name: turbopuffer-default spec: kind: turbopuffer default: true endpoint: url: https://api.turbopuffer.com region: aws-us-east-1 credential: secretRef: name: layer key: turbopuffer-api-key inboundAuth: mode: deriveFromStore ``` The standalone file mirrors the CR schema. Kubernetes installs read `credential.secretRef` from the cluster; standalone runs resolve the same `secretRef` from env. Inline `LAYER_STORE_JSON` accepts the same resource shape for short-lived local runs. If neither variable is set, the gateway uses a single default turbopuffer store and derives inbound auth from the request bearer. ## Connection | Field | Purpose | | --- | --- | | `kind` | The backend engine. `turbopuffer` today. `pinecone` is reserved by the schema but rejected by the operator until implemented. | | `default` | Marks the store used when an `Index` omits `spec.backend.storeRef`. A single store is treated as the default. | | `endpoint.url` | Upstream API base URL. | | `endpoint.region` | Operator-visible region label for this store. | | `turbopuffer.orgId` | Optional turbopuffer organization id for dashboard deep links and support orientation. It is not used for auth or routing. | | `credential.secretRef` | Secret key in the same namespace as the `VectorStore`. The credential is never stored in the CRD. | ## Routing The gateway builds one upstream client per `VectorStore` in the namespace. Requests whose namespace has an `Index` with `spec.backend.storeRef` use that store; other namespaces use the default store. Two `Index` objects cannot resolve to the same upstream namespace. ## Inbound auth `inboundAuth.mode` controls what bearer token the gateway accepts: | Mode | Behavior | | --- | --- | | `deriveFromStore` | Default. The gateway accepts the default store's credential as the inbound bearer. This is the single-tenant BYOC shape. | | `keys` | The gateway accepts the listed independent key Secrets and enforces their `read`, `write`, and `admin` scopes. | | `open` | No inbound auth. Use only for explicitly open environments. | Under `deriveFromStore`, clients set `Authorization: Bearer ` when calling the gateway. Operator-managed workers and KEDA use the same Secret through `LAYER_GATEWAY_API_KEY`. Under `keys`, each key points at a Secret in the same namespace: ```yaml spec: inboundAuth: mode: keys keys: - name: shop-rw scopes: [read, write] secretRef: name: layer key: layer-inbound-shop-rw-api-key ``` `read` covers GET/HEAD routes and read-shaped POST routes such as query, batch fetch, scans, and metrics proxy queries. `write` covers namespace writes and worker queue claim/complete routes. `admin` covers Pipeline and Function create/delete/control routes and also satisfies `read` and `write`. In every mode the gateway also accepts a minted [`ApiKey`](/docs/kubernetes/apikey-crd) token whose `vectorstore.` entitlement names this store, enforcing that entitlement's scopes and namespace globs. ## Status The operator sets `status.reachable` and a `Ready` condition after validating the Secret references and probing `GET /v1/namespaces` on the store endpoint. --- # ApiKey CRD Source: https://hevlayer.com/docs/kubernetes/apikey-crd An `ApiKey` is a minted credential as a resource. Layer owns the credential lifecycle — mint, verify, revoke, expire — and what the key opens is declared per resource: each entitlement names a [`VectorStore`](/docs/kubernetes/vectorstore-crd), a [`Warehouse`](/docs/kubernetes/warehouse-crd), an [`Agent`](/docs/kubernetes/agent-crd), or Layer itself, and carries the scopes and claims for that target. Claims are opaque to Layer — an external system can use Layer as its key store and keep authorization decisions to itself. Keys have two authoring surfaces that round-trip through one schema: `kubectl get apikey -o yaml` and `GET /v2/keys/{keyId}` are two spellings of the same object. ```yaml apiVersion: hevlayer.com/v1alpha1 kind: ApiKey metadata: name: cohort-reader namespace: layer spec: owner: acme description: cohort read access entitlements: vectorstore.prod-turbopuffer: scopes: [read] namespaces: ["cohort-*"] warehouse.prod-snowflake: claims: - "notes:cohort:*:read" expiresAfter: 365d status: keyId: 0a1b2c3d-… phase: Active lookupHash: sha256:… createdAt: "2026-06-10T00:00:00Z" expiresAt: "2027-06-10T00:00:00Z" secretRef: name: apikey-cohort-reader ``` ## Spec | Field | Purpose | | --- | --- | | `owner` | Optional free-form owner label, echoed in list and authenticate responses. | | `description` | Optional free-form description. | | `entitlements` | Map keyed by target resource. Each entry carries `scopes`, `namespaces`, and `claims` for that target. | | `expiresAfter` | Duration or `never`. Defaults to `365d`; `status.expiresAt` is computed at mint. | ## Entitlements | Key | Target | | --- | --- | | `vectorstore.` | Data-plane access through the named store. `scopes` (`read`, `write`) gate routes whose `Index` resolves to that store; `namespaces` globs constrain which upstream namespaces. | | `warehouse.` | A list of opaque `claims` strings bound to the source system. Layer stores and echoes them; the application routes on them. No client route reaches a source — clients touch indexes, not warehouses — so the entitlement grants nothing in Layer and inerts when the warehouse is deleted. | | `agent.` | Invocation of the named [`Agent`](/docs/kubernetes/agent-crd): the right to call `POST /v2/agents//query`. Data access is separate — the agent's reads run under the caller's own `vectorstore` grants — so this governs *who may invoke*, not *what it reads*. Inerts when the agent is deleted. | | `layer` | The control plane itself. `scopes: [admin]` covers key management and Pipeline/Function create/delete/control routes, and satisfies `read` and `write` everywhere. | Scope meanings match [inbound auth](/docs/kubernetes/vectorstore-crd#inbound-auth): `read` covers query, fetch, scans, and metrics; `write` covers namespace writes and worker routes. `claims` is a list of opaque strings, allowed on any entitlement and the only field on a warehouse entitlement. Layer stores them, returns them from list, get, and authenticate, and never interprets them — an existing permission grammar (`service:resource_type:resource_id:action` strings, a legacy entitlement vocabulary) drops in verbatim, and the consuming application maps them to its own authorization. An entitlement whose target does not exist grants nothing and surfaces as a status condition (`EntitlementTargetMissing`) — not an admission error, so keys and their targets can be applied in either order. Check the condition after applying: a typo in a target name looks the same as a missing target. A key whose entitlements carry only claims — no scopes — is a pure external-store key: it authenticates, but opens no Layer route. ## Minting **REST.** `POST /v2/keys` generates the token, creates the `ApiKey` resource, and returns the token in the response — once. The raw token is never persisted; Layer stores only one-way hashes on the resource. ```http POST /v2/keys # 201 { keyId, …, token } — token returned once GET /v2/keys # metadata only; ?includeRevoked GET /v2/keys/{keyId} POST /v2/keys/{keyId}/revoke # idempotent DELETE /v2/keys/{keyId} # hard delete; Revoked keys only POST /v2/keys/authenticate # body { token } → 200 { keyId, entitlements, … } | 401 ``` Key-management routes require a key with the `layer` entitlement at `admin` scope. `POST /v2/keys/authenticate` is unauthenticated by construction — the token is the credential. **CRD.** Apply an `ApiKey` with no credential. The operator mints the token, writes it to a Secret named in `status.secretRef` (key `token`), and moves `phase` from `Pending` to `Active`. The Secret is the token delivery; it is owned by the `ApiKey` and garbage-collected with it. Rotate by minting a replacement, deploying it, and revoking the old key. ## Verification External systems present the raw token to `POST /v2/keys/authenticate` and get back `keyId` (a stable actor id) plus the full `entitlements` map, then make their own authorization decisions from the claims. The gateway also accepts any `Active` key's token as a bearer on its own routes, enforcing the entitlement for the store or control-plane surface the route resolves to. Verification is one indexed lookup plus one hash check against a watch-fed in-memory map — the hot path never reads the control plane per request. `status.lastSeenAt` advances at most once per five minutes per key. | Phase | Meaning | | --- | --- | | `Pending` | CRD-authored key awaiting mint. | | `Active` | Verifiable; token works. | | `Revoked` | `POST /v2/keys/{keyId}/revoke` was called; token refused. | | `Expired` | `status.expiresAt` passed; token refused. | Revoke is the default lifecycle end-state. Revoked `ApiKey` resources are retained indefinitely for audit and are not automatically garbage-collected. Operators should set a retention policy appropriate to their obligations. Hard delete is rare and deliberate: `DELETE /v2/keys/{keyId}` accepts only a `Revoked` key and permanently removes the resource, its lifecycle record, and any owned token Secret. Other phases return `409 Conflict`; revoke first, then delete only after the retention period has passed or policy requires the record itself to be forgotten. Deleting a `VectorStore` or `Warehouse` inerts every entitlement that names it: the keys stay `Active` for their other entitlements, and the deletion is finalizer-guarded on the target's side while keys still reference it. ## Backup and migration Use a full namespace Velero backup, or an equivalent etcd snapshot, for portable `ApiKey` migration. Do not export and apply YAML: the verifier lives in `status`, and apply paths do not preserve that subresource. 1. Back up these Layer namespace objects: - `ApiKey` resources with `status`: `status.keyId`, `phase`, `lookupHash`, and `tokenHash` are the credential record; the raw token is not recoverable. - The chart's `*-keys` Secret: it holds `LAYER_KEY_PEPPER`, which must match every restored verifier. - Token-delivery Secrets referenced by CRD-authored keys through `status.secretRef`. 2. Restore `ApiKey` status explicitly. Velero restores specs by default and drops `status` unless the restore uses `--status-include-resources apikeys.hevlayer.com`, or the `Restore` sets `spec.restoreStatus.includedResources: ["apikeys.hevlayer.com"]`. 3. Restore the namespace, including the pepper Secret, before running Helm on the target release. If Helm touches an empty namespace first, it can generate a new random pepper, making restored argon2id verifiers unusable. 4. If source and target Velero servers share object storage, wait until the target server lists the completed source backup before creating the restore; otherwise its local `Backup` object may not have synced. 5. Refresh source Velero discovery before backup. Install Layer CRDs before Velero discovers resources, or restart the source Velero deployment after CRD installation and wait for readiness; stale discovery can produce a `Completed` backup that omits `ApiKey`. 6. Quiesce the Layer operator before backup and keep it quiesced through initial restore. Velero restores `ApiKey` spec and `status` separately; an operator that starts in that window can mark a transient spec-only key `MintBlocked` or `Pending`. 7. Inspect the completed backup from inside the Velero server's cluster. Its item list must include `apikeys.hevlayer.com`, expected `ApiKey` objects, the chart's `*-keys` pepper Secret, and token-delivery Secrets referenced by `status.secretRef`. 8. Restart gateway/operator discovery after restore so the watch-fed key map rebuilds from restored `ApiKey` status and Secrets before traffic depends on the migrated tokens. 9. Treat `MintBlocked` as a failed restore, not a key rotation. The operator refuses to mint a replacement when `status.keyId` is empty but the deterministic delivery Secret exists; it writes a `MintBlocked` condition and warning Event instead. A new CRD-authored `ApiKey` with no delivery Secret still mints normally. Validate the key store before accepting traffic: ```sh SOURCE_CONTEXT=layer-a \ TARGET_CONTEXT=layer-b \ LAYER_NAMESPACE=layer \ SOURCE_BASE_URL=https://source.example.com \ TARGET_BASE_URL=https://target.example.com \ SOURCE_ADMIN_KEY=hvl_... \ scripts/apikey-velero-migration.sh ``` The script mints REST and CRD-authored keys, backs up the namespace, waits for the target Velero server to sync the completed backup, restores with `--status-include-resources apikeys.hevlayer.com`, and asserts: - original active tokens still authenticate - revoked and expired tokens remain refused - the completed backup contains expected `ApiKey` resources and Secrets - the source operator was quiesced and the expired control key reached `Expired` before backup - the restored pepper Secret matches the source - restored key status fields are present, so spec-only, incomplete, racing, or misordered restores fail loudly Local reproduction currently needs the temporary test-license setup from the `/v2/keys` test `kid` keygen work in PR #199. Velero backups are credential-bearing: they contain one-way verifiers, the pepper, and any remaining CRD-authored delivery Secrets. Store and encrypt them like other credential material. ## Kubernetes RBAC CRD authoring makes kubectl a minting surface, so the chart ships roles to delegate key administration without cluster-admin: | ClusterRole | Grants | | --- | --- | | `hevlayer-key-admin` | Full verbs on `apikeys`, plus `get` on delivered token Secrets. Can mint, revoke, and collect tokens. | | `hevlayer-key-viewer` | `get`/`list`/`watch` on `apikeys`. No Secret access — status hashes are one-way, so viewing is audit, not credential access. | Neither role aggregates into the built-in `view`/`edit`/`admin` ClusterRoles: namespace viewer never silently means key viewer. Bindings are the cluster operator's explicit act; set `rbac.keyRoleBindings` in Helm values to render them for the single-team case. ## Bootstrapping `LAYER_GATEWAY_API_KEY` is the bootstrap credential: it mints the first admin key — ```yaml spec: entitlements: layer: scopes: [admin] ``` — after which routine minting uses minted admin keys. Cluster operators can equally bootstrap by applying an `ApiKey` resource, since CRD authoring needs only kubectl access. --- # Warehouse CRD Source: https://hevlayer.com/docs/kubernetes/warehouse-crd A `Warehouse` declares an upstream source system — the system of record pipelines extract rows from, plus the verified reachability and credential shape needed to reach it. Data in Layer is derived from a warehouse and reconstructible from it. The serving side is the [`VectorStore`](/docs/kubernetes/vectorstore-crd); the two sit on opposite sides of the gateway. ## Supported Warehouses | Kind | Status | Source | | --- | --- | --- | | `snowflake` | Shipped | Snowflake databases and tables, key-pair credential | | `huggingface` | Shipped | Hugging Face Hub datasets, public or token-gated | | `rest` | Shipped | Any paginated JSON HTTP API | | `databricks` | Reserved | Schema-reserved; rejected by the operator until implemented | | `iceberg` | Reserved | Schema-reserved; rejected by the operator until implemented | Snowflake warehouses hold a key-pair credential. Hugging Face dataset warehouses can be public and credentialless, or can point at a read-token Secret for gated/private datasets. REST warehouses declare a JSON HTTP API — a base URL, optional auth, and a pagination rule — so any paginated JSON API is a source without a bespoke kind. ```yaml apiVersion: hevlayer.com/v1alpha1 kind: Warehouse metadata: name: prod-snowflake namespace: layer spec: kind: snowflake snowflake: account: acme-xy12345 user: SVC_LAYER role: SVC_LAYER_ROLE warehouse: EXTRACT_WH keyPairSecretRef: name: snowflake-rsa pool: size: 5 timeout: 30s verifyInterval: 1h ``` ```yaml apiVersion: hevlayer.com/v1alpha1 kind: Warehouse metadata: name: huggingface-hub namespace: hev-shop spec: kind: huggingface huggingface: endpoint: https://huggingface.co # tokenSecretRef is optional for public datasets. # tokenSecretRef: # name: hf-read # key: token verifyInterval: 1h ``` ```yaml apiVersion: hevlayer.com/v1alpha1 kind: Warehouse metadata: name: openfda namespace: layer spec: kind: rest rest: baseUrl: https://api.fda.gov userAgent: hevlayer/rest-source (+https://hevlayer.com/contact) # auth is optional — omit for open APIs. # auth: # in: query # query | header # name: api_key # the param/header carrying the credential # secretRef: # name: openfda-key # key: token rateLimit: requestsPerSecond: 4 verify: path: /drug/label.json # a cheap GET the operator probes query: limit: "1" verifyInterval: 24h ``` ## Connection `spec.kind` selects the source system. `snowflake`, `huggingface`, and `rest` select a source; `databricks` and `iceberg` are reserved by the schema but rejected by the operator until implemented. Exactly one kind block must be present and match `spec.kind`. `verifyInterval` (default `1h`) sets the probe cadence for any kind. A warehouse is identity and credential — not a catalog. What to read — a Snowflake database and table, a Hugging Face dataset and split — belongs to the [pipeline source](#pipeline-source); one credential reaches many. ### Snowflake | Field | Purpose | | --- | --- | | `kind` | `snowflake`, `huggingface`, or `rest`. `databricks` and `iceberg` are reserved by the schema but rejected by the operator until implemented. | | `snowflake.account` | Snowflake account identifier. | | `snowflake.user` | Service user the key pair authenticates. | | `snowflake.role` | Optional role assumed on connect. | | `snowflake.warehouse` | Snowflake compute warehouse extraction queries run on. | | `snowflake.keyPairSecretRef` | Secret in the same namespace holding `private-key.pem` and optional `passphrase`. The credential is never stored in the CRD. | | `snowflake.pool` | Connection pool tuning: `size`, `timeout`. | | `huggingface.endpoint` | Hub endpoint. Defaults to `https://huggingface.co`; override for Enterprise Hub or a mirror. | | `huggingface.tokenSecretRef` | Optional Secret in the same namespace holding `token` for gated/private datasets. Omit for public datasets. | | `verifyInterval` | Probe cadence. Defaults to `1h`. | A warehouse is source identity and credential — not a catalog. Which database, schema, table, dataset repo, config, or split to read belongs to the [pipeline source](#pipeline-source); one credential can reach many tables or repos. ### REST / HTTP JSON API A `rest` warehouse declares a JSON HTTP API. The credential is optional — many public APIs need none, or take a key that only raises a rate limit (omit `auth` for the anonymous case, the same `Verified`-with-no-Secret shape as a public Hugging Face dataset). | Field | Purpose | | --- | --- | | `rest.baseUrl` | Required. API origin. A source's `request.path` resolves against it. | | `rest.userAgent` | User-Agent sent by the stock REST source worker. Defaults to `hevlayer/rest-source (+https://hevlayer.com/contact)`; set a project-specific contact URL for public crawls. | | `rest.auth` | Optional. `in` (`query` or `header`), `name` (the param or header carrying the credential), and `secretRef` (Secret holding `token`). Omit for open APIs. | | `rest.rateLimit` | Optional. `requestsPerSecond` is a client-side request cap, to stay under the API's fair-access ceiling. Omit to leave pacing to the source worker's defaults. | | `rest.verify` | Required. The reachability probe: a `path` and optional `query` the operator `GET`s to verify the API — and the credential, when `auth` is set. | `rest` holds access only; which endpoint, query, pagination, and field mapping to read belongs to the [pipeline source](#rest) — one warehouse serves many endpoints on the same API. ## Verification The operator probes the warehouse on apply, whenever the referenced Secret's content changes, and every `verifyInterval`. For `snowflake`, the probe opens a key-pair session, runs `SELECT 1` on the declared compute warehouse, and closes. For `huggingface`, the probe calls the Hub dataset API with the optional token. If pipelines reference the warehouse, the operator resolves each declared `repo@revision` and records the resolved commit SHA in `status.sourceRevisions`. With no consumers yet, it verifies that the Hub dataset API is reachable. For `rest`, the probe issues a single `GET` of `rest.verify.path` (with the optional `query`, and `auth` applied when set). Any `2xx` is `Verified`, confirming both reachability and — when a credential is configured — that it is accepted. | Phase | Meaning | | --- | --- | | `Pending` | Not yet probed. | | `Verified` | Last probe succeeded; `status.verifiedAt` is the probe time. | | `Failed` | Last probe failed; `status.failureReason` says why. | `Failed` is a loud signal, not an outage: in-flight pipeline runs keep their connections, new runs refuse to start, and the condition surfaces in `kubectl get warehouse` and the dashboard. Pipelines start only against a `Verified` warehouse. ## Rotation Swap the referenced Secret's content. The operator re-verifies and `status.verifiedAt` advances; consumers resolve credentials through the warehouse at connection-build time, so new connections pick up the new key with no redeploy. Pointing `keyPairSecretRef` or `tokenSecretRef` at a different Secret name is a spec edit with the same flow. For `huggingface`, swapping the token Secret's content re-verifies the same way. Adding or removing `tokenSecretRef` — moving a warehouse between anonymous and authenticated access — is a spec edit the operator re-verifies. ## Pipeline source A pipeline extracting from a warehouse names it in `spec.sourceRef`. The source block owns the *what* — for Snowflake a database, query, and cursor; for Hugging Face a dataset, split, and field mapping — and the warehouse owns the *where* and *who*. The operator requires `warehouseRef` to name a `Verified` warehouse of the matching kind in the same namespace, and carries the source block verbatim to the worker as `HEVLAYER_SOURCE_REF`, as for [any other source](/docs/kubernetes/pipeline-crd#source). ### Snowflake ```yaml spec: sourceRef: kind: snowflake warehouseRef: prod-snowflake database: ANALYTICS query: >- SELECT ID, TITLE, BODY, REFRESH_ID FROM PUBLIC.NOTES WHERE REFRESH_ID > :cursor cursor: column: REFRESH_ID ``` The operator mounts the warehouse's key-pair Secret into the worker pod at `/var/run/hevlayer/warehouse/` and injects `HEVLAYER_WAREHOUSE` — connection JSON resolved from the warehouse spec (account, user, role, compute warehouse, pool), no credential material. The worker builds its own connection from the two. ### Hugging Face ```yaml spec: target: namespace: squad sourceRef: kind: huggingface warehouseRef: hf-public dataset: rajpurkar/squad # Hub repo id config: plain_text # dataset config; omit for the default split: train # train | validation | test | … revision: ~ # omit → the operator resolves and records the parquet-ref commit mapping: id: id # column → document id text: context # column to index or embed attributes: [title, question] # columns to carry as attributes ``` | `mapping` field | Purpose | | --- | --- | | `text` | Required. The column indexed, and embedded by a following stage. | | `id` | Optional. Column used as the document id. Omitted, the worker synthesizes `{config}/{split}#{offset}` — stable within a revision; name a natural key for anything long-lived. | | `attributes` | Optional. Columns carried as attributes. Omit or `[]` for every remaining scalar column; binary feature columns (image, audio) are skipped. | `spec.worker.image` defaults to the stock Hugging Face source image from the mesh-account ECR registry, so no custom image is needed; set it to override. The operator mounts the warehouse's token Secret (when present) into the worker and injects `HEVLAYER_WAREHOUSE` with the endpoint only, no token. The worker streams rows from the dataset's Parquet conversion at the pinned `revision`; the row offset is the cursor. Omitting `revision` pins to the dataset's current parquet-ref commit, which the operator resolves and records, so a long run never drifts onto a newer version mid-flight and re-enumeration is exact. Reading a newer version is a deliberate `revision` edit. A dataset's `text` column is often a whole document that must be split into chunks before it is embedded. Add a `chunk` block to the source to declare how — it is a pipeline-source feature, not specific to Hugging Face. See [Chunking](/docs/kubernetes/pipeline-crd#chunking) on the Pipeline CRD page. ```yaml spec: sourceRef: kind: huggingface warehouseRef: huggingface-hub repo: McAuley-Lab/Amazon-Reviews-2023 config: raw_meta_Electronics split: train revision: main cursor: field: parent_asin ``` When `sourceRef.kind` is `huggingface`, the operator requires `warehouseRef` to name a `Verified` Hugging Face warehouse in the same namespace and requires `repo` on the source block. It injects `HEVLAYER_WAREHOUSE` with the Hub endpoint and, when `tokenSecretRef` is set, mounts the Secret at `/var/run/hevlayer/warehouse/token` and adds `tokenPath` to the connection JSON. `HEVLAYER_SOURCE_REF` remains the verbatim source block and owns the dataset repo, config, split, revision, and cursor. ### REST ```yaml spec: target: namespace: drug-labels sourceRef: kind: rest warehouseRef: openfda request: path: /drug/label.json query: # static query params search: 'openfda.product_type:"HUMAN PRESCRIPTION DRUG"' pagination: kind: offset # offset only in v1 (searchAfter, link: not yet supported) pageSizeParam: limit # required for offset offsetParam: skip # required for offset pageSize: 1000 # required for offset; must be > 0 response: items: $.results # JSONPath to the record array on each page cursor: field: effective_time # incremental + re-enumeration key (JSONPath per item) mapping: id: openfda.spl_set_id # JSONPath into each item text: ~ # omit when a chunk strategy supplies the section text attributes: [openfda.generic_name, openfda.brand_name, dea_schedule] ``` | Source field | Purpose | | --- | --- | | `request.path` / `request.query` | The endpoint (resolved against `baseUrl`) and any static query params — the *what*. | | `pagination` | How to walk pages. v1 supports `kind: offset` only — `pageSizeParam`, `offsetParam`, and `pageSize` (a positive integer) are all required. `searchAfter` (a cursor token the API echoes) and `link` (a `Link` header) are not yet supported and are rejected on apply. | | `response.items` | JSONPath to the record array on each page. | | `cursor.field` | JSONPath into each item, used as the incremental refresh key and the re-enumeration cursor. | | `mapping` | JSONPath expressions for `id`, `text`, and `attributes` — the same mapping shape as Hugging Face, over JSON instead of Parquet columns. | For one origin with an editorial set of paths, add `request.forEach`. The v1 value source is a ConfigMap in the Pipeline's namespace. `select` runs against the JSON stored at `ref.key`; each selected string is bound to `as`, substituted in `request.path` and `request.query`, and remains available while mapping rows. ```yaml spec: sourceRef: kind: rest warehouseRef: greenhouse request: forEach: values: from: configMap ref: { name: roles-companies, key: companies.json } select: "$[?@.ats == 'greenhouse'].token" as: token path: /v1/boards/{{token}}/jobs query: { content: "true" } pagination: kind: offset pageSizeParam: per_page offsetParam: page pageSize: 500 response: { items: $.jobs } cursor: { field: updated_at } snapshot: mode: full scope: forEachValue absentRow: set: { open: false, closed_at: "{{runStartedAt}}" } mapping: id: "{{token}}:{{id}}" text: "{{title}}\n{{content}}" attributes: title: title company: "{{token}}" ats: "{{warehouseRef}}" department: departments[*].name posted_at: first_published open: true ``` `mapping.attributes` accepts the existing list form or a name-to-expression object. Object values may be JSONPath expressions, template strings, or non-string JSON literals. `{{token}}` is the active `forEach` value; `{{warehouseRef}}` and `{{runStartedAt}}` are worker-provided values. `snapshot.mode: full` means each selected value is a complete diff partition. After a partition has been read through its final page, rows from its prior S3 manifest that are absent now receive `absentRow.set`; the new manifest commits only after every row write succeeds. A transport error, non-2xx response, or an empty first page leaves that partition's manifest and rows intact. This failure rule is deliberate: an unavailable or renamed endpoint is not evidence that all of its rows disappeared. `spec.worker.image` defaults to the stock REST source image from the mesh-account ECR registry, which pages any JSON API by these rules — there is no per-API worker. The operator injects `HEVLAYER_WAREHOUSE` with the `baseUrl` (and mounts the `auth` Secret when set); `HEVLAYER_SOURCE_REF` is the verbatim source block. A `text` field that is a whole document is split by a [`chunk`](/docs/kubernetes/pipeline-crd#chunking) block, as for any source; the `section` strategy with `sectionSource: jsonFields` makes each top-level JSON field its own section. ## Keys An [`ApiKey`](/docs/kubernetes/apikey-crd) binds to a warehouse with a `warehouse.` entitlement carrying a list of opaque claims strings. Layer stores and echoes the strings; the application routes on them. No client route reaches a source system — clients touch indexes, not warehouses — so the entitlement grants nothing in Layer, and it inerts when the warehouse is deleted. ## Deletion Deleting a warehouse fences everything drawing from it. A finalizer blocks deletion while `status.consumers` is non-zero — pipelines extracting from it or keys entitled to it — annotate with `hevlayer.com/force-delete: "true"` to override. ## Status ```yaml status: phase: Verified verifiedAt: "2026-06-10T00:00:00Z" failureReason: null sourceRevisions: McAuley-Lab/Amazon-Reviews-2023@main: 2b6d039ed471f2ba5fd2acb718bf33b0a7e5598e consumers: pipelines: 2 apiKeys: 1 ``` The operator emits Kubernetes Events on phase transitions and counts observed references in `status.consumers`. --- # Index CRD Source: https://hevlayer.com/docs/kubernetes/index-crd An `Index` represents one namespace exposed through the gateway. It declares which upstream namespace to use, snapshot policy, cache posture, and consistency mode. The backend connection itself lives in a [VectorStore](/docs/kubernetes/vectorstore-crd). ```yaml apiVersion: hevlayer.com/v1 kind: Index metadata: name: products namespace: layer spec: backend: storeRef: turbopuffer-default namespace: products distanceMetric: cosine_distance embedding: model: voyage-3-large@v1 outputDim: 1024 normalization: l2 metadata: labels: app: shop tags: - catalog snapshot: interval: 5m retention: never facetFields: - category - brand search: fullText: true scan: threads: 8 cache: ttl: 24h capGiB: 64 mode: standard consistency: strong ``` ## Backend | Field | Purpose | | --- | --- | | `backend.storeRef` | Optional `VectorStore` name in the same namespace. The gateway routes requests for this upstream namespace to that store. Defaults to the namespace's default store. | | `backend.namespace` | Optional upstream namespace override. Defaults to the Index name. | | `backend.distanceMetric` | Vector metric, default `cosine_distance`. | For `kind: search` stores, the operator accepts only the metrics the backend serves directly today: L2-style metrics for single-vector namespaces and `cosine_distance` for multivector namespaces. Unsupported values put the Index in `Ready=False` with reason `MetricMismatch`. ## Embedding `spec.embedding` declares the embedding identity of this namespace's vectors. It is optional for a single-namespace query, where distances are only ever compared within the namespace. It is **required** to include a namespace in a [federated vector query](/docs/api/federated-query#vector-merge-requires-a-matching-embedding-space): the gateway merges those by distance, which is only meaningful when every namespace in the set shares one embedding space. | Field | Purpose | | --- | --- | | `embedding.model` | Model identity and version, e.g. `voyage-3-large@v1`. Treated as an opaque token compared for equality across a namespace set. | | `embedding.outputDim` | Vector dimensionality. Part of the identity because a model truncated to a smaller dimension (Matryoshka) is not comparable to its full-width output. Cross-checked against the namespace schema. | | `embedding.normalization` | Vector normalization, e.g. `l2` or `none`. | Together with `backend.distanceMetric`, these form the embedding profile the gateway compares across a fan-out. Two namespaces are distance-comparable only when all four match; otherwise a fused vector query over them falls back to rank-interleave (or is rejected under `strict`). `spec.embedding` declares an already-computed embedding's identity for comparison purposes; it does not compute anything. To have the gateway compute vectors for you, declare `embed:` on a schema attribute instead — see below. ## Schema-attribute embedding A schema attribute can declare `embed:` so its source value is embedded on write. This is Turbopuffer's native-embeddings wire (`embed` on a schema attribute, private beta at [turbopuffer.com/docs/embedding](https://turbopuffer.com/docs/embedding)). With native serving on a Turbopuffer store, Layer validates and transparently forwards the wire to Turbopuffer's managed embedding service. Autoscaler serving and the hev search fallback use that same service through the gateway, then write only concrete derived vectors to the active store. ```jsonc // simple form — tpuf-compatible "schema": { "text": { "type": "string", "embed": "voyage/voyage-4-lite" } } // extended form "schema": { "text": { "type": "string", "embed": { "model": "Snowflake/snowflake-arctic-embed-m-v1.5", "revision": "refs/pr/5", "dims": 768, "attribute": "text_vector", "instructions": { "document": "Represent this passage for retrieval: ", "query": "Represent this query for retrieving passages: " }, "chunk": { "strategy": "recursive", "unit": "characters", "size": 1200, "overlap": 120 }, "serving": { "prefer": "autoscaler" } } } } ``` | Field | tpuf-compatible | Purpose | | --- | --- | --- | | `embed` (string) | ✅ | Simple form. A provider-namespaced model id such as `voyage/voyage-4-lite`. | | `embed.model` | ✅ | Extended form's provider-namespaced model id. | | `embed.dims` | ✅ | Output dimensionality via Matryoshka truncation. | | `embed.attribute` | ✅ | Override the derived vector attribute name. Omitting it derives `embed_`. | | `embed.serving.prefer` | Layer extension | `native`, `autoscaler`, or `local` (`lattice` is an alias for local Lattice serving). Defaults to `native`. Layer consumes this field before forwarding the native wire. | | `embed.revision` | Layer extension | Pin a Hugging Face model revision. It participates in profile identity and cache keys. | | `embed.instructions` | Layer extension | Optional `document` and `query` prefixes for asymmetric embedding models. | | `embed.modality` | Layer extension | `text` (default) or `image`. Image values are URLs or base64 strings for a CLIP-family checkpoint; query-time `Embed` still uses its text tower. | | `embed.chunk` | Layer extension | Split or fan out the source before embedding. See [Chunking](#chunking). | Layer extensions require `prefer: autoscaler`, with one exception: a CLIP-family `modality: image` profile may use `prefer: local` without the revision, instruction, or chunking extensions (see [Local CLIP](/docs/api/embed#local-clip)). Native and local serving otherwise reject extensions with `422`. A missing production embedding provider returns `503 service_unavailable`. The first row write that establishes an embedded schema must include `distance_metric`. Omitting it returns `422`. The default derived vector column is **`embed_`** — for example, `embed_title` for source attribute `title`. The source attribute is stored alongside the vector. Model ids must be provider-namespaced. Native mode uses Turbopuffer's stock model menu. Autoscaler mode accepts any Hugging Face repo id the configured provider can serve, including finetuned and mixed-case checkpoints. Provider load or model-support failures are returned as upstream embedding errors. ### Chunking For scalar text, `embed.chunk` reuses the Pipeline chunk shape: `strategy` is `none`, `fixed`, `recursive`, `sentence`, or `markdown`; `unit` is `characters` or `tokens`; and `size`, `overlap`, and `tokenizer` control the window. A split document remains as its original row, while each embedded chunk is written as `{id}#{i}` with `_hevlayer_parent_id` and `_hevlayer_chunk_index`. Structured fields use a two-level section composition: ```jsonc "schema": { "text": { "type": "string", "embed": { "model": "BAAI/bge-m3", "chunk": { "strategy": "section", "sectionSource": "jsonFields", "fields": ["boxed_warning", "drug_interactions"], "sectionAttribute": "section", "split": { "strategy": "recursive", "unit": "characters", "size": 1200, "overlap": 120 } }, "serving": { "prefer": "autoscaler" } }} } ``` Each configured non-empty string field on the document row fans out into the embedded attribute first; optional `split` then windows that section. Rows use ids `{id}#{section}#{i}`, retain the document attributes, and add the configured section attribute plus the standard parent/index attributes. Chunked writes require string document ids and `upsert_rows`; columnar writes return `422`. ### Serving mode Per model, `embed.serving.prefer` chooses how vectors are computed. | `prefer` | Behavior | | --- | --- | | `native` (default) | On Turbopuffer, forward the compatible wire to its managed service. On hev search, resolve through the configured provider because the store has no native embedding service. | | `autoscaler` | Resolve through the configured inference provider and send only concrete vectors to the active store. Use this for BYO checkpoints and Layer extensions. | | `local` | Resolve in the gateway process with its configured Lattice or CLIP artifact and send only concrete vectors to the active store. | | `lattice` | Alias for `local` with the `erikkaum/lattice-retrieval` model. | Layer does not switch modes after a provider failure. For Lattice, generate a deployment artifact with the upstream slicer, mount `model.safetensors` beside its `tokenizer.json`, and set `LAYER_LATTICE_MODEL_PATH` to the model file. The requested dimensions must match the artifact. The recommended int4-row, 512-dimensional artifact quantizes model weights; Layer stores its normalized output vectors as f32. For a CLIP-family model, set `LAYER_LOCAL_CLIP_MODEL_PATH` to the checkpoint directory — provisioning and the image-input contract are covered in [Local CLIP](/docs/api/embed#local-clip). Layer durably records gateway-served attribute profiles in S3, so later writes do not need to repeat `schema`. Query vectors use a short in-memory TTL cache. When changing an embedded source attribute, upsert the full row; patching that attribute returns `422` because Layer cannot safely recompute a vector from a partial row. For a hev search deployment, configure at least one credentialed `kind: turbopuffer` VectorStore alongside the search store; Layer uses the first one by name as the managed embedding provider. Without one, gateway-served embedding returns `503 service_unavailable`. ## Snapshot policy | Field | Default | Purpose | | --- | --- | --- | | `snapshot.facetFields` | `[]` | Fields the gateway materializes into durable facet snapshots. Empty disables the automatic writer. | | `snapshot.interval` | `5m` | Minimum spacing between automatic snapshot writes after upstream-stable advances. | | `snapshot.retention` | `never` | `never` keeps all snapshot bodies; a duration such as `30d` prunes older bodies while keeping the latest. | ## Search backend policy `spec.search` applies when the Index targets a `kind: search` VectorStore. The operator uses it to drive the backend's explicit index lifecycle. | Field | Default | Purpose | | --- | --- | --- | | `search.fullText` | `false` | Build the backend's BM25 index for the namespace's `text` column. Enable this for lexical, FTS, or hybrid-text namespaces. | ## Scan policy `scan.threads` sets the per-namespace default for origin scan fan-out: the maximum concurrent upstream requests one scan may issue during scatter/gather. It defaults to `8` and is clamped by the gateway's server cap and the active shard count. Request-level `threads` overrides this default for one scan. ## Cache policy Aerospike remains an ephemeral cache; durable snapshot history stays in S3. Cache warming uses the same scan fan-out policy as other origin scans. ## Status The operator reports observed generation, metadata sync state, and conditions. `status.snapshot.lastRun` and `lastSuccess` are reserved for the gateway history bridge. --- # InfraRules CRD Source: https://hevlayer.com/docs/kubernetes/scaling-crd import Callout from "../../../components/docs/Callout.astro"; import FeatureGate from "../../../components/docs/FeatureGate.astro"; Pipelines and Functions run on Layer's built-in `cpu`, `cpu-large`, and `gpu` pools when no `InfraRules/default` object exists. The built-in pools include the stock resource envelopes, placement rules, and per-workload replica ceilings shown below, so a fresh install needs no infrastructure policy object. `InfraRules` is the cluster-scoped policy object for Layer-managed runtime infrastructure. Apply the singleton `InfraRules/default` when you need to replace the built-in pools or declare document-cache policy. Pipelines and Functions do not reference a separate autoscaling resource. They set `spec.scaling` inline and choose a pool from the built-in pool set or `InfraRules/default.spec.computePools` when the custom object exists. ## InfraRules ```yaml apiVersion: hevlayer.com/v1alpha1 kind: InfraRules metadata: name: default spec: computePools: - name: cpu kind: cpu nodeSelector: layer.hev.dev/node-role: worker-cpu layer.hev.dev/compute: cpu tolerations: - key: layer.hev.dev/node-role operator: Equal value: worker-cpu effect: NoSchedule resources: requests: cpu: "1" memory: 2Gi limits: cpu: "2" memory: 4Gi maxReplicasPerWorkload: 32 - name: cpu-large kind: cpu nodeSelector: layer.hev.dev/node-role: worker-cpu layer.hev.dev/compute: cpu tolerations: - key: layer.hev.dev/node-role operator: Equal value: worker-cpu effect: NoSchedule resources: requests: cpu: "1" memory: 2Gi ephemeral-storage: 35Gi limits: cpu: "4" memory: 4Gi ephemeral-storage: 40Gi maxReplicasPerWorkload: 8 - name: gpu kind: gpu nodeSelector: layer.hev.dev/node-role: worker-gpu layer.hev.dev/compute: gpu tolerations: - key: layer.hev.dev/node-role operator: Equal value: worker-gpu effect: NoSchedule - key: nvidia.com/gpu operator: Exists effect: NoSchedule resources: requests: cpu: 250m memory: 4Gi nvidia.com/gpu: "1" limits: cpu: "2" memory: 10Gi nvidia.com/gpu: "1" maxReplicasPerWorkload: 4 documentCache: capGiB: 256 replicationFactor: 1 scaling: mode: autoscale nodes: min: 0 max: 1 ``` The operator validates that the object is named `default`. ## Compute pools Layer defines three built-in pools: | Pool | Use | | --- | --- | | `cpu` | General CPU workers. | | `cpu-large` | CPU workers that need local ephemeral-storage headroom. | | `gpu` | One-NVIDIA-GPU workers for embedding and inference. | The default pools select the Karpenter-backed worker nodes with `layer.hev.dev/node-role=worker-cpu` or `worker-gpu`. The default `gpu` pool also requests `nvidia.com/gpu: "1"` and includes the standard NVIDIA toleration. Apply `InfraRules/default` with different `nodeSelector`, `gpuType`, or resource envelopes when your cluster uses different worker pool names or specific SKUs. | Field | Purpose | | --- | --- | | `name` | Referenced by `spec.scaling.pool` on Pipeline and Function resources. | | `kind` | Pool class label such as `cpu` or `gpu`. | | `gpuType` | Optional descriptive GPU type for GPU pools. | | `nodeSelector` | Applied to worker pods that choose the pool. | | `tolerations` | Applied to worker pods that choose the pool. | | `resources` | Container resources applied to worker pods. | | `maxReplicasPerWorkload` | Hard ceiling for one Pipeline or Function. | If a workload names an unknown pool or asks for more replicas than the pool ceiling, the operator leaves the workload unready and records a condition on its status. ## Workload scaling ```yaml scaling: pool: gpu mode: autoscale warmWindowSeconds: 300 replicas: min: 0 max: 4 ``` | Mode | Behavior | | --- | --- | | `autoscale` | Emit a KEDA `ScaledObject` and let queue depth scale the Deployment between `min` and `max`. | | `fixed` | Set Deployment replicas to `replicas.min`; no KEDA object is emitted. | | `disabled` | Scale the Deployment to 0; no KEDA object is emitted. | | Field | Purpose | | --- | --- | | `pool` | Names a built-in pool, or a pool in `InfraRules/default.spec.computePools` when custom rules exist. When omitted, the operator maps `worker.computeClass` to the stock `cpu` or `gpu` pool. | | `mode` | `autoscale`, `fixed`, or `disabled` (see the table above). | | `replicas` | `min`/`max` bounds for the Deployment. `max` may not exceed the pool's `maxReplicasPerWorkload`. | | `warmWindowSeconds` | Cooldown that holds a workload warm after its last scaling trigger drains, before `autoscale` returns it to `replicas.min`. See below. | Paused workloads also scale to 0. To keep a cold-start-heavy worker warm, set `mode: autoscale` and `replicas.min: 1`. ### Warm window `warmWindowSeconds` maps to the KEDA `ScaledObject` `cooldownPeriod`: the operator waits this long after the last trigger fires before scaling the Deployment back to `replicas.min`. It defaults to `60` when unset. A non-zero value also annotates the worker pods with `karpenter.sh/do-not-disrupt`, so Karpenter retains the *node* — not just the replica — for the window rather than consolidating it away. This is aimed at scale-to-zero GPU pools, where each wake otherwise pays a full cold start (fresh nodeclaim, multi-GB image pull, model load). A warm window lets adjacent batches reuse one warm node, then lets the pool return to genuine scale-to-zero once the window elapses. It must be `>= 0` and requires `mode: autoscale`; the operator leaves the workload unready and records a condition otherwise. ## Document cache rules `documentCache` captures the operator-owned document cache settings: capacity, replication factor, and node count. Helm still renders the document-cache KEDA object directly; `InfraRules` is the declared policy shape the operator reports and validates against. --- # Pipeline CRD Source: https://hevlayer.com/docs/kubernetes/pipeline-crd The `Pipeline` CRD declares the scaling characteristics you want for ingesting data. Ingestion typically runs in stages: a CPU stage for chunking and extraction, followed by a GPU stage for embedding. You can declare the spec in YAML, from code through the [pipeline API](/docs/api/pipelines), or a combination of both — it is recommended you declare your pipeline scaling characteristics in YAML while setting your namespace via the client. `spec.sourceRef` lets you declare your pipeline's upstream details as well — the operator hands it to the worker as an environment variable, so the worker reads its source from config instead of hardcoding it. ```yaml apiVersion: hevlayer.com/v1alpha1 kind: Pipeline metadata: name: product-images namespace: layer spec: target: namespace: products sourceRef: kind: sqs queueUrl: https://sqs.us-east-1.amazonaws.com/123456789/product-images schedule: cron: "0 2 * * *" leaseSeconds: 600 worker: image: .dkr.ecr.us-east-1.amazonaws.com/hev-product-image-worker:latest computeClass: cpu batchSize: 64 timeoutSeconds: 60 scaling: pool: cpu mode: autoscale replicas: min: 0 max: 8 ``` ## Target `spec.target.namespace` is the turbopuffer namespace the pipeline writes. The gateway pipeline API owns document state, chunks, and vector writes for that target namespace. ## Pipeline id `spec.pipelineId` names the gateway pipeline (the queue) the worker stages into and scales on. It defaults to the resource name. Set it when multiple worker resources share one queue: the extract and embed stages of a [two-stage pipeline](/docs/api/pipelines) both set `pipelineId: products`. ## Source `spec.sourceRef` declares the external source that feeds the worker. Its `kind` selects how the operator treats it. For **open kinds** — SQS, Kafka, S3 events, a partner API, a one-off migration — `sourceRef` is arbitrary JSON injected into the worker pod verbatim as `HEVLAYER_SOURCE_REF`; the worker image owns source-specific behavior. See [Extract and chunk](/docs/api/pipelines#extract-and-chunk) for a worker reading it. ### Typed sources For **warehouse-backed kinds** — `snowflake`, `huggingface`, and `rest` — `kind` selects a typed shape the operator validates. The source names a [`Warehouse`](/docs/kubernetes/warehouse-crd) with `warehouseRef`; the operator resolves it (it must be `Verified`), mounts its credential Secret, and injects connection details as `HEVLAYER_WAREHOUSE` with no credential material. `spec.worker.image` is then optional: omit it and the operator defaults to the stock worker for that kind from the mesh-account ECR registry (for example, `.dkr.ecr.us-east-1.amazonaws.com/hev-huggingface-source` or `.dkr.ecr.us-east-1.amazonaws.com/hev-rest-source`), so a typed source needs no custom image. Set `worker.image` to override with your own. The per-kind source fields are on the [Warehouse CRD](/docs/kubernetes/warehouse-crd#pipeline-source) page. ### Chunking A source's text column is often a whole document that must be split before it is embedded. An optional `chunk` block declares how, with no code for the common strategies. It applies to any source whose worker honors it — the stock workers do. ```yaml spec: sourceRef: kind: huggingface warehouseRef: hf-public dataset: wikimedia/wikipedia config: 20231101.en split: train mapping: text: text attributes: [title, url] chunk: strategy: recursive # none | fixed | recursive | sentence | markdown unit: tokens # tokens | characters size: 512 overlap: 64 tokenizer: cl100k_base # when unit: tokens ``` | `chunk` field | Purpose | | --- | --- | | `strategy` | `none` (default — one document per row), `fixed`, `recursive` (a paragraph→line→sentence→word ladder kept under `size`), `sentence`, or `markdown` (split on headings). | | `unit` | `tokens` or `characters` — what `size` and `overlap` count in. | | `size` | Target maximum chunk length. | | `overlap` | Units repeated between adjacent chunks for context. | | `tokenizer` | Token model when `unit: tokens`. Pinned so chunk boundaries stay reproducible. | Each row maps to one document; `text` splits into chunks. The chunk is the unit indexed and embedded — a row with id `{documentId}#{i}` carrying the document's attributes plus reserved `_hevlayer_parent_id` and `_hevlayer_chunk_index`. For splits the stock strategies can't express, set `spec.worker.image` to your own chunker. ## Schedule `spec.schedule` is optional. When it is set, the operator wakes the Pipeline worker on a KEDA cron window instead of scaling it on pending pipeline queue depth: ```yaml schedule: cron: "0 2 * * *" # 5-field UTC cron; minute must be a single integer leaseSeconds: 600 # sizes the cron window ``` The worker still owns source semantics: what to pull on wake, how to advance cursors, and how to stage rows. The schedule only controls when the worker runs. Scheduled Pipelines must use `scaling.replicas.min: 0`; the cron window is the wake trigger. ## Worker | Field | Purpose | | --- | --- | | `image` | Worker image. Optional for [typed sources](#typed-sources), where it defaults to the stock worker for the source kind; required otherwise. | | `computeClass` | `cpu` or `gpu`. Defaults to `cpu`; when `scaling.pool` is omitted, the operator maps this to the stock `cpu` or `gpu` pool. | | `batchSize` | Work items per batch. | | `timeoutSeconds` | Worker call timeout. | | `podSpec` | Optional pod-level merge patch. | The operator creates one Deployment per Pipeline and injects: | Variable | Value | | --- | --- | | `HEVLAYER_PIPELINE_ID` | `spec.pipelineId`, defaulting to the resource name. | | `HEVLAYER_TARGET_NAMESPACE` | `spec.target.namespace`. | | `HEVLAYER_BASE_URL` | The gateway base URL. | | `HEVLAYER_SOURCE_REF` | `spec.sourceRef` as JSON, when set. | | `HEVLAYER_FOR_EACH_VALUES_PATH` | Read-only file projected from `request.forEach.values.ref` when a typed REST source uses `from: configMap`. | | `HEVLAYER_SNAPSHOT_BUCKET` | The chart's S3 bucket, injected for REST full-snapshot manifests. Region and optional S3-compatible endpoint are provided as `HEVLAYER_SNAPSHOT_REGION` and `HEVLAYER_SNAPSHOT_ENDPOINT`. | | `HEVLAYER_PIPELINE_SCHEDULE` | `1` when `spec.schedule` is set. | | `HEVLAYER_WAREHOUSE` | Resolved `Warehouse` connection JSON (no credential material), for [typed sources](#typed-sources). The credential Secret is mounted separately. | | `LAYER_GATEWAY_API_KEY` | Gateway bearer token. In `deriveFromStore` mode this is the default `VectorStore` credential; in `keys` mode it is the configured inbound worker key. | ## Scaling ```yaml scaling: pool: cpu mode: autoscale replicas: min: 0 max: 8 ``` `spec.scaling.pool`, when set, must name a pool in the [built-in set or `InfraRules/default`](/docs/kubernetes/scaling-crd). When omitted, the operator uses `worker.computeClass` to choose the stock `cpu` or `gpu` pool. Layer provides the well-known `cpu`, `cpu-large`, and `gpu` pools without requiring an `InfraRules` object. `mode: autoscale` creates a KEDA `ScaledObject` backed by pipeline queue depth, or by the cron window when `spec.schedule` is set. `mode: fixed` pins the Deployment to `replicas.min`; `mode: disabled` scales it to zero. `spec.scaling.warmWindowSeconds` sets a cooldown (and node retention) that holds the worker warm after its queue drains — see [Workload scaling](/docs/kubernetes/scaling-crd#warm-window). `spec.paused: true` also scales the worker to zero. ## Status Use the [pipeline status API](/docs/api/pipelines#wait-for-completion) for status: queue counts, stage progress, and worker state. The resource itself reports only managed object references and readiness conditions. --- # Function CRD Source: https://hevlayer.com/docs/kubernetes/function-crd import CodeTabs from "../../../components/docs/CodeTabs.astro"; The `Function` CRD is a User Defined Function (UDF) that runs over rows that already exist in an [Index](/docs/kubernetes/index-crd). It is the right shape for classifiers, enrichment, backfills, fan-out from an existing row, and deterministic re-upserts. UDFs are best defined in YAML and invoked by the [layer CLI](/docs/cli#run-a-function). The operator creates worker resources; the gateway owns discovery, queueing, retries, leases, and completion markers. Workers own their data writes. Use a [Pipeline](/docs/kubernetes/pipeline-crd) when external data becomes rows in Layer. Use a Function when compute starts from rows that are already in Layer. ```yaml apiVersion: hevlayer.com/v1alpha1 kind: Function metadata: name: tag-products namespace: layer spec: targetNamespaces: - products inputs: - id - title version: v1 filter: - category - Eq - outdoor worker: image: .dkr.ecr.us-east-1.amazonaws.com/hev-tag-products:latest dispatch: pull computeClass: cpu batchSize: 32 timeoutSeconds: 30 schedule: discoveryIntervalSeconds: 300 leaseSeconds: 120 maxInFlightBatches: 8 maxConcurrentScans: 1 retry: maxAttempts: 8 initialBackoffSeconds: 5 maxBackoffSeconds: 300 triggers: - discovery scaling: pool: cpu mode: autoscale replicas: min: 0 max: 6 ``` ## Selection Use `targetNamespaces` for explicit namespaces. Use `indexSelector` when labels on `Index` resources should choose the namespaces. `filter` preserves arbitrary JSON, including array-form turbopuffer filters. The operator stores the shape as-is; the gateway evaluates it during discovery after AND-ing it with the generated completion-marker predicate. Do not include a version-marker predicate in `filter`; the gateway creates that from `spec.version`. ## Worker | Field | Purpose | | --- | --- | | `image` | Worker image. | | `dispatch` | `pull` for SDK claim/poll workers, `push` for HTTP `/run` workers. | | `computeClass` | `cpu` or `gpu`. Defaults to `cpu`; when `scaling.pool` is omitted, the operator maps this to the stock `cpu` or `gpu` pool. | | `port` | Push-dispatch service port. | | `batchSize` | Rows per batch. | | `timeoutSeconds` | Worker call timeout. | | `podSpec` | Optional pod-level merge patch. | To apply the CR, register the gateway UDF, trigger discovery, and watch the queue with one command, use [`layer run -f`](/docs/cli#run-a-function). The worker pod receives `HEVLAYER_UDF_ID`, `HEVLAYER_BASE_URL`, `HEVLAYER_UDF_BATCH_SIZE`, `HEVLAYER_UDF_TIMEOUT_SECONDS`, `HEVLAYER_UDF_LEASE_SECONDS`, and `LAYER_GATEWAY_API_KEY`. The gateway bearer is sourced from the default `VectorStore` credential in `deriveFromStore` mode, or from the configured inbound worker key in `keys` mode. ## Simple classifier The Python client turns a normal function into the claim/process/complete loop. `output="tags"` is client-side metadata: the CRD does not declare an output attribute. `run_udf_worker` sends the returned value as a completion `attributes.tags` patch, and the gateway stamps the reserved completion marker in the same patch. The Go client drives the same worker protocol directly, as does the TypeScript client — claim a batch, process rows, report completions and failures. ```python import asyncio from hevlayer.udf import PermanentError, TransientError, run_udf_worker, udf @udf(inputs=["id", "title", "description"], output="tags", kind="tags") def tag_product(*, id: str, title: str | None, description: str | None) -> list[str]: if not title: raise PermanentError(f"{id}: missing title") try: text = f"{title} {description or ''}".lower() except TypeError as exc: raise TransientError(str(exc)) from exc tags: list[str] = [] if "wireless" in text: tags.append("wireless") if "waterproof" in text: tags.append("waterproof") return tags or ["uncategorized"] if __name__ == "__main__": asyncio.run(run_udf_worker(tag_product, udf_id="product-tags")) ``` ```go package main import ( "context" "os" "strings" hevlayer "github.com/hev/layer-go" ) func tags(title, description string) []string { text := strings.ToLower(title + " " + description) var out []string if strings.Contains(text, "wireless") { out = append(out, "wireless") } if strings.Contains(text, "waterproof") { out = append(out, "waterproof") } if len(out) == 0 { out = []string{"uncategorized"} } return out } func main() { ctx := context.Background() udfID := os.Getenv("HEVLAYER_UDF_ID") layer := hevlayer.NewClient( hevlayer.WithBaseURL(os.Getenv("HEVLAYER_BASE_URL")), hevlayer.WithAPIKey(os.Getenv("LAYER_GATEWAY_API_KEY")), ) for { claimed, err := layer.ClaimUdfItems(ctx, udfID, &hevlayer.UdfClaimRequest{ WorkerID: "tag-products-0", Limit: 32, }) if err != nil { continue } var done []hevlayer.UdfCompleteItem var failed []hevlayer.UdfFailItem for _, item := range claimed.Items { title, _ := item.Input["title"].(string) description, _ := item.Input["description"].(string) if title == "" { failed = append(failed, hevlayer.UdfFailItem{ Namespace: item.Namespace, ID: item.ID, Kind: "permanent", Message: "missing title", }) continue } done = append(done, hevlayer.UdfCompleteItem{ Namespace: item.Namespace, ID: item.ID, Attributes: map[string]interface{}{"tags": tags(title, description)}, }) } if len(done) > 0 { layer.CompleteUdfItems(ctx, udfID, &hevlayer.UdfCompleteRequest{ WorkerID: "tag-products-0", Items: done, }) } if len(failed) > 0 { layer.FailUdfItems(ctx, udfID, &hevlayer.UdfFailRequest{ WorkerID: "tag-products-0", Items: failed, }) } } } ``` ```typescript import { Hevlayer } from "hevlayer"; function tags(title: string, description: string): string[] { const text = `${title} ${description}`.toLowerCase(); const out: string[] = []; if (text.includes("wireless")) out.push("wireless"); if (text.includes("waterproof")) out.push("waterproof"); return out.length ? out : ["uncategorized"]; } const udfId = process.env.HEVLAYER_UDF_ID!; const layer = new Hevlayer({ baseUrl: process.env.HEVLAYER_BASE_URL, apiKey: process.env.LAYER_GATEWAY_API_KEY, }); while (true) { const claimed = await layer.claimUdfItems(udfId, { worker_id: "tag-products-0", limit: 32, }); const done = []; const failed = []; for (const item of claimed.items) { const title = typeof item.input.title === "string" ? item.input.title : ""; const description = typeof item.input.description === "string" ? item.input.description : ""; if (!title) { failed.push({ namespace: item.namespace, id: item.id, kind: "permanent", message: "missing title", }); continue; } done.push({ namespace: item.namespace, id: item.id, attributes: { tags: tags(title, description) }, }); } if (done.length > 0) { await layer.completeUdfItems(udfId, { worker_id: "tag-products-0", items: done }); } if (failed.length > 0) { await layer.failUdfItems(udfId, { worker_id: "tag-products-0", items: failed }); } } ``` In Python, function parameters are keyword-only and named to match `inputs`; raise `TransientError` for retryable work and `PermanentError` for unrecoverable input. In Go and TypeScript, report the same split through `FailUdfItems` / `failUdfItems` with `kind: "transient"` or `kind: "permanent"`. ## GPU classifier More complicated classifiers (e.g. a vision-language classifier) may require a model to run on a GPU. ```yaml apiVersion: hevlayer.com/v1alpha1 kind: Function metadata: name: product-color namespace: layer spec: targetNamespaces: - amazon-products inputs: - id - image_url version: v1 worker: image: .dkr.ecr.us-east-1.amazonaws.com/hev-shop-udf-product-color:latest dispatch: pull computeClass: gpu batchSize: 8 timeoutSeconds: 120 schedule: leaseSeconds: 300 maxInFlightBatches: 2 triggers: - discovery scaling: pool: gpu mode: autoscale replicas: min: 0 max: 2 ``` `worker.computeClass: gpu` defaults omitted `scaling.pool` to the [built-in `gpu` pool](/docs/kubernetes/scaling-crd). The stock pool selects `layer.hev.dev/node-role=worker-gpu`, requests one NVIDIA GPU, and carries the worker and NVIDIA tolerations: ```yaml computePools: - name: gpu kind: gpu maxReplicasPerWorkload: 4 nodeSelector: layer.hev.dev/node-role: worker-gpu layer.hev.dev/compute: gpu tolerations: - key: layer.hev.dev/node-role operator: Equal value: worker-gpu effect: NoSchedule - key: nvidia.com/gpu operator: Exists effect: NoSchedule resources: requests: { memory: 4Gi, nvidia.com/gpu: "1" } limits: { memory: 10Gi, nvidia.com/gpu: "1" } ``` The worker loads the model once at startup and classifies per row. CLIP zero-shot classification labels each product image with its dominant color: ```python import asyncio import io import httpx import torch from PIL import Image from transformers import pipeline from hevlayer.udf import PermanentError, TransientError, run_udf_worker, udf COLORS = ["black", "white", "gray", "red", "blue", "green", "brown", "multicolor"] classifier = pipeline( "zero-shot-image-classification", model="openai/clip-vit-large-patch14", device="cuda" if torch.cuda.is_available() else "cpu", ) @udf(inputs=["id", "image_url"], output="color", kind="classification") def classify_color(*, id: str, image_url: str | None) -> str: if not image_url: raise PermanentError(f"{id}: missing image_url") try: resp = httpx.get(image_url, timeout=10.0, follow_redirects=True) resp.raise_for_status() image = Image.open(io.BytesIO(resp.content)).convert("RGB") except httpx.HTTPError as exc: raise TransientError(f"{id}: image fetch failed: {exc}") from exc except OSError as exc: raise PermanentError(f"{id}: undecodable image: {exc}") from exc scores = classifier(image, candidate_labels=COLORS) return scores[0]["label"] if __name__ == "__main__": asyncio.run(run_udf_worker(classify_color, udf_id="product-color")) ``` The worker image needs `torch`, `transformers`, `pillow`, and `httpx` alongside the `hevlayer` Python client. Bake the model weights into the image so autoscaled pods do not re-download them on every cold start. Sizing for inference: keep `worker.batchSize` low and `worker.timeoutSeconds` high enough for one batch of forward passes, and make `schedule.leaseSeconds` outlast a full batch so claims do not reissue mid-inference. `replicas.min: 1` keeps a warm worker when model cold-start dominates; `min: 0` scales to zero between sweeps. ## Scaling `spec.scaling` is the same scaling config [Pipelines use](/docs/kubernetes/pipeline-crd#scaling): a pool from the built-in set or `InfraRules/default`, a mode, and replica bounds. For Functions, `mode: autoscale` emits a KEDA `ScaledObject` triggered by `layer_udf_queue_depth`. Replica maxima above the pool's `maxReplicasPerWorkload` are rejected in status. For GPU Functions on a scale-to-zero pool, set `spec.scaling.warmWindowSeconds` to hold the worker — and its node — warm for a cooldown after the queue drains, so adjacent batches skip the cold start (fresh node, image pull, model load) before the pool returns to zero. See [Workload scaling](/docs/kubernetes/scaling-crd#warm-window). ## Writeback Workers own data writes. The common single-attribute case uses the Python client's sugar: `@udf(output="tags")` makes `run_udf_worker` send returned values as `attributes.tags` in the completion call — in Go (or over REST) the same thing is `attributes` on each completion item. The gateway applies those attributes and the reserved completion marker in one `patch_columns` write. Completion attributes must not use the reserved `_hevlayer_*` prefix. Embedding Functions can include `vector` on each completion item; hev search multivector Functions can include `vectors` with a vector bag. The Python helper emits `vector` for `@udf(kind="embedding")` return values. The gateway fetches the existing row, merges returned attributes and reserved markers, then re-upserts the full row with the replacement vector or multivector bag. This is the writeback path for search-backed stores, which can replace vectors directly without a column-patch primitive. Python workers that need more control can declare the `tpuf` parameter, write through the client, and return `None`; completion then stamps only the marker. Use deterministic IDs when a Function creates rows so at-least-once retries remain idempotent. Deleting a Function garbage-collects operator-managed Kubernetes resources. It does not delete already-written attributes. ## Lifecycle ```sh kubectl get function product-tags kubectl describe function product-tags layer udf get product-tags kubectl patch function product-tags --type=merge -p '{"spec":{"paused":true}}' kubectl patch function product-tags --type=merge -p '{"spec":{"paused":false}}' curl -X POST -H "authorization: Bearer $LAYER_GATEWAY_API_KEY" \ $LAYER_GATEWAY_URL/v2/udfs/product-tags/reset-failed kubectl delete function product-tags ``` Registration in the gateway's UDF registry happens at reconciliation, not at first discovery run — a Function created with `spec.paused: true` (or paused later) is registered immediately with `paused: true`, so it is observable from creation onward: When the Function spec changes, reconciliation upserts the registered UDF definition in place. Pending, processing, failed, and indexed queue state remain attached to the same UDF id. ``` GET /v2/udfs → 200 {"udfs": [{"id": "product-tags", "paused": true, ...}]} GET /v2/udfs/product-tags/status → 200 {"udf_id": "product-tags", "paused": true, ...} ``` A `404` from `/v2/udfs/{id}/status` means the Function was never registered — a real failure, not an intentional pause. `paused` on the `Udf` and `UdfStatus` resources is the single source of truth for "is this installed and paused" versus "does this exist at all." ## Version markers `spec.version` is the re-run safety rail and defaults to `v1`. On completion, the gateway stamps `_hevlayer_udf__v` with that version, normalizing hyphens in the Function name to underscores. For `metadata.name: product-color`, the marker is `_hevlayer_udf_product_color_v`. Discovery automatically looks for rows whose marker is missing, differs from `spec.version`, or has an expired `_hevlayer_udf__stale_after` marker. Bump `spec.version` when a model, taxonomy, or prompt changes. ## Tuning knobs | Knob | What it bounds | | --- | --- | | `worker.batchSize` | Rows per worker batch. | | `worker.timeoutSeconds` | Worker call timeout. | | `schedule.leaseSeconds` | How long a claim is held before reissue. | | `schedule.discoveryIntervalSeconds` | Time between discovery scan jobs. | | `schedule.maxInFlightBatches` | Concurrent worker batches per UDF. | | `schedule.maxConcurrentScans` | Concurrent namespace discovery jobs. | | `retry.maxAttempts` | Tries before a row lands in `failed`. | --- # Agent CRD Source: https://hevlayer.com/docs/kubernetes/agent-crd import FeatureGate from "../../../components/docs/FeatureGate.astro"; import Callout from "../../../components/docs/Callout.astro"; An `Agent` is a saved agentic-search configuration as a resource. It binds an inference model, a turn budget, and a set of [indices](/docs/kubernetes/index-crd) to a name, so a caller searches with [`POST /v2/agents/{name}/query`](/docs/api/agents) and sends only a query string. Everything that decides what a call costs and what it can read lives on the resource, not in the request. An agent adds no retrieval primitive. Its only tool is the [federated query](/docs/api/federated-query); it sits one level above the [`Auto`](/docs/api/query#query-routing) router, using a model to reformulate the query, fan out for recall, and score the candidates for relevance. What it returns is the same row shape as every other search endpoint — a better-ranked result set, not a generated answer. Like the other CRDs here, an Agent has two authoring surfaces that round-trip through one schema: `kubectl get agent -o yaml` and `client.agent("support-search").apply()` are two spellings of the same object. ```yaml apiVersion: hevlayer.com/v1alpha1 kind: Agent metadata: name: support-search namespace: layer spec: model: provider: openrouter name: anthropic/claude-haiku-4-5 fallback: anthropic/claude-sonnet-4-6 # optional; used on primary timeout/error apiKeySecretRef: name: openrouter key: credential budget: deadlineMs: 60000 onDeadline: bestEffort indices: [docs, tickets] retrieval: fanout: 8 recallDepth: 50 rankBy: auto relevanceWeight: 0.6 output: provenance: false trace: false status: phase: Ready conditions: - type: SecretResolved status: "True" - type: ModelReachable status: "True" - type: IndicesResolved status: "True" ``` ## Model | Field | Purpose | | --- | --- | | `provider` | Inference backend. `openrouter`. | | `name` | Model id passed to the provider, e.g. `anthropic/claude-haiku-4-5`. | | `fallback` | Optional model used when the primary times out or errors. | | `apiKeySecretRef` | Secret holding the provider credential (`name`, `key`). The token is never inline on the resource — the same rule the [ApiKey](/docs/kubernetes/apikey-crd) and [VectorStore](/docs/kubernetes/vectorstore-crd) CRDs follow. | The gateway's in-memory agent spec also accepts a raw inline `model.apiKey` token. This exists only as a local dev-seed escape hatch for running an agent without provisioning a Secret. It is deliberately absent from the `Agent` CRD — the operator rejects anything but `apiKeySecretRef`, and RFC 0074 bans a raw token on the resource. Do not use it in any deployed configuration. ## Budget | Field | Purpose | | --- | --- | | `deadlineMs` | Wall-clock deadline for the whole request. | | `onDeadline` | `bestEffort` (default) returns the best ranking the agent has when the deadline hits; `error` fails the request instead. | ## Indices `indices` is the set of namespaces the agent searches, passed to the [federated query](/docs/api/federated-query) as its `namespaces`. The operator checks that the list is present and every entry is a non-empty string, surfaced as the `IndicesResolved` condition; it does not currently confirm each entry names a namespace that exists. The agent reads each listed namespace under the caller's credential, so a caller only reaches the namespaces its own key grants. ## Retrieval All retrieval behavior is expressed against the [federated query](/docs/api/federated-query) — nothing new reaches the upstream. | Field | Default | Purpose | | --- | --- | --- | | `fanout` | `8` | Query reformulations the agent issues, run in parallel via layer's [scatter/gather](/docs/concepts#scattergather) and merged for recall. Higher fan-out trades latency for recall. | | `recallDepth` | `50` | Candidates gathered for ranking before `top_k`, at least `top_k`. Bounds how much the model reads. | | `rankBy` | `auto` | Default route per leg: `auto`, `hybridText`, or `semantic`. A `semantic` leg needs a query vector, which the caller supplies on the request (`vector`) — layer never embeds query text; see [Agentic search](/docs/api/agents#bring-your-own-embedding). Without a supplied vector, semantic legs fall back to the lexical route. | | `relevanceWeight` | `0.6` | Weight of the relevance score against the recall score in the final ranking. | ## Output | Field | Purpose | | --- | --- | | `provenance` | When true, each row carries a `$agent` field with its `retrievalScore` and `relevanceScore`, and the response gains a top-level `agent` echo. | | `trace` | When true, the `agent` echo also carries the full reasoning trace. | Default off, the response is byte-for-byte the [federated query](/docs/api/federated-query#response) shape: a client cannot tell a reasoning loop produced it. See [Agentic search](/docs/api/agents) for the request and response contract. ## Auth Auth follows the same model as the other API endpoints; multi-namespace queries follow [federated query](/docs/api/federated-query#entitlements) auth behavior. Who may invoke an agent is an `agent.` entitlement on the [ApiKey](/docs/kubernetes/apikey-crd#entitlements). ## Status `status` carries health and validation only; latency, turn counts, and spend go to metrics and history, never into etcd. | Condition | Meaning | | --- | --- | | `SecretResolved` | `model.apiKeySecretRef` exists and is readable. | | `ModelReachable` | the provider answered with the bound credential. | | `IndicesResolved` | `spec.indices` is non-empty and every entry is a non-empty string. (It does not yet verify each entry is a known namespace.) | | Phase | Meaning | | --- | --- | | `Ready` | Resolvable and callable. | | `Degraded` | Reachable but a condition is failing — calls may fall back or error. | | `Invalid` | A required field or reference does not resolve; calls are refused. | `kubectl get agent` print columns: `MODEL`, `INDICES`, `PHASE`. Edits are picked up shortly after they apply: the gateway resolves agents — spec, provider credential, and per-index schema — into memory and refreshes on a periodic tick, so a `kubectl apply` lands within the refresh interval rather than instantly. ## Naming Agent names are cluster-unique at the gateway because callers address agents by name only. If two namespaces define the same Agent name, the gateway sorts by agent name and namespace, keeps the lexicographically later namespace, and logs a warning naming both namespaces. Keep Agent names unique across the cluster. ## Observability Deadline-hit rate, provider latency, and token usage per agent export as `hevlayer_*` metrics. Token counts come back on the inference response, so they cost no extra call; dollar cost is derived from them downstream rather than fetched in the request path. The reasoning trace is written to the [search-history](/docs/api/search-history) record alongside the query it belongs to, so agentic and plain searches share one history surface for evaluation. --- # Failure Modes Source: https://hevlayer.com/docs/failure-modes import Callout from "../../components/docs/Callout.astro"; Layer strives to degrade gracefully: queries and document fetch served from turbopuffer keep functioning when components around them fail. This page details the scenarios where that does not apply. ## Read Reads route through the gateway, but a gateway outage does not take your queries dark. The Python and Go SDKs fall through to turbopuffer direct when the gateway is unreachable, so turbopuffer-compatible queries keep serving rather than failing, minus the document cache, search history, and Layer's query enhancements (see [Client fall-through](#client-fall-through) below). Layer-only read paths (document fetch, warm jobs, pipeline and UDF status, snapshots, and search history) fail fast, because they depend on gateway-owned cache, queue, history, and consistency state. The document cache is stateless and can scale to zero with no disruption: document fetches fall through to origin (turbopuffer, or S3 for snapshots) on a miss or cache outage, so a cache failure degrades latency, not availability. ## Write Writes also fall through to turbopuffer direct when the gateway is unreachable (again, see [Client fall-through](#client-fall-through)); the durable upstream still accepts the row, but the write skips document-cache warming and pipeline staging until the gateway returns. ### Pipeline stop-writes The primary failure mode for writes through a healthy gateway is Aerospike stop-writes during a multi-stage pipeline job: staged documents stay warm in the cache but carry no vector data yet, and once that data exceeds the Aerospike drive allocation the cache rejects further writes. The pipeline does not stall. Each stage persists its chunk bodies to S3 before it touches the cache, and pipeline state lives in PostgreSQL, so the Aerospike write is best-effort: on stop-writes the gateway logs the skipped write and the stage still completes. Downstream chunk reads degrade to the S3 backing for as long as the cache is rejecting writes. Recovery is automatic. The Helm document cache restarts on stop-writes by default (`documentCache.autoRestartOnStopWrites: true`) and clears its Aerospike backing file on pod start (`documentCache.storage.resetOnStart: true`); the gateway reconnects in the background and refills the cache from S3 on demand. No pipeline work is lost — S3 and PostgreSQL are the durable recovery boundary and must stay healthy. Operator signals: - `layer_aerospike_op_duration_seconds{status="aerospike_stop_writes"}` — the stop-writes condition itself, the same series the [dashboard](/docs/dashboard) charts. - `hevlayer_cache_cold_responses_total` — reads being served from S3 backing instead of the cache while it recovers. - `hevlayer_document_cache_cold_starts_total` and `hevlayer_document_cache_cold_start_seconds` — the demand-triggered reconnect-and-refill cycle after the cache restarts. - Gateway warn logs `Aerospike chunk write failed (best-effort)` and `Aerospike chunk read failed; falling back to S3 backing`. ## Client failures When the gateway is unreachable, the SDKs return the gateway connection error. They do not retry directly against a backing store. That keeps the client surface backend-neutral: a namespace may be backed by turbopuffer or another store selected by `VectorStore`, and callers should not need a store-specific credential or fallback path. --- # Layer CLI Source: https://hevlayer.com/docs/cli The `layer` CLI operates hevlayer from the terminal. It manages named environments, initializes adopted namespaces for sharding, observes index, pipeline, and UDF state from the gateway, mints and revokes API keys, shows VectorStore and Warehouse health, and runs Function manifests. Every read goes through the gateway API with an API key; besides the [`install` lifecycle](#install-an-aws-environment), only `run` touches Kubernetes — it applies the Function CR, registers the UDF spec with the gateway, triggers discovery, and optionally watches until the queue drains. `run` is the only command that needs a kube context: set it on the environment with `--kube-context`/`--kube-namespace` or per invocation with `--context`/`--kube-namespace`. ## Install From the repository root: ```sh go build -o layer ./apps/layer-cli ``` ## Install An AWS Environment `layer install` provisions a full AWS environment — Terraform (VPC, EKS, IAM/IRSA, S3, ECR) followed by the Helm release — as a single shot. `--profile demo` (the default) installs the lean footprint; `--profile indexing` adds the dedicated document-cache node pool. `layer install status` reports release and workload health, and `layer install uninstall` tears the environment down. The command runs from a `hev/layer` source checkout, which carries the Terraform configuration and the Helm chart it drives: ```sh git clone https://github.com/hev/layer cd layer go build -o layer ./apps/layer-cli ./layer install ``` Missing required inputs prompt interactively on a TTY; the command prints the resolved plan and asks for confirmation before provisioning anything. The [Install](/docs/install/) page is the reference for every flag, profile, and value it sets. ## Configuration `layer` reads named environments from `~/.hevlayer/config.toml`. The directory is created with mode `0700`; the config file is written with mode `0600`. ```toml active = "partner" [envs.partner] base_url = "https://aws-us-east-1.hevlayer.com" api_key = "..." kube_context = "partner-cluster" kube_namespace = "hevlayer" [envs.local] base_url = "http://localhost:8080" api_key = "dev" kube_context = "kind-hevlayer" ``` Resolution order is: | Priority | Source | | --- | --- | | 1 | Explicit flags such as `--base-url`, `--api-key`, `--context`, and `--kube-namespace` | | 2 | `LAYER_BASE_URL`, `LAYER_API_KEY`, and the `HEVLAYER_` twins | | 3 | Environment selected by `--env` or `LAYER_ENV` | | 4 | Active environment in `~/.hevlayer/config.toml` | | 5 | Built-in base URL default | A shell exporting `LAYER_BASE_URL` or `LAYER_API_KEY` keeps the env-var-only behavior and does not need a config file. `--env` and `LAYER_ENV` select an environment for one invocation without changing the active environment. | Flag | Environment | Default | | --- | --- | --- | | `--base-url` | `LAYER_BASE_URL`, `HEVLAYER_BASE_URL` | `https://aws-us-east-1.hevlayer.com` | | `--api-key` | `LAYER_API_KEY`, `HEVLAYER_API_KEY` | none | | `--env` | `LAYER_ENV` | active config env | | `-o`, `--output` | none | `table` | Output formats are `table`, `json`, and `names`. ## Environments ```sh layer env add partner --base-url https://aws-us-east-1.hevlayer.com \ --api-key "$LAYER_API_KEY" --kube-context partner-cluster \ --kube-namespace hevlayer layer env use partner layer env ls layer env show partner -o json layer env rm partner ``` `env add` prompts for missing values on a TTY. On a non-TTY, the required values must be supplied by flags. API keys are masked in `env ls` and `env show`. ## Initialize a Namespace ```sh layer init products --shards 8 layer init products --shards 8 --watch=false layer init products --shards 8 --poll-interval 5s ``` `init` calls `POST /v2/namespaces/{namespace}/init` through the selected gateway environment. The command creates or reattaches to the namespace shard marker, starts the shard backfill, and watches by default until `shard_lag_rows` reaches `0` and scatter/gather is active. Re-running with the same shard count is idempotent; requesting a different shard count returns a conflict message instead of changing the marker. ## Run A Function ```sh layer run -f tag-products.yaml layer run -f tag-products.yaml --index amazon-products-staging layer run -f tag-products.yaml --detach layer run -f tag-products.yaml --rm ``` The input is a Kubernetes `Function` manifest. `--index` overrides `spec.targetNamespaces` with one target. `--context` selects a kubeconfig context; `--kube-namespace` selects the Kubernetes namespace for the Function CR. `--no-apply` skips the Kubernetes apply step for workers managed outside the operator. `spec.version` is registered with the gateway as the Function completion marker version. Bump it before re-running a Function after changing a model, prompt, taxonomy, or worker write contract. `--detach` returns after registration and discovery. Without `--detach`, the CLI polls UDF status until discovery has completed and `pending_count` and `processing_count` are both zero. A drained queue with failures exits non-zero. `--rm` deletes the gateway registration and, unless `--no-apply` is set, the Function CR after the queue drains cleanly. A drain with failures leaves both in place so you can inspect them. Watch a run from another terminal: ```sh layer udf list layer udf get product-tags --watch ``` `udf list` lists registered UDFs with pending, processing, failed, discovery sweep count, and indexed rate. `udf get` shows those fields for one UDF; `--watch` polls until `pending_count` and `processing_count` are both zero. ## TUI Bare `layer` on a TTY opens the read-only operations TUI (`layer browse` is the explicit spelling); on a non-TTY it prints usage and exits `2`. Press `i`/`f`/`p`/`k`/`e` to switch between indexes, functions, pipelines, keys, and environments from any view, `enter` to open a detail view, and `esc`/`q` to back out. Every view has a non-interactive command twin with the same data — the TUI humanizes timestamps and sizes; the commands emit raw values for scripting. | TUI view | Command | | --- | --- | | Environments | `layer env ls` | | Functions | `layer udf list` | | Function detail | `layer udf get UDF_ID [--watch]` | | Indexes | `layer index list` | | Index detail | `layer index get NAME` | | Pipelines | `layer pipeline list` | | Pipeline detail | `layer pipeline get ID` | | Keys | `layer keys ls` | | Key detail | `layer keys get KEY_ID` | The keys views are read-only like the rest of the TUI: minting and revoking stay in the commands. ## Keys ```sh layer keys mint cohort-reader --owner acme \ --entitle vectorstore.prod-turbopuffer=read \ --namespaces "cohort-*" \ --claim warehouse.prod-snowflake="notes:cohort:*:read" layer keys ls layer keys get cohort-reader layer keys revoke cohort-reader layer keys rm cohort-reader ``` `keys mint` creates the key through the gateway and prints the token once — alone on stdout, so `layer keys mint … | pbcopy` captures it; the metadata table goes to stderr. There is no way to print it again. `keys revoke` is the default way to retire a key and keeps its audit record. `keys rm` is rare permanent cleanup and accepts only a key already in the `Revoked` phase. | Flag | Shape | | --- | --- | | `--entitle` | `TARGET[=SCOPE[+SCOPE]]`, repeatable. Targets are `vectorstore.`, `warehouse.`, or `layer`. | | `--namespaces` | Upstream-namespace globs for the vectorstore entitlement, comma-separated. | | `--claim` | `TARGET=STRING`, repeatable. Appends an opaque claim string to that target's entitlement. | | `--expires-after` | Duration or `never`; defaults to `365d`. | `--entitle layer=admin` mints an admin key. For anything longer than a couple of flags, write the object instead: `layer keys mint -f key.yaml` takes the same `ApiKey` manifest `kubectl apply` does. `keys ls` and `keys get` show metadata only — key id, owner, phase, entitlement targets, expiry, last seen — never tokens or hashes. `revoke` is idempotent and keeps the record; `rm` hard-deletes it. All `keys` commands call the gateway key routes, which require a key with the `layer` entitlement at `admin` scope (or the bootstrap gateway key); no kube access is involved. ## Vector Store And Warehouse ```sh layer vectorstore list layer vectorstore get layer vectorstore get prod-turbopuffer -o json layer warehouse list layer warehouse get prod-snowflake ``` `vectorstore list` shows declared stores with kind, default marker, region, reachability, and turbopuffer org id. `vectorstore get` prints endpoint, credential Secret reference, inbound auth mode, reachability, observed generation, and the turbopuffer dashboard link when the store has `spec.turbopuffer.orgId`. Without a name, `vectorstore get` selects the default-marked store, or the only store when exactly one exists. `warehouse list` leads with name, kind, phase, verification time, and consumer counts. `warehouse get NAME` shows the Snowflake source identity, credential Secret reference, verification status, failure reason, and what still depends on the warehouse. These commands are read-only and gateway-backed; they never read or print Secret contents. ## Ask The Docs `layer ask` queries the committed docs digest with the `ask` CLI. It is keyless and local by default: from a checkout, it finds `site/.hev-ask`, prefers a sibling `../ask` source checkout, and falls back to the docs site's installed `@hevmind/ask` package or an `ask` binary on `PATH`. ```sh layer ask tree layer ask grep "warm cache" layer ask cat api/query layer ask glossary get watermark layer -o json ask tree ``` Use `--endpoint` to query a deployed hev ask endpoint instead of the local digest: ```sh layer ask --endpoint https://hevlayer.com/api/ask tree ``` ## Inspect An Index ```sh layer index get shop-products layer index get shop-products -o json ``` `index get` reports row count, size, schema summary, last write, stable watermark and lag, and index (WAL) and cache state on any gateway. The RECENT SNAPSHOTS table (and `index list`'s last-snapshot column) additionally need snapshot history, which is served only by a managed/pro gateway; timestamps and sizes are raw (epoch-ms, bytes) and `-o json` carries the full snapshot list where available. ## Manage Snapshots ```sh layer index snapshot shop-products --field category layer index policy shop-products --facet-field category --facet-field brand --interval 5m --retention 30d ``` `index snapshot` creates an on-demand snapshot job and waits for it to complete; `index policy` writes the same `facetFields`, `interval`, and `retention` shape used by `Index.spec.snapshot`, so shared-gateway namespaces can enable the automatic writer without applying a Kubernetes resource. Both work against the free, self-hosted gateway — no operator or license required. ## Delete An Index ```sh layer index delete shop-products layer index delete shop-products shop-products-staging layer index delete --prefix shop- layer index delete --prefix shop- --yes ``` `index delete` purges the upstream turbopuffer namespace, document cache rows and snapshot mirrors, S3 snapshots/search history/clickstream/shard metadata, in-memory job state, and the operator-discovered Index CR where GC is enabled. Pass one or more names, or `--prefix` to delete every index whose name starts with that prefix — name arguments and `--prefix` are mutually exclusive, and the prefix must be non-empty. The prefix form lists the matched indexes for confirmation before deleting; on a TTY it prompts, and `--yes` skips the prompt. `--yes` is required when stdin is not a TTY. A prefix that matches nothing exits `0` without deleting. ## Pipelines ```sh layer pipeline list layer pipeline get product-images ``` `pipeline list` reads registered pipelines and fans out to the [pipeline status API](/docs/api/pipelines#wait-for-completion) for each one's live queue depth (`pending`, `processing`, `failed`, `rate/min`); `pipeline get` adds target namespace, distance metric, and created-at. A pipeline with no worker staged into it yet renders without queue counts rather than erroring. Reads need only an API key — no kube access. `layer push` is deferred to the managed build/dev-loop milestone. --- # Dashboard Source: https://hevlayer.com/docs/dashboard import Callout from "../../components/docs/Callout.astro"; The Layer dashboard is the operator UI that ships in-cluster alongside the gateway, as the `layer-dashboard` Deployment and Service. This page covers running it: the access it needs, how to reach it, how to gate it, and how to turn it off. ## Access it needs The dashboard is read-mostly and backed by three sources, each with its own grant: - **The gateway API** — the same endpoints customers use, plus the Prometheus-compatible metrics proxy at `/v2/metrics`. Authenticated with a gateway bearer (`LAYER_GATEWAY_API_KEY`). In `deriveFromStore` mode this is the default `VectorStore` credential; in `keys` mode it is the configured inbound worker key. It does not touch PostgreSQL, Aerospike, or VictoriaMetrics directly — metrics and cost arrive through the gateway proxy and `/v2/cost`. - **The Kubernetes API** — reads `hevlayer.com` CRDs (VectorStores, Indexes, InfraRules, Pipelines) and the workload objects behind them (pods, deployments/statefulsets, HPAs, KEDA ScaledObjects, nodes) through RBAC bound to its ServiceAccount. `dashboard.kubeAccess.enabled` grants the read role; with it off the dashboard still runs but the cluster/scaling views show a "kube access not configured" banner. `dashboard.writeAccess.enabled` adds the operator write role: Index spec patches, Karpenter NodePool disruption, VectorStore and Warehouse create/edit, and namespace-scoped Secret writes for credentials. Set it `false` for a read-only install. - **Cost data** — the cost view reads the gateway cost API. turbopuffer spend is metered from upstream billing counters; AWS spend is read by the gateway from Cost Explorer using the gateway IRSA role. The dashboard does not need direct AWS cost API permissions for the standard cost view. In a Terraform install, still pass `dashboard.serviceAccount.roleArn` from the `layer_dashboard_role_arn` output so the dashboard ServiceAccount is annotated with its cluster-specific IRSA role at first boot. ## Networking The dashboard is an operator tool. **Reach it over a port-forward** rather than exposing it publicly: ```sh kubectl port-forward -n svc/layer-dashboard 8081:8081 ``` Then open `http://localhost:8081`. Customer workloads only ever receive the gateway base URL and credentials — never the dashboard. ## Basic auth HTTP Basic auth sits in front of every dashboard route and is **required** — the dashboard refuses to start without it. Set credentials through the chart: ```yaml dashboard: basicAuth: user: ops password: ``` The chart render fails if either field is blank while the dashboard is enabled. ## Disabling the dashboard The dashboard is optional. Disable it and the Deployment, Service, RBAC, and ingress all skip rendering: ```yaml dashboard: enabled: false ``` The gateway and transform runtime run unchanged without it; you lose only the operator UI. ## Operational notes ### Pipeline queue states The pipeline overview combines gateway queue counters with the matching `Pipeline` resources and their operator-managed Deployments and KEDA ScaledObjects. A queue with pending work is presented as: - **PAUSED** when every matching `Pipeline` has `spec.paused: true`. Its workers are intentionally at zero replicas; pending documents remain queued. - **CRON-GATED** when its cron ScaledObject is inactive. The card shows the next UTC window, scaler activity, and managed worker replica counts. - **STALLED** when work is pending with no processing or indexing activity and neither an intentional pause nor a closed cron window explains the stop. Failed-document counts, operator condition messages, and missing or inactive managed workers appear as reason hints on the same card. A growing queue that is still processing remains **BACKLOG** rather than **STALLED**. The **data** tab is split into indexes, vector store, and warehouse views. Indexes come from `/v2/namespaces`; vector stores and warehouses come from the gateway's `/v2/vectorstores` and `/v2/warehouses` projections, so the dashboard shows the same credential-safe shape as the CLI and SDKs. When write access is enabled, the vector store and warehouse panes can create and edit those CRDs. Raw credentials are accepted only in the form submission; the dashboard writes them into Kubernetes Secrets first, then creates or patches the CR. The read views keep showing only Secret references. The dashboard is intentionally read-mostly. Mutating actions (UDF pause, InfraRules or scaling edits, data-supply CRD apply) are gated through CRD apply or explicit confirm dialogs, and write access is governed separately by `dashboard.writeAccess.enabled`. --- # Introduction Source: https://hevlayer.com/docs/api/introduction import CodeTabs from "../../../components/docs/CodeTabs.astro"; import StoreSwitch from "../../../components/docs/StoreSwitch.astro"; import StoreNote from "../../../components/docs/StoreNote.astro"; import Upstream from "../../../components/docs/Upstream.astro"; import FeatureGate from "../../../components/docs/FeatureGate.astro"; Layer speaks the upstream store's native wire protocol, adding the fields it needs through `x-layer-*` headers, so pointing a client at the gateway just works — and gains stable reads, the document cache, and Layer's enhanced search features without changing the requests you already send. ## Install There are four ways to call Layer: the Python client, the Go client, the TypeScript client, and the REST API itself. The clients are generated from `apps/layer-gateway/openapi.yaml`, so all four expose the same operations — every endpoint page on this site shows them side by side. Anything the clients can do, plain HTTP can do. ```sh pip install hevlayer # Python 3.11+ go get github.com/hev/layer-go # Go 1.22+ npm install hevlayer # Node 18+ ``` Point a client at the gateway: ```python import os from hevlayer import AsyncHevlayer client = AsyncHevlayer( base_url=os.environ["LAYER_GATEWAY_URL"], api_key=os.environ["LAYER_GATEWAY_API_KEY"], ) ``` ```go import ( "os" hevlayer "github.com/hev/layer-go" ) client := hevlayer.NewClient( hevlayer.WithBaseURL(os.Getenv("LAYER_GATEWAY_URL")), hevlayer.WithAPIKey(os.Getenv("LAYER_GATEWAY_API_KEY")), ) ``` ```typescript import { Hevlayer } from "hevlayer"; const client = new Hevlayer({ baseUrl: process.env.LAYER_GATEWAY_URL, apiKey: process.env.LAYER_GATEWAY_API_KEY, }); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` Code examples across these pages assume this `client` — and in Go, a `ctx context.Context`. The cURL tab on each page is the bare REST contract; any HTTP stack works the same way. ## Authentication Every request carries `Authorization: Bearer `. The gateway accepts two kinds of bearer: - **The store key.** The default `VectorStore` credential (the turbopuffer key you already own) is accepted as an admin bearer. This is the drop-in default for a turbopuffer-backed store: point an existing client at the gateway and keep your key. No setup, full access. - **A minted key.** Admin can mint keys scoped to a set of namespaces crossed with `read`/`write` — hand one to a team or a service without exposing the rest of the store. Minted keys are gateway-only and never work against the upstream directly. See [API keys](/docs/api/keys). Routes are classified `read`, `write`, or `admin`; each endpoint page notes anything beyond the obvious (GET/query-shaped routes are `read`, namespace writes are `write`, Pipeline/Function/key management is `admin`). A request past a key's scope or namespace grant answers 403 with the reason named. Connection environment variables: | Variable | Purpose | | --- | --- | | `LAYER_GATEWAY_URL` | Base URL of the gateway. | | `LAYER_GATEWAY_API_KEY` | Bearer token sent on every gateway request. In `deriveFromStore` mode this is the default `VectorStore` credential; in `keys` mode it is one of the configured inbound keys. | A turbopuffer-backed store also accepts a direct fallback for turbopuffer-compatible SDK calls when the gateway is unreachable: | Variable | Purpose | | --- | --- | | `TURBOPUFFER_API_KEY` | Optional direct fallback key. | | `TURBOPUFFER_API_URL` | Optional direct fallback base URL; defaults to `https://aws-us-east-1.turbopuffer.com`. | Additional language targets are added through the SDK harness rather than maintained by hand. ## Gateway failures The Python, Go, and TypeScript SDKs talk to the Layer gateway. If the gateway is unreachable, the original connection error is returned. SDKs do not retry directly against a backing store, because the server chooses that store from the namespace's `VectorStore` and the client surface stays backend-neutral. ## Enhancements to upstream routes Each of the routes below is wire-compatible with turbopuffer. The body of each section describes only what Layer overlays on top. ### Write — `POST /v2/namespaces/{ns}` Upstream contract for upsert, delete, and `patch_rows`. - Best-effort Aerospike document-cache mirror before explicit-id upstream writes. - Server-stamped `_hevlayer_upserted_at` on every upsert and patch, which powers the consistency watermark on the query path. - `_hevlayer_*` attributes are reserved — writes to them are rejected. Page: [Write](/docs/api/write). ### Query — `POST /v2/namespaces/{ns}/query` Upstream contract for vector and FTS queries — request shape, ranking, filters, attribute selection. - Stable reads via an injected `_hevlayer_upserted_at <= watermark` predicate while the upstream index is `updating`. - One-shot 429 retry with the watermark filter forced on, for queries that race a write storm. - `x-layer-stable-as-of` returned on stable-read responses so callers can correlate freshness across reads. Page: [Query](/docs/api/query). ### Metadata — `GET /v2/namespaces/{ns}/metadata` Upstream contract for namespace metadata — schema, row count, index status, timestamps. - Proxied upstream verbatim, then enriched with a `layer` block containing `stable_as_of` and `is_stable`. Page: [Namespace metadata](/docs/api/namespace-metadata). ### Cache warm hint — `GET /v1/namespaces/{ns}/hint_cache_warm` Upstream contract for the cache warm hint. - With no query parameters: a raw upstream passthrough, response returned verbatim. - With any warm option supplied: forwards the hint upstream and runs Layer-side warm steps — a warm job to backfill the Aerospike document cache from origin, plus a mirror of the latest S3 snapshot body into Aerospike. Each step is independently toggleable per request. Page: [Warm cache](/docs/api/warm-cache). ## Cross-cutting conventions These apply to every endpoint Layer proxies, whether the route is upstream-compatible or Layer-only. - **`_hevlayer_*` reserved.** Document attributes prefixed with `_hevlayer_` are reserved for the proxy layer. Writing to them is a validation error; reading them is fine when explicitly requested. The gateway stamps `_hevlayer_upserted_at` itself on every upsert and patch — a caller-supplied value is ignored and overwritten with the server's epoch-ms watermark. - **Hard vs soft failures.** The backing engine's write and query failures are hard failures and return 5xx. Aerospike document-cache failures are soft and never block the response. - **`x-layer-cache` header.** Fetch responses include `hit`, `miss`, or `miss-on-error` so callers can distinguish a cold cache from an outage. - **Response headers.** Query pagination uses `x-layer-next-cursor`. See [Response headers](/docs/api/response-headers). ## Compatibility posture Layer is a drop-in for existing turbopuffer clients. Routes the upstream does not implement are namespaced under `/v2/` and do not shadow upstream behavior; a request to a route Layer doesn't proxy returns 404 rather than being silently re-routed to an upstream that might handle it differently. --- # Write & Stage Source: https://hevlayer.com/docs/api/write import StoreSwitch from "../../../components/docs/StoreSwitch.astro"; import StoreNote from "../../../components/docs/StoreNote.astro"; import Upstream from "../../../components/docs/Upstream.astro"; import FeatureGate from "../../../components/docs/FeatureGate.astro"; import CodeTabs from "../../../components/docs/CodeTabs.astro"; This is Layer's write API: `POST /v2/namespaces/{ns}` with a native write body (upserts, deletes, patches, and filter writes, combined in one request), sent with `write_namespace`. Backed by turbopuffer, writes are wire-compatible with the upstream `POST /v2/namespaces/{ns}` endpoint and forwarded as-is after validation. The request body is documented upstream; the sections below are what Layer adds on top. Layer stamps every row-producing write with `_hevlayer_upserted_at` and mirrors it to the document cache. The stamp is what holds the [read watermark](/docs/api/query); the full set of reserved attributes Layer manages on a row lives in the [document model](/docs/document-model). ## Status Layer validates the body before forwarding and can fail independently of the backing store, so the write path carries a few statuses a plain proxy wouldn't: - **200 OK** — applied to the store and stamped. - **422 Unprocessable Entity** — Layer rejected the body before forwarding: no recognized native write operation, a reserved `_hevlayer_*` attribute name, a removed custom-write key, or a schema type the configured store cannot represent. The body is a Layer error (`{ "error": "validation_error", … }` or `{ "error": "UnsupportedByStore", … }`), not a store one. - **Upstream passthrough** — any non-2xx the backing store returns is relayed verbatim. - **502 Bad Gateway** — Layer could not reach the backing store (`{ "error": "upstream_error", … }`); the write did not apply. The passthrough case includes a failed conditional write (`upsert_condition`, `patch_condition`, `delete_condition`) — turbopuffer's error body comes back untouched. ## Stage Stage caches a document before it's upserted into your vector store. That O(1) read/write is especially useful for queuing chunks in a [two-stage pipeline](/docs/api/pipelines), where a CPU worker stages chunks and a GPU worker reads them back to write vectors. Staged documents are ephemeral until they're upserted, though — a Layer document cache outage loses anything still staged. ```python await client.put_pipeline_document_chunks("product-images", "asin-B08N5WRWNW", { "chunks": [ {"id": "asin-B08N5WRWNW-0", "text": "Wireless noise-cancelling headphones"}, {"id": "asin-B08N5WRWNW-1", "text": "40-hour battery life", "metadata": {"page": 2}}, ], }) ``` ```go client.PutPipelineDocumentChunks(ctx, "product-images", "asin-B08N5WRWNW", &hevlayer.PutChunksRequest{ Chunks: []hevlayer.Chunk{ {ID: "asin-B08N5WRWNW-0", Text: "Wireless noise-cancelling headphones"}, {ID: "asin-B08N5WRWNW-1", Text: "40-hour battery life", Metadata: map[string]interface{}{"page": 2}}, }, }) ``` ```typescript await client.putPipelineDocumentChunks("product-images", "asin-B08N5WRWNW", { chunks: [ { id: "asin-B08N5WRWNW-0", text: "Wireless noise-cancelling headphones" }, { id: "asin-B08N5WRWNW-1", text: "40-hour battery life", metadata: { page: 2 } }, ], }); ``` ```bash curl -X PUT "$LAYER_GATEWAY_URL/v2/pipelines/product-images/documents/asin-B08N5WRWNW" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "chunks": [ {"id": "asin-B08N5WRWNW-0", "text": "Wireless noise-cancelling headphones"}, {"id": "asin-B08N5WRWNW-1", "text": "40-hour battery life", "metadata": {"page": 2}} ] }' ``` Staging stores chunks in the Aerospike document cache and marks the document `pending`. Re-staging the same document ID replaces the chunks and resets state to `pending`. The full pipeline API is documented under [Pipelines](/docs/api/pipelines). --- # Blobs Source: https://hevlayer.com/docs/api/blobs import CodeTabs from "../../../components/docs/CodeTabs.astro"; Blobs store opaque bytes in Layer's S3 bucket and serve them through the gateway with Aerospike as a pull-through hot cache. A row never stores the bytes themselves. It stores an ordinary string attribute such as `image_blob: "blob://products/"`. Use blobs for media or other binary payloads that must have a durable home outside the vector engine while still riding the gateway read path. ## Routes | Route | Method | Behavior | | --- | --- | --- | | `PUT /v1/namespaces/{ns}/blobs` | PUT | Store raw bytes by sha256 and return a `blob://` reference. | | `GET /v1/namespaces/{ns}/blobs/{sha256}` | GET | Serve bytes from Aerospike raw cache, falling back to S3 and backfilling cache. | ## Store ```python with open("image.jpg", "rb") as f: stored = await client.put_blob("products", f.read()) print(stored.ref) ``` ```go body, _ := os.ReadFile("image.jpg") stored, err := client.PutBlob(ctx, "products", body, nil) ``` ```typescript import fs from "node:fs/promises"; const bytes = await fs.readFile("image.jpg"); const stored = await client.putBlob("products", bytes); ``` ```bash curl -X PUT "$LAYER_GATEWAY_URL/v1/namespaces/products/blobs" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/octet-stream" \ --data-binary @image.jpg ``` Response: ```json { "ref": "blob://products/9f86d081884c7d659a2feaa0c55ad015...", "sha256": "9f86d081884c7d659a2feaa0c55ad015...", "size": 48213 } ``` The same bytes always return the same reference. The route rejects empty bodies and bodies over the gateway's blob size cap. Write the returned `ref` as a normal row attribute: ```json { "id": "B0123", "vector": [0.1, 0.2], "image_blob": "blob://products/9f86d081884c7d659a2feaa0c55ad015..." } ``` The removed document `blobs` payload shape is still rejected. Binary bytes do not traverse `/v2/namespaces/{ns}` writes. ## Fetch ```python image = await client.get_blob("products", stored.sha256) ``` ```go image, err := client.GetBlob(ctx, "products", stored.Sha256) ``` ```typescript const image = await client.getBlob("products", stored.sha256); ``` ```bash curl "$LAYER_GATEWAY_URL/v1/namespaces/products/blobs/$SHA256" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -o image.jpg ``` Successful responses include immutable cache headers: ```http Cache-Control: public, max-age=31536000, immutable ETag: "" ``` The gateway sniffs common image types (`jpeg`, `png`, `gif`, `webp`) for `Content-Type`; otherwise it returns `application/octet-stream`. ## Warm Policy Blob reads are pull-through today: cache miss reads S3, then backfills Aerospike best-effort. `PUT ...?warm=true` can write through to cache for one object. Bulk `hint_cache_warm?blobs=true` is intentionally not part of the first slice. It needs a namespace declaration for which attributes are blob references and an explicit cache budget because images are much larger than document attributes. --- # Query & Fetch Source: https://hevlayer.com/docs/api/query import CodeTabs from "../../../components/docs/CodeTabs.astro"; import StoreSwitch from "../../../components/docs/StoreSwitch.astro"; import StoreNote from "../../../components/docs/StoreNote.astro"; import Upstream from "../../../components/docs/Upstream.astro"; import FeatureGate from "../../../components/docs/FeatureGate.astro"; This is Layer's query API. Layer reports its own metadata in `x-layer-*` response headers. Backed by turbopuffer, Layer runs on top of your own turbopuffer namespace, adding stable reads, the document cache, and search history around it. The native query body — `vector`, `rank_by`, `filters`, and multi-query — is documented upstream; the sections below are what Layer adds on top. ## Stable reads Layer's query API defaults to [stable reads](/docs/concepts#control-loops). Every response carries an `x-layer-stable-as-of` watermark: the point the backing index is known to be caught up to. A query issued right after an upsert never returns partially-indexed rows and never 429s under write pressure, so derived views like [facets](/docs/api/snapshots) and [counts](/docs/api/scans) stay in sync with your index. ```http HTTP/1.1 200 OK x-layer-stable-as-of: 1715600400000 {"rows":[{"id":"asin-B08N5WRWNW","$dist":0.42,"title":"..."}]} ``` This is achieved by: 1. Queries run at `consistency=eventual` upstream, so they never block on indexing. 2. A [control loop](/docs/concepts#control-loops) polls each registered namespace's `index.status` and records the latest status plus, when stable, a watermark equal to `poll_start - safety_margin`. Cold or updating namespaces use the fast polling interval; stable namespaces back off to the stable interval until the next write re-arms the fast tier. 3. Per-query decision: - `Updating` → inject a hidden `_hevlayer_upserted_at <= watermark` predicate so the read never sees partially-indexed rows. - `Stable` or `Unknown` → run without the predicate. The upstream index is caught up (or no contrary evidence exists). 4. On a 429 to an unfiltered query, Layer retries once with the watermark filter forced on. Responses report `x-layer-stable-as-of` (epoch ms) when the watcher has a watermark for the namespace. It is omitted on a cold-start gateway that has not yet observed a stable poll. Query responses always carry a top-level `next_cursor` in the body — the next page token when more results may exist, or `null` on the last page — and mirror a non-null token in the `x-layer-next-cursor` header. Pass a non-null value back as `cursor` in the next request body. Native vector queries use a score-band cursor. Layer-fused `HybridText` and executed `Auto` routes use a bounded server-side re-fetch cursor, capped at 10,000 ranked rows. To pin a query to an explicit temporal cut, pass either `as_of` or `between` in the request body. `as_of: 1747300000123` conjoins `_hevlayer_upserted_at <= 1747300000123`; `between: [lo, hi]` conjoins `lo < _hevlayer_upserted_at <= hi`. They are mutually exclusive. Temporal selectors compose with `filters`, `nearest_to_id`, a top-level batch query, and the Layer `HybridText` / `Auto` rank expressions. Native turbopuffer passthrough bodies remain upstream-shaped and are not rewritten. Stable-read behavior is set per namespace with the `consistency` field on the [Index CRD](/docs/kubernetes/index-crd). Two gateway tunables control the watcher: | Variable | Default | Purpose | | --- | --- | --- | | `CONSISTENCY_POLL_INTERVAL_MS` | 1000 | Fast cadence for cold and updating namespaces. | | `CONSISTENCY_STABLE_POLL_INTERVAL_MS` | 60000 | Slow cadence for namespaces last observed stable. Set equal to the fast interval to restore one uniform cadence. | | `CONSISTENCY_SAFETY_MARGIN_MS` | 500 | Cushion between poll time and watermark to cover in-flight upserts. | ## Query by id Pass `nearest_to_id` in place of `vector` to rank by stored document vectors instead of a raw query vector — exactly one of the two is required. `nearest_to_id` takes an **array of document ids**: the gateway resolves each id's vector (document cache first, the namespace's configured VectorStore on miss with a cache backfill) and averages them component-wise into a single centroid, then ranks nearest neighbors to that centroid. Pass one id to rank by a single document; pass several to get "more like these" over a set of seeds. ```python response = await client.query_namespace("products", { "nearest_to_id": ["asin-B08N5WRWNW", "asin-B07PXGQC1Q"], "top_k": 10, "include_attributes": ["title", "category"], }) ``` ```go response, err := client.QueryNamespace(ctx, "products", &hevlayer.QueryRequest{ NearestToID: []string{"asin-B08N5WRWNW", "asin-B07PXGQC1Q"}, TopK: 10, IncludeAttributes: []string{"title", "category"}, }) ``` ```typescript const response = await client.queryNamespace("products", { nearest_to_id: ["asin-B08N5WRWNW", "asin-B07PXGQC1Q"], top_k: 10, include_attributes: ["title", "category"], }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/query" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "nearest_to_id": ["asin-B08N5WRWNW", "asin-B07PXGQC1Q"], "top_k": 10, "include_attributes": ["title", "category"] }' ``` | Outcome | Status | | --- | --- | | Every id resolved (cache or origin) | 200, ranked results | | Any id has no stored vector anywhere | 404 (names the missing ids) | | `nearest_to_id` empty, or both/neither of `vector` / `nearest_to_id` | 422 | The centroid is an unweighted mean, so seed ids contribute equally regardless of how many you pass. All resolved vectors share the namespace's dimensionality, so no reconciliation is needed across seeds. This fuses the seeds into one ranking; to run several *independent* rankings in a single request, see [batch query](#batch-query). ## Rank expressions Pass `rank_by` with `top_k` when you need an explicit ranking operator instead of the top-level `vector` / `nearest_to_id` shape. Layer handles the portable subset with the same cache, history, and stable-read behavior as vector queries. Native upstream query bodies that omit `top_k` remain pass-through. `rank_by` is mutually exclusive with `vector` and `nearest_to_id`. ## Batch query `nearest_to_id` fuses several seeds into a **single** ranking. To run several **independent** queries in one round trip, each with its own ranking, post a `queries` array. The response is a parallel `results` array — one ranked result set per query, in request order: `{ "results": [{ "rows": ... }] }`. Layer holds every leg on the same stable cut, so a batch reads one consistent view of the index. (The method is `batch_query_namespace`. It is named apart from turbopuffer's own upstream multi-query — a `rerank_by` body, which Layer passes through unchanged, as noted at the end of this section — to keep the two distinct.) ```python batch = await client.batch_query_namespace("products", { "queries": [ {"rank_by": ["vector", "ANN", [0.1, 0.2, 0.3]], "top_k": 10}, {"rank_by": ["title", "BM25", "wireless earbuds"], "top_k": 10}, ], }) # batch.results[0].rows ranked by vector; batch.results[1].rows by text ``` ```go batch, err := client.BatchQueryNamespace(ctx, "products", &hevlayer.BatchQueryRequest{ Queries: []hevlayer.TurbopufferQueryRequest{ {"rank_by": []any{"vector", "ANN", []float64{0.1, 0.2, 0.3}}, "top_k": 10}, {"rank_by": []any{"title", "BM25", "wireless earbuds"}, "top_k": 10}, }, }) ``` ```typescript const batch = await client.batchQueryNamespace("products", { queries: [ { rank_by: ["vector", "ANN", [0.1, 0.2, 0.3]], top_k: 10 }, { rank_by: ["title", "BM25", "wireless earbuds"], top_k: 10 }, ], }); // batch.results[0].rows ranked by vector; batch.results[1].rows by text ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/query" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "queries": [ {"rank_by": ["vector", "ANN", [0.1, 0.2, 0.3]], "top_k": 10}, {"rank_by": ["title", "BM25", "wireless earbuds"], "top_k": 10} ] }' ``` All legs in a non-fused batch share one `x-layer-stable-as-of` value. A leg may use native `rank_by`, or the Layer `vector` / `nearest_to_id` single-query shape; `nearest_to_id` is resolved before the leg is sent upstream. Batches must contain 2 to 16 legs. `cursor` is rejected at the top level and per leg because pagination is single-query only. When `rerank_by` is present, Layer treats the request as an upstream fused query and passes the body through unchanged. Reach for a batch query when you genuinely need N rankings — distinct user queries batched into one round trip, or hybrid retrieval fused upstream with RRF. Reach for `nearest_to_id` when many seeds should collapse into one "more like these" ranking. To get typo-tolerant text search without building the fused query yourself, see [hybrid text fusion](#hybrid-text-fusion). Every leg here targets the namespace in the path. To fan one query across a **set** of namespaces — and merge them into a single ranked list — see [federated query](/docs/api/federated-query). ## Hybrid text fusion BM25 misses typos and morphological variants; fuzzy matching alone loses the relevance signal BM25 provides. `HybridText` runs both in one request: the gateway tokenizes your input string, expands it into one BM25 leg plus one fuzzy leg per token, and the effective legs are RRF-fused into one ranking. One expression in, typo-tolerant ranked results out. `HybridText` is a Layer-only `rank_by` spelling on the existing query route — no new endpoint, no client changes beyond the expression. The gateway tokenizes with [`alyze`](https://github.com/turbopuffer/alyze), turbopuffer's own open-source tokenizer and the same code that segmented your text at index time, so query tokens match index terms by construction. The ranked field must be indexed for both full-text and fuzzy matching — declare it `{"type": "string", "full_text_search": true, "fuzzy": true}` in the namespace schema. The BM25 leg uses the full-text index; the per-token fuzzy legs use the fuzzy index. ```python response = await client.query_namespace("support-tickets", { "rank_by": ["content", "HybridText", "conection timout kubernets"], "top_k": 10, "filters": ["tenant", "Eq", "t-42"], "include_attributes": ["content", "title"], }) ``` ```go response, err := client.QueryNamespace(ctx, "support-tickets", &hevlayer.QueryRequest{ RankBy: []any{"content", "HybridText", "conection timout kubernets"}, TopK: 10, Filters: []any{"tenant", "Eq", "t-42"}, IncludeAttributes: []string{"content", "title"}, }) ``` ```typescript const response = await client.queryNamespace("support-tickets", { rank_by: ["content", "HybridText", "conection timout kubernets"], top_k: 10, filters: ["tenant", "Eq", "t-42"], include_attributes: ["content", "title"], }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/support-tickets/query" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "rank_by": ["content", "HybridText", "conection timout kubernets"], "top_k": 10, "filters": ["tenant", "Eq", "t-42"], "include_attributes": ["content", "title"] }' ``` An optional fourth tuple element tunes the expansion. Defaults: ```json ["content", "HybridText", "conection timout kubernets", { "fuzziness": "auto", "rank_constant": 60, "per_leg_limit": null }] ``` | Option | Default | Meaning | | --- | --- | --- | | `fuzziness` | `"auto"` | Edit-distance tolerance for the fuzzy legs, keyed to each token's length (turbopuffer requires at least 3 query characters per edit). `"auto"` permits up to distance 2: exact for tokens of 3–5 characters, distance 1 for 6–8, distance 2 for 9 or more. Fixed `0`, `1`, or `2` caps the ladder, so `0` is exact-only. | | `rank_constant` | `60` | turbopuffer's RRF constant, passed through verbatim. Integer > 0. | | `per_leg_limit` | `clamp(5 × top_k, 50, 200)` | How deep each leg retrieves before fusion. Integer > 0. | | `threads` | `Index.spec.scan.threads`, else `8` | Maximum concurrent upstream requests when the gateway scatter/gathers the expansion across a [sharded](/docs/concepts#scattergather) namespace — the same fan-out control as [scans](/docs/api/scans). Clamped to active shards. No effect on unsharded namespaces, where the expansion is a single fused upstream call. | Set top-level `include_leg_breakdown: true` to return per-row `$fused.legs` attribution. Each leg entry reports the leg label, that row's 1-based rank within the leg, and the leg's raw score or distance. `rank` and `score` are `null` when the row fell outside that leg's `per_leg_limit` cut. Labels are `bm25`, `fuzzy:`, and `semantic` on routed fused queries. ### Tokenization The input string becomes tokens under a fixed, documented policy: 1. Split on Unicode (UAX #29) word boundaries and lowercase, using `alyze` — the code behind turbopuffer's production `word_v4` tokenizer. Punctuation-only tokens never survive the split. 2. Drop tokens shorter than 2 characters. 3. Dedupe. 4. Cap at 15 tokens (15 fuzzy legs + 1 BM25 leg = 16, the upstream subquery limit). Tokens cut by the cap are counted in `tokens_dropped`. Stemming, stopword removal, and language detection are not applied. The input must yield at least one token; one token is fine (that is still two legs, the RRF minimum). ### Response Results are the RRF-fused list. A `hybrid` block echoes the effective expansion so defaults are never invisible: ```json { "rows": [ { "id": "ticket-4117", "$score": 0.0639, "content": "...", "title": "Connection timeout on Kubernetes ingress" } ], "hybrid": { "tokens": ["conection", "timout", "kubernets"], "tokens_dropped": 0, "fuzziness": "auto", "rank_constant": 60, "legs": 4, "per_leg_limit": 50 }, "next_cursor": null } ``` | Field | Meaning | | --- | --- | | `$score` | RRF score. Comparable **within** a response, not across requests — do not threshold on it. | | `$fused.legs` | Present only when `include_leg_breakdown: true`. Per-leg attribution in effective leg order; each item has `leg`, `rank`, and `score`. | | `tokens` | Tokens that produced fuzzy legs, post-policy. | | `tokens_dropped` | Tokens removed by the 15-token cap (not by the length or punctuation rules). | | `legs` | Total effective subqueries in the fused expansion. Normally the fuzzy legs + 1 BM25 leg (plus 1 ANN leg on routed fused queries). On the `surfaced` fallback there is no BM25 leg, so `legs` equals the token count (one fuzzy leg per token). | | `surfaced` | Present and `true` only when the empty-result fallback fired (see [Surfacing fallback](#surfacing-fallback)). Absent on the normal path. | | `next_cursor` | Top-level field (not inside `hybrid`), always present in the body: the next page token, or `null` on the last page. Mirrors the `x-layer-next-cursor` header. Pass a non-null value back as `cursor`. | The `hybrid` block appears only on `HybridText` responses. On sharded namespaces it also reports the effective `threads` fan-out width. Requests without a `HybridText` expression, including native turbopuffer multi-query + `rerank_by` bodies, keep their upstream-shaped responses byte-for-byte. ### Surfacing fallback Every primary leg ranks by BM25 over the full input, which upstream scores at zero — and drops — when no token matches a stored term exactly. A fully-misspelled query therefore fuses to zero rows. When the primary expansion returns nothing, Layer re-runs one fuzzy leg per token, reorders each leg by field/token edit distance, and fuses those instead, so a typo-heavy query still surfaces near matches. The response then carries `"surfaced": true` in the `hybrid` block, and `legs` reflects the surfacing expansion — one fuzzy leg per token, with no BM25 leg. Working queries never reach this path; the fallback is purely additive and absent (`surfaced` omitted) on the normal path. ### Semantics - **Fusion.** RRF uses the effective leg order: BM25 first, then one fuzzy leg per token, then the semantic ANN leg on routed fused queries. `include_leg_breakdown: true` can require one upstream query per leg on unsharded namespaces so Layer can report per-leg ranks. - **One consistency cut.** Request-level `filters` are replicated to every leg, and the [stable-read](#stable-reads) watermark predicate is injected into every leg from a single read — all legs see the same cut. Responses carry `x-layer-stable-as-of` as usual. - **All-or-nothing.** Any leg failure fails the request; Layer does not return a partial fusion over surviving legs. - **Replay as a unit.** The query logs to [search history](/docs/api/search-history) as one entry carrying the `HybridText` expression, so replaying it reproduces the whole expansion. ### Validation All return `422`: | Condition | Why | | --- | --- | | Input yields zero tokens under the policy | Nothing to expand. | | `HybridText` inside a `queries` array | The expansion is already one batch deep by construction. | | `fuzziness` not in `"auto" \| 0 \| 1 \| 2`; `rank_constant` ≤ 0; `per_leg_limit` ≤ 0; `threads` < 1 | Out of range. | To let the gateway pick between hybrid text and semantic retrieval per query, see [query routing](#query-routing). ## Query routing Real search boxes receive both `"timout"` and `"why do pods lose their connection during deploys"`. The first wants [hybrid text fusion](#hybrid-text-fusion); the second wants semantic retrieval — lexical legs add noise on long conversational input, and ANN underperforms on short identifier-shaped tokens. `Auto` is a Layer-only `rank_by` spelling that makes that call per query, so the branch doesn't live ad hoc in your application code. The route is chosen from the shape of the input alone. Supply an inline `Embed` as the vector source and Layer resolves it only after the policy selects `semantic` or `fused`; a `hybrid_text` route never calls the embedding provider. Without either an inline `Embed` or a numeric vector, a vector-needing route returns the routing decision instead of results so the application can embed and re-issue. Short keyword traffic executes immediately and never pays for an embedding. ```python response = await client.query_namespace("support-tickets", { "rank_by": ["title", "Auto", user_input, { "vector": ["Embed", user_input, {"field": "content"}], }], "top_k": 10, "filters": ["tenant", "Eq", "t-42"], }) ``` ```go response, err := client.QueryNamespace(ctx, "support-tickets", &hevlayer.QueryRequest{ RankBy: []any{"title", "Auto", userInput, map[string]any{ "vector": []any{"Embed", userInput, map[string]any{"field": "content"}}, }}, TopK: 10, Filters: []any{"tenant", "Eq", "t-42"}, }) ``` ```typescript const response = await client.queryNamespace("support-tickets", { rank_by: ["title", "Auto", userInput, { vector: ["Embed", userInput, { field: "content" }], }], top_k: 10, filters: ["tenant", "Eq", "t-42"], }); ``` ```bash # One request: title is lexical; content selects the embedding profile. curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/support-tickets/query" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "rank_by": ["title", "Auto", "why do pods lose their connection during deploys", { "vector": ["Embed", "why do pods lose their connection during deploys", { "field": "content" }] }], "top_k": 10, "filters": ["tenant", "Eq", "t-42"] }' ``` ### Routing policy The v1 policy reads the token count of the input under the same [tokenizer policy](#tokenization) as hybrid text fusion: | Tokens | Route | Runs | | --- | --- | --- | | ≤ 2 | `hybrid_text` | The [hybrid text fusion](#hybrid-text-fusion) expansion. | | ≥ 8 | `semantic` | ANN over the supplied query vector. | | 3 – 7 | `fused` | Both, merged upstream by RRF. | Vector availability never changes which route is chosen — only whether it executes in this request. `hybrid_text` always executes and does not resolve an inline `Embed`; `semantic` and `fused` resolve it after routing, execute with a numeric `vector`, and defer only when neither is present. The policy is versioned (`"policy": "v1"`) so threshold changes are visible in [search history](/docs/api/search-history). ### Options The optional fourth tuple element: | Option | Default | Meaning | | --- | --- | --- | | `route` | `"auto"` | Force `"hybrid_text"`, `"semantic"`, or `"fused"` instead of applying the policy. Used on re-issue after a deferral, and for A/B comparison of strategies on the same input. | | `vector` | — | A numeric query vector, or `["Embed", input, {field?, model?}?]`. `field` selects the source or derived attribute whose embedding profile supplies the model and vector target; it defaults to the Auto lexical field. Layer resolves `Embed` only for `semantic` and `fused`. A derived `embed_` field requires `model`. | | `fuzziness` | `"auto"` | Forwarded to the `HybridText` expansion on the `hybrid_text` and `fused` routes: `"auto"`, `0`, `1`, or `2`. `0` forces exact-only matching. No effect on the `semantic` route. | When the chosen route expands hybrid-text legs, the hybrid defaults apply and the [`hybrid` echo block](#response) appears alongside `routing`. Set top-level `include_leg_breakdown: true` to add `$fused.legs` to each fused row; the fused route includes a final `semantic` leg after the BM25 and fuzzy-token legs. ### Response Every `Auto` response carries a `routing` block: ```json { "rows": [{"id": "ticket-4117", "$score": 0.0639, "title": "..."}], "routing": { "route": "hybrid_text", "policy": "v1", "tokens": 1, "executed": true }, "hybrid": {"tokens": ["timout"], "tokens_dropped": 0, "fuzziness": "auto", "rank_constant": 60, "legs": 2, "per_leg_limit": 50} } ``` | Field | Meaning | | --- | --- | | `route` | The strategy chosen (or forced). | | `policy` | Routing policy version that made the decision. `"forced"` when `route` was supplied. | | `tokens` | Token count the policy read, post tokenizer policy. | | `executed` | `false` on a deferral: the route needs a vector the request didn't supply. `rows` is empty; embed and re-issue with the route forced. | Routed queries follow the same semantics as their underlying strategy: one consistency cut across all legs, all-or-nothing leg failure, and a single [search history](/docs/api/search-history) entry carrying the `Auto` expression and the decision. When an inline `Embed` is resolved, its `embedding_tokens` and `embedding_ms` measurements are merged into the normal top-level `performance` object. ### Validation All return `422`: | Condition | Why | | --- | --- | | Forced `"semantic"` or `"fused"` without `vector` | Forcing asserts you have the vector; only auto-routing defers. | | Input yields zero tokens under the policy | Nothing to route. | | `vector` dimensionality mismatch | Same check as a plain vector query. | | `Auto` inside a `queries` array | Inherited from [hybrid text fusion](#validation). | ## Counting matches To count how many rows match a full-text or vector query, use [scan](/docs/api/scans) count mode with the `fts` or `ann` selector. Ranked counts share the single `/scans` endpoint with filter counts — `fts` is exact, `ann` is a radius scan flagged `approximate`, and both honor the `exhaustive` flag and the count deadline. ## Fetch Fetch is a Layer-only endpoint with no upstream equivalent. The document cache is checked first; on miss or error the gateway falls through to the backing store and backfills the cache best-effort. ### Single fetch ```python doc = await client.fetch_document( "products", "asin-B08N5WRWNW", include_attributes=["title", "category"], ) ``` ```go doc, err := client.FetchDocument(ctx, "products", "asin-B08N5WRWNW", &hevlayer.FetchDocumentParams{ IncludeAttributes: []string{"title", "category"}, }) ``` ```typescript const doc = await client.fetchDocument("products", "asin-B08N5WRWNW", { includeAttributes: ["title", "category"], }); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces/products/documents/asin-B08N5WRWNW?include_attributes=title,category" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` | Outcome | Status | Header | | --- | --- | --- | | Cached hit | 200 | `x-layer-cache: hit` | | Cache miss, upstream hit, cache backfilled | 200 | `x-layer-cache: miss` | | Cache unavailable, upstream hit | 200 | `x-layer-cache: miss-on-error` | | Missing from both layers | 404 | — | ### Batch fetch ```python batch = await client.fetch_documents("products", { "ids": ["asin-1", "asin-2", "asin-3"], "include_attributes": ["title"], }) ``` ```go batch, err := client.FetchDocuments(ctx, "products", &hevlayer.FetchDocumentsRequest{ Ids: []string{"asin-1", "asin-2", "asin-3"}, IncludeAttributes: []string{"title"}, }) ``` ```typescript const batch = await client.fetchDocuments("products", { ids: ["asin-1", "asin-2", "asin-3"], include_attributes: ["title"], }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/documents" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "ids": ["asin-1", "asin-2", "asin-3"], "include_attributes": ["title"] }' ``` ```json { "documents": [ {"id": "asin-1", "attributes": {"title": "..."}}, {"id": "asin-3", "attributes": {"title": "..."}} ], "missing": ["asin-2"] } ``` Batch fetch returns found documents and missing ids inline instead of a partial 404. `documents` preserves request order; ids the gateway could not find anywhere land in `missing`. Because order is preserved, batch fetch is a convenient way to reassemble a [pipeline](/docs/api/pipelines)'s chunks back into their original document — request the chunk ids in sequence and concatenate the results. ### Behavior matrix | Cache state | Single fetch | Batch fetch | | --- | --- | --- | | Hit | cache | cache | | Miss, upstream present | upstream + backfill | upstream + backfill | | Miss, upstream absent | 404 | inline `missing` | | Cache unavailable | upstream, `miss-on-error` | upstream, `miss-on-error` | --- # Federated query Source: https://hevlayer.com/docs/api/federated-query import CodeTabs from "../../../components/docs/CodeTabs.astro"; A **federated query** runs one query across a **set** of namespaces. `POST /v2/query` is namespace-less: the per-namespace [query endpoint](/docs/api/query) names its namespace in the path, while this endpoint takes the set in the body, so the namespace is no longer a path parameter. It has no upstream equivalent — turbopuffer's multi-query is single-namespace, so the fan-out and the merge are a Layer composition over per-namespace reads. One ranking is spread across every namespace in the set and merged into a single ranked list — "search my whole feed / my whole library." For several **independent** rankings instead, query each namespace separately; a federated query always returns one fused list. ## Fan-out and fuse Supply one ranking and a `namespaces` set. The gateway runs the ranking against each namespace and returns one merged `rows` list, each row tagged with the namespace it came from. ```python response = await client.query({ "namespaces": ["moment-pod-changelog", "moment-pod-latent-space", "moment-pod-no-priors"], "rank_by": ["text", "Auto", "evaluating RAG systems"], "top_k": 12, "filters": ["published_at", "Gte", 1740000000], "include_attributes": ["text", "show", "source_url", "start_sec"], }) ``` ```go response, err := client.Query(ctx, &hevlayer.FederatedQueryRequest{ Namespaces: []string{"moment-pod-changelog", "moment-pod-latent-space", "moment-pod-no-priors"}, RankBy: []any{"text", "Auto", "evaluating RAG systems"}, TopK: 12, Filters: []any{"published_at", "Gte", 1740000000}, IncludeAttributes: []string{"text", "show", "source_url", "start_sec"}, }) ``` ```typescript const response = await client.query({ namespaces: ["moment-pod-changelog", "moment-pod-latent-space", "moment-pod-no-priors"], rank_by: ["text", "Auto", "evaluating RAG systems"], top_k: 12, filters: ["published_at", "Gte", 1740000000], include_attributes: ["text", "show", "source_url", "start_sec"], }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/query" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "namespaces": ["moment-pod-changelog", "moment-pod-latent-space", "moment-pod-no-priors"], "rank_by": ["text", "Auto", "evaluating RAG systems"], "top_k": 12, "filters": ["published_at", "Gte", 1740000000], "include_attributes": ["text", "show", "source_url", "start_sec"] }' ``` `rank_by` accepts the same vocabulary as a single-namespace query, including the Layer [`HybridText`](/docs/api/query#hybrid-text-fusion) and [`Auto`](/docs/api/query#query-routing) expressions. The query text is identical across the fan-out, so `Auto` routes **once** and the chosen route runs against every namespace — a single `routing` block is echoed. `filters` apply to every namespace. Name the explicit set to search, or pass `namespaces: ["*"]` to expand the authenticated key's namespace allowlist at request time. Omitting `namespaces` is equivalent to `["*"]`. ### Response One fused list. Each row carries `$namespace` (its origin) and `$rank` (its position within that namespace's results — the key the merge orders on). A `merge` block names the strategy, and a `namespaces` block reports each namespace's freshness and how many rows it returned to the merge. ```json { "rows": [ { "id": "ep123#7", "$namespace": "moment-pod-latent-space", "$rank": 1, "$score": 11.4, "show": "Latent Space", "text": "..." } ], "merge": { "method": "rank-interleave", "route": "fused" }, "routing": { "route": "fused", "policy": "v1", "tokens": 3, "executed": true }, "hybrid": { "tokens": ["evaluating", "rag", "systems"], "tokens_dropped": 0, "fuzziness": "auto", "rank_constant": 60, "legs": 4, "per_leg_limit": 60 }, "namespaces": [ { "namespace": "moment-pod-latent-space", "stable_as_of": 1747300000123, "matched": 14 }, { "namespace": "moment-pod-changelog", "stable_as_of": 1747299999001, "matched": 9 }, { "namespace": "moment-pod-no-priors", "stable_as_of": 1747300000050, "matched": 0 } ] } ``` | Field | Meaning | | --- | --- | | `$namespace` | The namespace this row came from. | | `$rank` | The row's 1-based rank **within its namespace**. The merge orders on this on a text route; see [Merge](#merge). | | `$score` / `$dist` | The row's native score within its namespace, carried as provenance. Comparable **within** a namespace, not across the fused list — do not threshold on it. On a vector route `$dist` is the merged ordering key and *is* globally comparable. | | `merge` | The merge applied. `method` is `"distance"` on a vector route, `"rank-interleave"` on a text route; `route` is currently always `"fused"` (reserved for future route-specific provenance). | | `namespaces` | Per-namespace echo: each reached namespace's `stable_as_of` watermark and `matched` — the rows it **returned to the merge** (up to [`per_namespace_limit`](#fusion-options)), not its surviving share of the final `top_k`. A namespace that matched nothing reports `matched: 0`. | `routing` and `hybrid` echo exactly as they do for a single-namespace [`Auto`](/docs/api/query#query-routing) / [`HybridText`](/docs/api/query#response) query, since the route runs once for the whole fan-out. ### Fusion options | Option | Default | Meaning | | --- | --- | --- | | `fusion.per_namespace_limit` | `clamp(2 × top_k, 10, 100)` | How many rows each namespace returns to the merge. Shallower than the single-namespace `per_leg_limit`: across a wide fan-out most namespaces contribute nothing to the final `top_k`, so deep per-namespace retrieval is wasted. Integer > 0. | | `fusion.rank_constant` | `60` | Reserved for route-dependent fusion tuning. It is accepted for forward compatibility and is inert in the current gateway. | ## Entitlements The `namespaces` set uses the same store-derived authentication path as individual namespace queries: the upstream key must be able to read each listed namespace. A namespace read failure is reported under [partial failure](#partial-failure) unless `strict` is enabled. For minted scoped keys, explicitly named namespaces must be inside a `vectorstore.` read grant. Naming a namespace outside the grant is a hard `403`, not a partial result. `namespaces: ["*"]` expands by listing upstream namespaces and filtering them through the key's vectorstore namespace globs, so the key is the feed/library set and the client does not enumerate it. ## Filters One `filters` expression is applied to **every** namespace in the set, and each namespace evaluates it independently against its own schema. So a filter only behaves uniformly when the attributes it references are part of a **shared filterable schema** across the set — present in every namespace, with the same type, and declared filterable. This is the filter analog of the [shared embedding space](#vector-merge-requires-a-matching-embedding-space) a vector merge needs: a federated query is only as coherent as the contract its namespaces share. A namespace that cannot evaluate the filter — the attribute is absent, has an incompatible type, or is not filterable there — is reported as `filter_schema_mismatch`, not as `matched: 0`. This is a per-namespace validity failure: the namespace did not participate in the filtered ranking because it could not evaluate the predicate. In default best-effort mode, the namespace is omitted from `rows`, listed in `errors`, and the response carries `x-layer-partial: true`. With `strict: true`, the whole request fails with `422`. This docs contract selects `422` for the strict-mode failure because the namespace set and filter expression are valid only if every required namespace can evaluate the filter. A feed or library assembled from namespaces indexed by one pipeline shares a filterable schema by construction, so this is invisible in the common case. The contract bites when a set spans heterogeneously-indexed namespaces — keep a filter to attributes the whole set declares, or scope the set to namespaces that share them. ## Merge A federated query merges by the quantity the route makes comparable across namespaces: - **Vector route** (`ANN` over a query vector) — distances are comparable across namespaces that share an embedding space, so the gateway merges by `$dist` and the fused order is the exact global nearest-neighbor ranking. `merge.method` is `"distance"`. - **Text route** (`BM25`, `HybridText`, or a routed `fused`/`hybrid_text`) — BM25 and hybrid scores are **not** comparable across corpora (different term statistics; a hybrid `$score` is already a per-namespace fusion). The gateway merges by **rank-interleave**: rows are ordered by their `$rank` within their namespace, ties broken by `id`. `merge.method` is `"rank-interleave"`. No single pod dominates the head of the list; each contributes its best matches in rank order. The fused list exposes `$rank` as the ordering key and carries each row's native `$score` only as provenance. `top_k` truncates the merged list. Each namespace returns [`fusion.per_namespace_limit`](#fusion-options) rows to the merge. ### Vector merge requires a matching embedding space Merging by distance is only meaningful when every namespace in the set embeds into the same geometry. Declare each namespace's embedding identity with [`spec.embedding`](/docs/kubernetes/index-crd#embedding) on its `Index` — model, output dimension, and normalization, alongside the existing `spec.backend.distanceMetric`. The gateway compares those profiles before a vector fan-out uses distance merge: - All reached namespaces have the same profile → `merge.method: "distance"`. - A profile is missing or differs → best-effort responses downgrade to `merge.method: "rank-interleave"` and include `merge.downgraded_reason` (`"missing_embedding_profile"` or `"embedding_profile_mismatch"`). - With `strict: true`, a missing or mismatched profile is a `422`. A text fan-out is rank-interleave regardless, so embedding profiles do not affect it. ## Consistency Each namespace has its own [stable-read](/docs/api/query#stable-reads) watermark; there is no single consistent cut across independent namespaces. The `namespaces` block reports each namespace's `stable_as_of`, and the response header `x-layer-stable-as-of` carries the **minimum** across the reached namespaces — the most conservative answer to "as of when." Layer does not manufacture a global watermark. ## Partial failure Across many namespaces, one may be deleted, time out, error upstream, or fail to evaluate the [filter](#filters). Unlike single-namespace [multi-query](/docs/api/query#multi-query), the fan-out defaults to **best-effort**: it returns the namespaces that succeeded, lists the rest in an `errors` block, and sets `x-layer-partial: true`. A feed search does not blank because one pod is briefly unavailable. ```json { "rows": [ "..." ], "errors": [ { "namespace": "moment-pod-no-priors", "error": "Upstream error: namespace not found" } ] } ``` Availability failures carry a human-readable upstream message in `error`, not a stable machine code — match on `namespace`, not on the `error` string. Filter schema mismatches are the exception: they carry the stable `code: "filter_schema_mismatch"` plus a human-readable `detail` so clients can separate "namespace unavailable" from "namespace cannot evaluate this filter." ```json { "rows": [ { "id": "ep123#7", "$namespace": "moment-pod-latent-space", "$rank": 1 } ], "errors": [ { "namespace": "moment-pod-no-priors", "code": "filter_schema_mismatch", "error": "filter schema mismatch", "detail": "filter attribute published_at is absent, not filterable, or has an incompatible type" } ] } ``` Set `"strict": true` to opt into fail-fast: any namespace error fails the whole request. Reach for it when a missing namespace would make the result misleading rather than merely thinner. ## Limits | Limit | Value | | --- | --- | | Namespaces per request | 512. Over the cap → `422` naming the excess. | | Pagination | Not supported. `cursor` is rejected — a fused cursor across independent namespaces does not form the monotone bands pagination relies on. | ## Validation | Condition | Status | | --- | --- | | A named namespace is outside the minted key's namespace grant | 403 | | A namespace read fails upstream | 200 with `errors`, or 502 with `strict: true` | | A namespace cannot evaluate `filters` (attribute absent, wrong type, or not filterable) | 200 with `errors[].code: "filter_schema_mismatch"` and `x-layer-partial: true`, or 422 with `strict: true` | | `namespaces` is empty | 422 | | `namespaces: ["*"]` mixed with other names | 422 | | `namespaces` set exceeds the cap | 422 | | Vector route missing/mismatched embedding profiles with `strict: true` | 422 | | `cursor` present | 422 | | `rank_by` expression invalid | 422 (same checks as a single-namespace query) | --- # Agentic search Source: https://hevlayer.com/docs/api/agents import CodeTabs from "../../../components/docs/CodeTabs.astro"; **Agentic search** runs a configured reasoning loop over one or more namespaces and returns the same row shape as every other search endpoint. A model reads the query, fans out diverse phrasings in parallel via layer's [scatter/gather](/docs/concepts#scattergather), ranks the candidates for relevance, and returns a ranking fused from both signals. It is a better-ranked result set, not a generated answer — the response is the [federated query](/docs/api/federated-query#response) shape, so any client that reads `/v2/query` reads this with no changes. The endpoint names a configured [`Agent`](/docs/kubernetes/agent-crd) in the path. The model, the turn budget, the indices, and the output shaping are bound on that resource, so the request body is just a query, an optional query embedding, and a result count. ``` POST /v2/agents/{name}/query ``` ## Request ```python response = await client.agent("support-search").query({ "query": "auth errors after the june upgrade", "top_k": 20, }) ``` ```go response, err := client.Agent("support-search").Query(ctx, &hevlayer.AgentQueryRequest{ Query: "auth errors after the june upgrade", TopK: 20, }) ``` ```typescript const response = await client.agent("support-search").query({ query: "auth errors after the june upgrade", top_k: 20, }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/agents/support-search/query" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "auth errors after the june upgrade", "top_k": 20 }' ``` | Field | Purpose | | --- | --- | | `query` | The natural-language query. The agent reformulates it; you do not pre-shape it into a route expression. | | `vector` | Optional. The embedding of `query`, used for the agent's semantic recall leg. See [Bring your own embedding](#bring-your-own-embedding). | | `top_k` | Rows to return after fusion. | The model, fan-out, fusion weighting, and output are bound on the [`Agent`](/docs/kubernetes/agent-crd), which keeps the request trivial and makes the agent the single source of truth for what a call costs and what it can read. `query` and `vector` are the only request inputs, and they are data, not config — there are no per-request overrides of the agent's configured behavior. ### Bring your own embedding The agent fans out for recall over both routes: a lexical leg on your query text and a semantic leg on a query vector. Layer never embeds query text — you supply the vector, the same bring-your-own-embedding contract as [`/v2/query`](/docs/api/query). Pass it as `vector`: ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/agents/support-search/query" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "auth errors after the june upgrade", "vector": [0.0123, -0.0456, 0.0789], "top_k": 20 }' ``` `vector` is the embedding of `query`. The agent uses this one vector for every planned semantic leg — it does not embed the reformulated phrasings, so the vector carries the query's semantic intent while the text reformulations broaden lexical recall. Embed `query` with the same model your indices were embedded with, and match their dimensionality. Omit `vector` and the agent's semantic legs fall back to the lexical route: recall runs on the reformulations alone, which is the right behavior when an index has no vector column or you have no embedder on the client. ## Response The response is the [federated query](/docs/api/federated-query#response) shape: `rows` each carrying `$namespace`, `$rank`, and a native `$score`/`$dist`, plus a `merge` block and a `namespaces` block. The merge names the dual-score fusion. ```json { "rows": [ { "id": "T-4821", "$namespace": "tickets", "$rank": 1, "$score": 9.7, "subject": "SSO login fails after upgrade" } ], "merge": { "method": "weighted-rrf", "route": "dual-score" }, "namespaces": [ { "namespace": "tickets", "stable_as_of": 1747300000123, "matched": 20 } ] } ``` By default the response is byte-shape-identical to a [federated query](/docs/api/federated-query): a client cannot tell whether a reasoning loop produced it. Set [`output.provenance`](/docs/kubernetes/agent-crd#output) on the agent to surface the scores. ### Provenance and trace With provenance on, the response gains an `agent` echo and each row carries a `$agent` field with both scores: ```json { "rows": [ { "id": "T-4821", "$namespace": "tickets", "$rank": 1, "$score": 9.7, "$agent": { "retrievalScore": 3, "relevanceScore": 0.92, "query": "authentication failure post-upgrade", "queryIndex": 0 } } ], "merge": { "method": "weighted-rrf", "route": "dual-score" }, "namespaces": [ { "namespace": "tickets", "stable_as_of": 1747300000123, "matched": 20 } ], "agent": { "turns": 2, "deadlineHit": false, "recallDepth": 50, "relevanceWeight": 0.6, "queries": [ { "namespaces": ["tickets"], "rankBy": "hybridText", "query": "authentication failure post-upgrade", "filters": { "created_at": { "$gte": "2026-06-01" } } } ] } } ``` | Field | Meaning | | --- | --- | | `$agent.retrievalScore` | The row's rank within the leg that first surfaced it — the per-query position (1-based; lower is better), not the fused-pool rank. With `fanout` > 1 a row can appear in several legs; this records the first leg's rank. The fused recall signal is computed separately (RRF over every leg the row appeared in). | | `$agent.relevanceScore` | The model's graded relevance for the row (the precision signal). | | `$agent.query` | The planned variant that first surfaced the row. | | `$agent.queryIndex` | Zero-based index of that planned variant in `agent.queries`. | | `agent.turns` | Model turns the call spent. | | `agent.deadlineHit` | True when the deadline ended the request early and returned the best ranking so far. | | `agent.queries` | The planned variants: route, reformulated text, and inferred filters. | With [`output.trace`](/docs/kubernetes/agent-crd#output) the `agent` echo also carries the full reasoning trace. The trace is written to the [search-history](/docs/api/search-history) record whether or not it is echoed, so agentic and plain searches share one surface for evaluation. ## Auth Auth follows the same model as the other API endpoints; multi-namespace queries follow [federated query](/docs/api/federated-query#entitlements) auth behavior. A minted key additionally needs an `agent.` entitlement on its [ApiKey](/docs/kubernetes/apikey-crd#entitlements) to invoke the agent. ## Configuration Everything the request omits is bound on the [`Agent`](/docs/kubernetes/agent-crd) resource: the model and its credential, the deadline, the indices, the fan-out and fusion weighting, and the output shaping. `kubectl get agent -o yaml` and `client.agent("support-search").apply()` are two spellings of the same object. ## Validation | Condition | Status | | --- | --- | | `{name}` is not a known, `Ready` agent | 404 | | The minted key lacks the `agent.` entitlement | 403 | | A bound index is outside the key's namespace grant | 403 | | `query` is empty | 422 | | `vector` is present and its dimensionality does not match the bound indices' vector schema | 422 | | Deadline hit with `onDeadline: bestEffort` | 200, best ranking so far, `agent.deadlineHit: true` | | Deadline hit with `onDeadline: error` | 504 | | The provider is unreachable on both primary and fallback | 502 | --- # Scan Source: https://hevlayer.com/docs/api/scans import CodeTabs from "../../../components/docs/CodeTabs.astro"; A scan is on-demand row selection over a namespace. It picks rows by one of four **selectors** and returns their IDs (`mode: ids`, an asynchronous job), their count (`mode: count`, synchronous), or the distinct values of one attribute field (`mode: values`, an asynchronous job): | Input | Field | Meaning | Notes | | --- | --- | --- | --- | | Filter selector | `filters` | An attribute predicate, or all rows when omitted. | Exact | | Full-text selector | `fts` | A BM25 predicate against a text field. | Exact | | Hybrid-text selector | `hybrid_text` | The BM25 leg, the per-token fuzzy legs, and the per-token surfacing legs — a superset of the `hybrid_text` query route (see [Hybrid text count](#hybrid-text-count)). | Exact | | Radius selector | `ann` | Rows within `radius` of a query vector. | Approximate (ANN recall) | | Fan-out control | `threads` | Maximum concurrent upstream requests for origin scatter/gather. | Origin only; defaults from `Index.spec.scan.threads`, then `8`. | Origin scatter/gather is enabled only for namespaces whose shard backfill is complete. For adopted namespaces initialized through `POST /v2/namespaces/{ns}/init`, scans stay on the single-namespace origin path while `layer.shard_lag_rows` is greater than `0`; this keeps count, ID, and values scans from missing rows that have not yet been stamped with `_hevlayer_shard`. A request carries **at most one** ranked selector (`fts`, `hybrid_text`, or `ann`). `filters` is always optional and, when present alongside a ranked selector, is ANDed onto the match set as an extra constraint. A request with more than one ranked selector is a `422`. At cutover, `mode: ids` is filter-only (ranked IDs are a defined fast-follow), while `mode: count` and `mode: values` support all four selectors. Use scans for bulk exports, manual inspection, UDF discovery debugging, cache/origin consistency checks, exact or approximate counts, and field value discovery. ## Routes | Route | Method | Behavior | | --- | --- | --- | | `POST /v2/namespaces/{ns}/scans` | POST | Create an ID or values scan job, or return a count. | | `GET /v2/namespaces/{ns}/scans` | GET | List scan jobs for the namespace. | | `GET /v2/namespaces/{ns}/scans/{id}` | GET | Read one scan job. | | `GET /v2/namespaces/{ns}/scans/{id}/results` | GET | Read completed scan IDs or values. | | `DELETE /v2/namespaces/{ns}/scans/{id}` | DELETE | Drop the in-memory scan job. | ## ID Mode ```python job = await client.create_scan("products", { "source": "auto", "mode": "ids", "filters": ["category", "Eq", "Electronics"], "threads": 8, "page_size": 1000, }) ``` ```go job, err := client.CreateScan(ctx, "products", &hevlayer.CreateScanRequest{ Source: "auto", Mode: "ids", Filters: []interface{}{"category", "Eq", "Electronics"}, Threads: 8, PageSize: 1000, }) ``` ```typescript const job = await client.createScan("products", { source: "auto", mode: "ids", filters: ["category", "Eq", "Electronics"], threads: 8, page_size: 1000, }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/scans" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "source": "auto", "mode": "ids", "filters": ["category", "Eq", "Electronics"], "threads": 8, "page_size": 1000 }' ``` `mode` defaults to `ids`. Valid ID-mode sources are `auto`, `cache`, and `origin`. The Python and TypeScript clients also ship `scan(...)` helpers that create the job and poll until it completes; in Go, poll `GetScan` until `status` is `completed`. The create response is `202 Accepted`: ```json { "id": "scan-uuid", "namespace": "products", "source": "auto", "effective_source": "origin", "status": "running", "progress": 0, "documents_scanned": 0, "threads": 8, "created_at": "2026-05-26T10:00:00Z" } ``` Read IDs after `status` is `completed`: ```python results = await client.get_scan_results("products", job.id, limit=1000, offset=0) ``` ```go results, err := client.GetScanResults(ctx, "products", scanID, &hevlayer.GetScanResultsParams{Limit: 1000, Offset: 0}) ``` ```typescript const results = await client.getScanResults("products", job.id, { limit: 1000, offset: 0, }); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces/products/scans/scan-uuid/results?limit=1000&offset=0" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` ```json { "ids": ["doc-1", "doc-2"], "total": 2 } ``` ## Count Mode ```python count = await client.create_scan("products", { "mode": "count", "source": "auto", "filters": ["category", "Eq", "Electronics"], "threads": 8, "timeout_seconds": 30, }) ``` ```go count, err := client.CreateScan(ctx, "products", &hevlayer.CreateScanRequest{ Mode: "count", Source: "auto", Filters: []interface{}{"category", "Eq", "Electronics"}, Threads: 8, TimeoutSeconds: 30, }) ``` ```typescript const count = await client.createScan("products", { mode: "count", source: "auto", filters: ["category", "Eq", "Electronics"], threads: 8, timeout_seconds: 30, }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/scans" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mode": "count", "source": "auto", "filters": ["category", "Eq", "Electronics"], "threads": 8, "timeout_seconds": 30 }' ``` ```json { "count": 4210, "served_by": "snapshot", "snapshot_sha": "3f9e8b21", "watermark_ms": 1747300000123, "elapsed_ms": 3 } ``` When `watermark_ms` is present, the response also includes `x-layer-stable-as-of` with the same epoch-ms value. Count-mode sources are `auto`, `snapshot`, `cache`, and `origin`. Snapshot reads are eligible only for a single leaf `Eq` or `In` filter on a field present in the latest snapshot `fields[]`. `And`, `Or`, `Not`, range operators, fields absent from the snapshot, and skipped fields fall through under `auto` and fail with `412 precondition_failed` under `source: snapshot`. All scan modes accept the same temporal selectors as query: `as_of` conjoins `_hevlayer_upserted_at <= as_of`; `between: [lo, hi]` conjoins `lo < _hevlayer_upserted_at <= hi`. The temporal predicate is ANDed with `filters` and with any ranked selector (`fts`, `hybrid_text`, or `ann`). Snapshot-served scans cannot evaluate temporal windows from a pre-aggregated body, so `source: auto` falls through to cache/origin when a temporal selector is present and `source: snapshot` fails with `412 precondition_failed`. Live count responses include: ```json { "count": 4210, "served_by": "origin", "bounded": false, "timed_out": false, "shards_saturated": 0, "shards_total": 1, "threads": 1, "elapsed_ms": 42 } ``` ## Values Mode A values scan enumerates the distinct values of one attribute `field` over the rows the selector picks, each with its document count. Use it to discover a field's value set — what product categories exist, what tags appear on rows matching a query — instead of confirming values you already know with counts. `field` is required for `mode: values` (and rejected on other modes with `422`). It must name a scalar string or integer attribute, or an array of strings — each array element counts once per containing document. Vector fields are a `422`. ```python job = await client.create_scan("products", { "mode": "values", "field": "category", "source": "auto", "filters": ["in_stock", "Eq", True], }) ``` ```go job, err := client.CreateScan(ctx, "products", &hevlayer.CreateScanRequest{ Mode: "values", Field: "category", Source: "auto", Filters: []interface{}{"in_stock", "Eq", true}, }) ``` ```typescript const job = await client.createScan("products", { mode: "values", field: "category", source: "auto", filters: ["in_stock", "Eq", true], }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/scans" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mode": "values", "field": "category", "source": "auto", "filters": ["in_stock", "Eq", true] }' ``` Like ID mode, the create response is a `202 Accepted` job, and the `scan(...)` SDK helpers poll it to completion: ```json { "id": "scan-uuid", "namespace": "products", "mode": "values", "field": "category", "source": "auto", "effective_source": "origin", "status": "running", "progress": 0, "documents_scanned": 0, "threads": 8, "created_at": "2026-05-26T10:00:00Z" } ``` Read values from the same results route after `status` is `completed`, with the same `limit`/`offset` pagination as scan IDs: ```json { "values": [ {"v": "electronics", "n": 4210}, {"v": "books", "n": 1240} ], "total": 2, "truncated": false } ``` `v`/`n` is the same vocabulary [snapshot](/docs/api/snapshots) facet histograms use: `v` is the value, `n` its document count. Ordering is deterministic — `n` descending, then `v` ascending. Counts are exact for filter-selector scans; on a ranked scan with a saturated shard the job carries `bounded: true` and each `n` is a `>=` lower bound. ### Precomputed serving An unfiltered values scan (no `filters`, no ranked selector) on a field present in the latest snapshot `fields[]` is answered straight from the snapshot's facet histogram: the job completes during the create call — the `202` body already shows `status: completed` — and carries `effective_source: snapshot` with `snapshot_sha` and `watermark_ms`. Fields in `fields_skipped[]` or absent from the snapshot fall through to cache/origin under `auto` and fail with `412 precondition_failed` under explicit `source: snapshot`, as do scans carrying any selector. ### High cardinality Snapshot facet histograms cap each field at 10,000 distinct values and skip fields beyond it; values scans are the enumeration path for exactly those fields. A values job accumulates its histogram in gateway memory and caps the listing at **1,000,000 distinct values**. A scan that crosses the cap completes rather than failing: - The cap applies after the full pass, so every emitted `n` stays exact. - The listing truncates deterministically to the top 1,000,000 values by count (value-ascending tiebreak); the low-count tail is dropped. - The job and its results carry `truncated: true`, meaning the listing is incomplete. `truncated`, `bounded`, and `approximate` are independent flags: `truncated` is a gateway memory bound on the listing, `bounded` is upstream `top_k` saturation on a ranked scan's counts, and `approximate` is ANN recall fuzz on a radius ball's membership. ## Fan-out width Origin scans fan out one upstream request per active shard. `threads` sets the maximum number of those upstream requests a single scan may have in flight at once. It means concurrent requests, not operating-system threads; the gateway is async. Resolution order: 1. `threads` on the scan request. 2. `spec.scan.threads` on the namespace's `Index` resource. 3. The gateway default, `8`. The effective value is clamped to the active shard count and the server cap, `32`, then echoed as `threads` on origin responses and completed scan jobs. Snapshot and cache reads do not fan out, so they ignore this field and omit the echo. ## Full-text count Count rows matching a BM25 query with the `fts` selector. Full-text counts are exact and always run origin scatter/gather, so `source` must be omitted, `auto`, or `origin`. A `filters` array, when present, is ANDed on as an extra constraint. ```python count = await client.create_scan("products", { "mode": "count", "fts": {"field": "title", "query": "wireless headphones"}, "filters": ["category", "Eq", "Electronics"], "exhaustive": True, }) ``` ```go count, err := client.CreateScan(ctx, "products", &hevlayer.CreateScanRequest{ Mode: "count", Fts: &hevlayer.FtsScan{Field: "title", Query: "wireless headphones"}, Filters: []interface{}{"category", "Eq", "Electronics"}, Exhaustive: true, }) ``` ```typescript const count = await client.createScan("products", { mode: "count", fts: { field: "title", query: "wireless headphones" }, filters: ["category", "Eq", "Electronics"], exhaustive: true, }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/scans" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mode": "count", "fts": {"field": "title", "query": "wireless headphones"}, "filters": ["category", "Eq", "Electronics"], "exhaustive": true }' ``` ## Hybrid text count Count rows in the keyword/fuzzy neighborhood of a `HybridText` query with the `hybrid_text` selector. The scan tokenizes `query` with the HybridText policy, then evaluates the BM25 leg, one fuzzy leg per token, **and** one surfacing leg per token (the RFC 0057 empty-result fallback's legs), and counts the de-duplicated union of returned row ids. This count is a **superset** of the `hybrid_text` query route's deduped rows: the scan always includes the surfacing legs, whereas the query route only adds them when its primary legs (BM25 + fuzzy) return nothing. On a partial-typo query whose primary legs do match, the scan can therefore count more rows than the route returns. Use this selector for a generous live count next to `hybrid_text` or `auto` results that routed to `hybrid_text`; plain `fts` counts exact BM25 only. ```python count = await client.create_scan("products", { "mode": "count", "hybrid_text": {"field": "title", "query": "wireles headphones"}, "filters": ["category", "Eq", "Electronics"], }) ``` ```go count, err := client.CreateScan(ctx, "products", &hevlayer.CreateScanRequest{ Mode: "count", HybridText: &hevlayer.HybridTextScan{Field: "title", Query: "wireles headphones"}, Filters: []interface{}{"category", "Eq", "Electronics"}, }) ``` ```typescript const count = await client.createScan("products", { mode: "count", hybrid_text: { field: "title", query: "wireles headphones" }, filters: ["category", "Eq", "Electronics"], }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/scans" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mode": "count", "hybrid_text": {"field": "title", "query": "wireles headphones"}, "filters": ["category", "Eq", "Electronics"] }' ``` ## Radius count Count rows within `radius` of a query vector with the `ann` selector — a distance-ball scan. `radius` is required and finite (without an upper bound every row is in the ball); `field` defaults to `vector`. Like `fts`, radius counts always run origin scatter/gather. The count is **approximate**: ANN recall means the index's membership of the ball may differ from the true set, independent of saturation, so the response carries `approximate: true`. The radius bound is applied by the gateway to the `$dist` returned by the ranked query. It is not sent upstream as a filter. ```python count = await client.create_scan("products", { "mode": "count", "ann": {"field": "vector", "vector": [0.12, -0.3, 0.88], "radius": 0.25}, }) ``` ```go count, err := client.CreateScan(ctx, "products", &hevlayer.CreateScanRequest{ Mode: "count", Ann: &hevlayer.AnnScan{Field: "vector", Vector: []float64{0.12, -0.3, 0.88}, Radius: 0.25}, }) ``` ```typescript const count = await client.createScan("products", { mode: "count", ann: { field: "vector", vector: [0.12, -0.3, 0.88], radius: 0.25 }, }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/scans" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mode": "count", "ann": {"field": "vector", "vector": [0.12, -0.3, 0.88], "radius": 0.25} }' ``` ```json { "count": 980, "served_by": "origin", "approximate": true, "bounded": false, "timed_out": false, "shards_saturated": 0, "shards_total": 1, "threads": 1, "elapsed_ms": 51 } ``` ### Bounding ranked scans Ranked selectors fan out one turbopuffer query per shard, each capped at `top_k = 10_000`. `threads` bounds fan-out width: how many shard requests can run at once. `exhaustive` and `timeout_seconds` bound depth: what happens when a shard hits that cap and how long recursion can run. - `exhaustive: false` (default) — one scatter/gather. A saturated shard contributes its cap as a lower bound; the response carries `bounded: true` with `shards_saturated > 0`. - `exhaustive: true` — for BM25, recurse on each saturated shard via score-band pagination (`$score < last` with an `id` tiebreak) until every page is short or `timeout_seconds` elapses. ANN radius scans do not push `$dist` filters upstream; the gateway counts returned rows whose `$dist <= radius` and marks the shard exhausted when the first over-radius row appears. If the full page is still inside the radius, the shard remains `bounded`. The same `threads` value applies to the initial round and every exhaustive round over the remaining saturated shards. `bounded` and `approximate` are independent. `bounded` means a shard saturated and the count is a `>=` lower bound for the rows the index returned; `approximate` means the distance ball's membership is itself fuzzy. An `ann` count can be `bounded: false` yet still `approximate: true`. ## Sources | Source | ID mode | Count mode | Values mode | | --- | --- | --- | --- | | `auto` | Cache when fresh enough, otherwise origin | Snapshot first, then cache/origin. | Snapshot when eligible, then cache/origin. | | `snapshot` | Not supported | Latest snapshot only; requires eligible `Eq` or `In`. | Latest snapshot facet listing; requires an unfiltered scan on a field in `fields[]`. | | `cache` | Aerospike document cache only | Aerospike document cache only | Aerospike document cache only. | | `origin` | turbopuffer paginated scan | turbopuffer paginated scan | turbopuffer paginated scan with gateway-side dedupe. | This table covers the filter selector. The `fts`, `hybrid_text`, and `ann` selectors have no snapshot or cache evaluator, so they always run origin scatter/gather: omitted, `auto`, and `origin` all resolve to origin, and `snapshot` or `cache` returns `422`. ## Filters Scans accept the same turbopuffer filter array as [query](/docs/api/query). On origin scans, the filter is pushed to turbopuffer. On cache scans, the gateway evaluates it against cached document attributes. Supported cache operators are `Eq`, `NotEq`, `Gt`, `Gte`, `Lt`, `Lte`, `In`, `NotIn`, `And`, `Or`, and `Not`. If `auto` sees a filter the cache cannot evaluate, it uses origin. Explicit `source: cache` with an unsupported filter fails rather than returning partial results. ## Auto-Mode Policy Auto ties cache freshness to the same consistency watermark used by [stable reads](/docs/api/query#stable-reads). The gateway tracks per-namespace `cache_warmed_through`, the watermark observed at the end of the last successful origin warm. | Cache state | Watermark state | Action | | --- | --- | --- | | Empty | any | Run origin and stamp `cache_warmed_through`. | | Populated, `cache_warmed_through >= watermark` | observed | Serve cache. | | Populated, `cache_warmed_through < watermark` | observed | Serve cache and start a background origin warm. | | Populated, no `cache_warmed_through` yet | observed | Serve cache and start a background origin warm. | | Populated | not yet observed | Serve cache. | When cache is used, `_hevlayer_upserted_at <= cache_warmed_through` is added before the user filter so the scan is a stable warmed view. ## Operational notes - ID and values scan state is in-memory and ephemeral; it resets on gateway restart. - Count scans have a deadline, default 30s and maximum 300s. - Values jobs cap at 1,000,000 distinct values per scan and set `truncated: true` when crossed; the listing keeps the top values by count, each with an exact count. - Origin scan fan-out defaults to 8 concurrent upstream requests per scan unless the request or `Index.spec.scan.threads` sets a different value. - Snapshot-served count scans are exact at the snapshot `watermark_ms`. --- # Pipelines Source: https://hevlayer.com/docs/api/pipelines import CodeTabs from "../../../components/docs/CodeTabs.astro"; The pipeline API keeps the code you need to index data simple and organized. A typical pipeline has two stages: extraction and chunking on CPU, followed by embedding on GPU. This guide walks through a best-practice layout for that pipeline; the concepts expand to N stages. ## Document lifecycle ``` put chunks put vectors (new doc) ──────────► pending ──────────────► indexed ▲ │ re-stage (idempotent) ``` - **pending** — chunks stored, waiting for embedding. - **indexed** — vectors written to the namespace's configured VectorStore. `embedding` is a claim stage: documents sit in it only while leased to a worker, and recover to `pending` when a lease expires. Re-staging a document resets it to `pending` with new chunks, which is how you reprocess after source data changes. ## File tree ``` indexer/ ├── pipelines/ │ ├── extract-chunk.yaml # CPU stage — Pipeline resource │ └── embed.yaml # GPU stage — Pipeline resource ├── extract_chunk.py # read the source, stage chunks ├── embed.py # claim pending docs, write vectors └── app.py # REST API: trigger a run, wait for completion ``` The two YAML files declare the worker images, pools, and scaling — see the [Pipeline CRD](/docs/kubernetes/pipeline-crd) for the fields. Both set `pipelineId: products` so the two workers share one queue. The rest of this page is the worker code — shown in Python and Go; every call is also a plain REST endpoint (see [Write & Stage](/docs/api/write)). ## Extract and chunk The CPU worker reads the source, splits text into chunks, and stages them. Staging chunks stores them durably (S3, cached in the document cache) and marks the document `pending`. The worker hardcodes nothing: the operator injects the pipeline id, the gateway URL, and `spec.sourceRef` as environment variables — see the [worker variables](/docs/kubernetes/pipeline-crd#worker) on the CRD page. The queue URL below comes from the `sourceRef` declared in `pipelines/extract-chunk.yaml`. ```python # extract_chunk.py import asyncio import json import os import boto3 from hevlayer import AsyncHevlayer PIPELINE = os.environ["HEVLAYER_PIPELINE_ID"] SOURCE = json.loads(os.environ["HEVLAYER_SOURCE_REF"]) sqs = boto3.client("sqs") def chunks(text: str, size: int = 800) -> list[str]: return [text[i : i + size] for i in range(0, len(text), size)] async def main() -> None: async with AsyncHevlayer( base_url=os.environ["HEVLAYER_BASE_URL"], api_key=os.environ.get("LAYER_GATEWAY_API_KEY"), ) as layer: while True: batch = sqs.receive_message( QueueUrl=SOURCE["queueUrl"], MaxNumberOfMessages=10, ).get("Messages", []) for m in batch: doc = json.loads(m["Body"]) await layer.put_pipeline_document_chunks(PIPELINE, doc["id"], { "chunks": [ {"id": f"{doc['id']}-{i}", "text": t} for i, t in enumerate(chunks(doc["text"])) ], }) sqs.delete_message(QueueUrl=SOURCE["queueUrl"], ReceiptHandle=m["ReceiptHandle"]) asyncio.run(main()) ``` ```go // extract_chunk.go package main import ( "context" "encoding/json" "fmt" "os" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/sqs" hevlayer "github.com/hev/layer-go" ) func chunks(text string, size int) []string { var out []string for i := 0; i < len(text); i += size { out = append(out, text[i:min(i+size, len(text))]) } return out } func main() { ctx := context.Background() pipeline := os.Getenv("HEVLAYER_PIPELINE_ID") var source struct { QueueURL string `json:"queueUrl"` } json.Unmarshal([]byte(os.Getenv("HEVLAYER_SOURCE_REF")), &source) cfg, _ := config.LoadDefaultConfig(ctx) queue := sqs.NewFromConfig(cfg) layer := hevlayer.NewClient( hevlayer.WithBaseURL(os.Getenv("HEVLAYER_BASE_URL")), hevlayer.WithAPIKey(os.Getenv("LAYER_GATEWAY_API_KEY")), ) for { batch, err := queue.ReceiveMessage(ctx, &sqs.ReceiveMessageInput{ QueueUrl: &source.QueueURL, MaxNumberOfMessages: 10, }) if err != nil { continue } for _, m := range batch.Messages { var doc struct { ID string `json:"id"` Text string `json:"text"` } json.Unmarshal([]byte(*m.Body), &doc) var staged []hevlayer.Chunk for i, t := range chunks(doc.Text, 800) { staged = append(staged, hevlayer.Chunk{ID: fmt.Sprintf("%s-%d", doc.ID, i), Text: t}) } layer.PutPipelineDocumentChunks(ctx, pipeline, doc.ID, &hevlayer.PutChunksRequest{Chunks: staged}) queue.DeleteMessage(ctx, &sqs.DeleteMessageInput{ QueueUrl: &source.QueueURL, ReceiptHandle: m.ReceiptHandle, }) } } } ``` ```typescript // extract_chunk.ts import { DeleteMessageCommand, ReceiveMessageCommand, SQSClient, } from "@aws-sdk/client-sqs"; import { Hevlayer } from "hevlayer"; const PIPELINE = process.env.HEVLAYER_PIPELINE_ID!; const SOURCE = JSON.parse(process.env.HEVLAYER_SOURCE_REF!); const sqs = new SQSClient({}); const layer = new Hevlayer({ baseUrl: process.env.HEVLAYER_BASE_URL, apiKey: process.env.LAYER_GATEWAY_API_KEY, }); function chunks(text: string, size = 800): string[] { const out: string[] = []; for (let i = 0; i < text.length; i += size) out.push(text.slice(i, i + size)); return out; } while (true) { const batch = await sqs.send(new ReceiveMessageCommand({ QueueUrl: SOURCE.queueUrl, MaxNumberOfMessages: 10, })); for (const message of batch.Messages ?? []) { const doc = JSON.parse(message.Body ?? "{}"); await layer.putPipelineDocumentChunks(PIPELINE, doc.id, { chunks: chunks(doc.text).map((text, i) => ({ id: `${doc.id}-${i}`, text })), }); await sqs.send(new DeleteMessageCommand({ QueueUrl: SOURCE.queueUrl, ReceiptHandle: message.ReceiptHandle, })); } } ``` ## Embed The GPU worker claims pending documents, reads their chunks back, and writes vectors. Writing vectors upserts to the namespace's configured VectorStore and marks the document `indexed`. Claims are leased, so a worker that crashes loses nothing. For multivector namespaces on a `kind: search` store, send `vectors: [[...], [...]]` on an entry instead of `vector: [...]`; the gateway forwards the bag and caches the first inner vector for `nearest_to_id` lookup. ```python # embed.py import asyncio import os from hevlayer import AsyncHevlayer from sentence_transformers import SentenceTransformer PIPELINE = os.environ["HEVLAYER_PIPELINE_ID"] model = SentenceTransformer("all-MiniLM-L6-v2") async def main() -> None: async with AsyncHevlayer( base_url=os.environ["HEVLAYER_BASE_URL"], api_key=os.environ.get("LAYER_GATEWAY_API_KEY"), ) as layer: while True: claimed = await layer.claim_documents(PIPELINE, { "stage": "pending", "claim_stage": "embedding", "limit": 16, "worker_id": "embed-0", }) for doc_id in claimed.documents: doc_chunks = await layer.get_pipeline_document_chunks(PIPELINE, doc_id) vectors = model.encode([c.text for c in doc_chunks]) await layer.put_pipeline_document_vectors(PIPELINE, doc_id, { "vectors": [ {"id": c.id, "vector": v.tolist(), "attributes": {"text": c.text}} for c, v in zip(doc_chunks, vectors) ], }) asyncio.run(main()) ``` ```go // embed.go package main import ( "context" "os" hevlayer "github.com/hev/layer-go" ) func main() { ctx := context.Background() pipeline := os.Getenv("HEVLAYER_PIPELINE_ID") layer := hevlayer.NewClient( hevlayer.WithBaseURL(os.Getenv("HEVLAYER_BASE_URL")), hevlayer.WithAPIKey(os.Getenv("LAYER_GATEWAY_API_KEY")), ) for { claimed, err := layer.ClaimDocuments(ctx, pipeline, &hevlayer.ClaimDocumentsRequest{ Stage: "pending", ClaimStage: "embedding", Limit: 16, WorkerID: "embed-0", }) if err != nil { continue } for _, docID := range claimed.Documents { docChunks, err := layer.GetPipelineDocumentChunks(ctx, pipeline, docID) if err != nil { continue } texts := make([]string, len(*docChunks)) for i, c := range *docChunks { texts[i] = c.Text } vectors := embed(texts) // your embedding model or service entries := make([]hevlayer.VectorEntry, len(*docChunks)) for i, c := range *docChunks { entries[i] = hevlayer.VectorEntry{ ID: c.ID, Vector: vectors[i], Attributes: map[string]interface{}{"text": c.Text}, } } layer.PutPipelineDocumentVectors(ctx, pipeline, docID, &hevlayer.PutVectorsRequest{Vectors: entries}) } } } ``` ```typescript // embed.ts import { Hevlayer } from "hevlayer"; const PIPELINE = process.env.HEVLAYER_PIPELINE_ID!; const layer = new Hevlayer({ baseUrl: process.env.HEVLAYER_BASE_URL, apiKey: process.env.LAYER_GATEWAY_API_KEY, }); while (true) { const claimed = await layer.claimDocuments(PIPELINE, { stage: "pending", claim_stage: "embedding", limit: 16, worker_id: "embed-0", }); for (const docId of claimed.documents) { const docChunks = await layer.getPipelineDocumentChunks(PIPELINE, docId); const vectors = await embed(docChunks.map((chunk) => chunk.text)); await layer.putPipelineDocumentVectors(PIPELINE, docId, { vectors: docChunks.map((chunk, i) => ({ id: chunk.id, vector: vectors[i], attributes: { text: chunk.text }, })), }); } } ``` ## Deploy Build the two workers into the images your YAML references and push them to a registry your cluster can pull — Layer does not build images. Then apply the resources: ```sh kubectl apply -f pipelines/ ``` The operator creates one Deployment per resource and the embed pool's KEDA object. Order doesn't matter here: the app creates the gateway pipeline before it enqueues a batch (staging into a pipeline id that doesn't exist returns 404), so workers never see a missing pipeline. Nothing else to wire: the CRD [types themselves](/docs/install#helm) install with the Helm chart. ## Trigger a run The app exposes the pipeline to the rest of your system as one endpoint: `POST /index-runs` sends a batch to the source queue, then waits for the run to complete and returns the snapshot it produced. The pipeline is created on first use — this is where the target namespace is set in code. Layer deduplicates `create_missing` staging by pipeline and document id. Large staging calls publish queue segments incrementally, so downstream workers can claim committed rows and KEDA can observe their depth while the same call is still adding later segments. ```python # app.py import asyncio import json import os import time import boto3 from fastapi import FastAPI from hevlayer import AsyncHevlayer, HevlayerError QUEUE = "https://sqs.us-east-1.amazonaws.com/123456789/product-updates" sqs = boto3.client("sqs") app = FastAPI() layer = AsyncHevlayer( base_url=os.environ["HEVLAYER_BASE_URL"], api_key=os.environ.get("LAYER_GATEWAY_API_KEY"), ) @app.post("/index-runs") async def index_run(documents: list[dict]) -> dict: started_ms = int(time.time() * 1000) try: await layer.create_pipeline({"id": "products", "target_namespace": "products"}) except HevlayerError as e: if e.status_code != 409: # 409: already exists raise for doc in documents: sqs.send_message(QueueUrl=QUEUE, MessageBody=json.dumps(doc)) await drain() sha = await next_snapshot(after_ms=started_ms) return {"documents": len(documents), "snapshot": sha} ``` ```go // app.go var ( queueURL = "https://sqs.us-east-1.amazonaws.com/123456789/product-updates" queue *sqs.Client // sqs.NewFromConfig in main layer = hevlayer.NewClient( hevlayer.WithBaseURL(os.Getenv("HEVLAYER_BASE_URL")), hevlayer.WithAPIKey(os.Getenv("LAYER_GATEWAY_API_KEY")), ) ) func indexRun(w http.ResponseWriter, r *http.Request) { ctx := r.Context() startedMs := time.Now().UnixMilli() var documents []map[string]interface{} json.NewDecoder(r.Body).Decode(&documents) _, err := layer.CreatePipeline(ctx, &hevlayer.CreatePipelineRequest{ ID: "products", TargetNamespace: "products", }) var herr *hevlayer.HevlayerError if err != nil && !(errors.As(err, &herr) && herr.StatusCode == 409) { // 409: already exists http.Error(w, err.Error(), http.StatusBadGateway) return } for _, doc := range documents { body, _ := json.Marshal(doc) mb := string(body) queue.SendMessage(ctx, &sqs.SendMessageInput{QueueUrl: &queueURL, MessageBody: &mb}) } drain(ctx) sha := nextSnapshot(ctx, startedMs) json.NewEncoder(w).Encode(map[string]interface{}{ "documents": len(documents), "snapshot": sha, }) } ``` ```typescript // app.ts import { SendMessageCommand, SQSClient } from "@aws-sdk/client-sqs"; import { Hevlayer } from "hevlayer"; const queueUrl = "https://sqs.us-east-1.amazonaws.com/123456789/product-updates"; const queue = new SQSClient({}); const layer = new Hevlayer({ baseUrl: process.env.HEVLAYER_BASE_URL, apiKey: process.env.LAYER_GATEWAY_API_KEY, }); async function indexRun(documents: Record[]) { const startedMs = Date.now(); await layer.ensurePipeline({ id: "products", target_namespace: "products" }); for (const doc of documents) { await queue.send(new SendMessageCommand({ QueueUrl: queueUrl, MessageBody: JSON.stringify(doc), })); } await drain(); return { documents: documents.length, snapshot: await nextSnapshot(startedMs) }; } ``` ## Wait for completion A run is complete in two steps: the queue drains, then the consistency watcher observes the namespace stable and writes a [snapshot](/docs/api/snapshots) past the run's watermark. `pending_count` is the same signal KEDA scales on — when it reaches zero, the embed pool scales back to zero. `status` is `waiting_on_upstream` when a downstream worker has no pending rows to claim while upstream stages are still active. If a worker cannot load a document's durable chunk payload after the configured retry budget, Layer moves that document to `failed` and adds the dead-letter reason to `failed_reasons`, such as `{"chunks_unavailable": 2}`. The snapshot SHA addresses facet listings and counts exact at that watermark; flip your application to it. ```python # app.py async def drain() -> None: while True: status = await layer.get_pipeline_status("products") if status.pending_count == 0: # status.counts: {"pending": 0, "indexed": 8530} return await asyncio.sleep(10) async def next_snapshot(after_ms: int) -> str: while True: history = await layer.list_namespace_history("products", limit=1) if history and history[0].watermark_ms >= after_ms: return history[0].sha await asyncio.sleep(30) ``` ```go // app.go func drain(ctx context.Context) { for { status, err := layer.GetPipelineStatus(ctx, "products") if err == nil && status.PendingCount == 0 { // status.Counts: {"pending": 0, "indexed": 8530} return } time.Sleep(10 * time.Second) } } func nextSnapshot(ctx context.Context, afterMs int64) string { for { history, err := layer.ListNamespaceHistory(ctx, "products", &hevlayer.ListNamespaceHistoryParams{Limit: 1}) if err == nil && len(history) > 0 && history[0].WatermarkMs >= afterMs { return history[0].Sha } time.Sleep(30 * time.Second) } } ``` ```typescript // app.ts const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); async function drain() { while (true) { const status = await layer.getPipelineStatus("products"); if (status.pending_count === 0) return; await sleep(10_000); } } async function nextSnapshot(afterMs: number): Promise { while (true) { const history = await layer.listNamespaceHistory("products", { limit: 1 }); if (history.length > 0 && history[0].watermark_ms >= afterMs) { return history[0].sha; } await sleep(30_000); } } ``` Once vectors are indexed, query and fetch them through the namespace API — see [Query & Fetch](/docs/api/query). ## Failure model - VectorStore write failures are hard: the vectors route returns 502 and the document stays in `embedding` for re-claim. - Aerospike cache failures do not block chunk reads when S3 backing is present; PostgreSQL connectivity failures return 500 and should be retried with backoff. The stop-writes recovery path and the metrics to watch live in the [failure-mode runbook](/docs/failure-modes#pipeline-stop-writes). - Lease expiry is handled server-side. A worker that crashes mid-embedding has its documents recovered on the next claim sweep. --- # Namespace metadata Source: https://hevlayer.com/docs/api/namespace-metadata import Upstream from "../../../components/docs/Upstream.astro"; import CodeTabs from "../../../components/docs/CodeTabs.astro"; The metadata payload is proxied verbatim from the upstream `/v2/namespaces/{ns}/metadata` endpoint. Schema, row counts, index status, and timestamps follow the upstream contract. Layer adds a single sub-object on top. ## Request ```python metadata = await client.get_namespace_metadata("products") ``` ```go metadata, err := client.GetNamespaceMetadata(ctx, "products") ``` ```typescript const metadata = await client.getNamespaceMetadata("products"); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces/products/metadata" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` ```jsonc { // Proxied from turbopuffer verbatim "schema": { }, "approx_row_count": 12500, "approx_logical_bytes": 48800000, "created_at": "2026-03-15T10:30:45Z", "updated_at": "2026-05-12T18:49:00Z", "last_write_at": "2026-05-12T18:48:30Z", "index": { "status": "up-to-date" }, // Layer enhancement "layer": { "stable_as_of": 1715600400000, "is_stable": true, "indexed": true, "index_lag_rows": 0 } } ``` ## The `layer` block | Field | Meaning | | --- | --- | | `stable_as_of` | Epoch-ms watermark from the most recent stable poll. Null on cold start before the watcher has observed a stable namespace. | | `is_stable` | Whether the most recent poll observed `index.status == "up-to-date"`. False on cold start, true once the watcher catches up. | | `indexed` | Whether every row in the namespace carries an indexed vector. True once the snapshot's indexed-vector row count has caught up to the namespace row count; false while rows are still awaiting their first index, as during a bulk load or a [pipeline](/docs/api/pipelines) mid-flight. Null for FTS-only namespaces, which have no vector column to reconcile. | | `index_lag_rows` | Count of rows present in the namespace that do not yet have an indexed vector. Zero when `indexed` is true. Reconciled from the most recent [snapshot](/docs/api/snapshots), so it trails live writes by the snapshot cadence. | A read for a namespace that does not exist returns upstream's 404, matching turbopuffer's own metadata endpoint. `is_stable` is the *current* signal — it drives the per-query filter-skip decision on the query path. `stable_as_of` is the *historical* watermark — the cut a filtered query would apply. After a namespace is observed stable, the watcher refreshes this watermark on the stable-tier cadence (`CONSISTENCY_STABLE_POLL_INTERVAL_MS`, default 60000 ms). Writes re-arm the fast tier, so active namespaces are polled on `CONSISTENCY_POLL_INTERVAL_MS`. `indexed` answers a different question than `is_stable`. `is_stable` reports whether the upstream index has caught up on the rows it has *seen*, which is what read-after-write depends on. `indexed` reports whether every row that *should* be present is present and queryable, which is what a bulk load or a [pipeline](/docs/api/pipelines) needs to know it has finished: rows can be staged and counted before their vectors are indexed, so a namespace can read `is_stable: true` while `indexed: false` with a non-zero `index_lag_rows`. The reconciliation runs against the latest [snapshot](/docs/api/snapshots), so `indexed` advances on the snapshot cadence rather than per write. For snapshot history derived from these freshness signals, see [Snapshots](/docs/api/snapshots). ## List namespaces `GET /v2/namespaces` is a Layer-only augmented listing. It pages the upstream namespace list and enriches each row with stability and cache signals. It is the endpoint the dashboard's inventory view reads. ```python namespaces = await client.list_namespaces(prefix="prod", page_size=100) ``` ```go namespaces, err := client.ListNamespaces(ctx, &hevlayer.ListNamespacesParams{ Prefix: "prod", PageSize: 100, }) ``` ```typescript const namespaces = await client.listNamespaces({ prefix: "prod", pageSize: 100, }); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces?prefix=prod&page_size=100" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` ```jsonc { "namespaces": [ { "name": "products", "row_count": 12500, "size_bytes": 48800000, "stable_as_of_ms": 1715600400000, "is_stable": true, "index": { "status": "up-to-date" }, "cache_state": {"state": "warm", "warm_inflight": false}, "last_write_ms": 1715600399000, "shadow": false, "labels": {} } ], "next_cursor": "..." } ``` Each row carries freshness signals derived from that row's metadata fetch. `is_stable` is true when `index.status` is `"up-to-date"`, false when it is `"updating"`, and omitted when metadata has no index signal or the fetch failed. `stable_as_of_ms` is set to the metadata observation time for rows reported up to date. `indexed` and `index_lag_rows` live on `GET /v2/namespaces/{namespace}/metadata`, where the gateway can do the snapshot lookup for one namespace without adding object-store reads to the high-fanout list path. `index` is turbopuffer's indexing state, passed through verbatim: | Field | Meaning | | --- | --- | | `index.status` | `"updating"` or `"up-to-date"`. | | `index.unindexed_bytes` | Write-ahead-log bytes not yet indexed. Present only while `updating` (omitted once caught up). Unindexed data is still searched by queries, so a non-zero value means *behind, but serving* — watch it fall to confirm indexing is draining rather than wedged. | Listing is read-only and does not register namespaces with the consistency watcher. Write traffic and snapshot facet configuration register the namespaces that need durable watermarks. | Query param | Purpose | | --- | --- | | `prefix` | Restrict to namespaces whose name starts with this string. | | `cursor` | Pagination cursor from a prior `next_cursor`. | | `page_size` | Page size; the upstream list page is capped at 1000. | A per-row metadata failure degrades to a row with `metadata_error` set rather than dropping the namespace, so the list stays complete even when a single namespace's metadata call fails. Responses are served from a short-TTL cache (`NAMESPACE_LIST_CACHE_TTL_MS`, default `10000`) so dashboard polling does not fan out a metadata call per namespace per refresh. --- # VectorStores And Warehouses Source: https://hevlayer.com/docs/api/data-supply Layer exposes the declared data-supply resources through read-only gateway routes. `VectorStore` is the serving-side connection; `Warehouse` is the source-side connection used by pipelines. The responses are safe to show in operator tools: they include Secret reference names and keys, never Secret contents. All routes require a key with `read` scope. Create and edit these resources through the Kubernetes CRDs or the dashboard's apply forms. The gateway surface is the credential-safe read projection. ## VectorStores ```sh curl -H "Authorization: Bearer $LAYER_API_KEY" \ "$LAYER_BASE_URL/v2/vectorstores" ``` ```json { "vectorstores": [ { "name": "prod-turbopuffer", "kind": "turbopuffer", "default": true, "endpoint": { "url": "https://aws-us-east-1.turbopuffer.com", "region": "aws-us-east-1" }, "turbopuffer": { "orgId": "org_123" }, "credential": { "secretRef": { "name": "layer-turbopuffer", "key": "turbopuffer-api-key" } }, "inboundAuth": { "mode": "deriveFromStore" }, "status": { "reachable": true, "observedGeneration": 7, "conditions": [] }, "turbopufferUrl": "https://turbopuffer.com/organizations/org_123" } ] } ``` `GET /v2/vectorstores/{name}` returns one object in the same shape. `turbopufferUrl` is omitted when `spec.turbopuffer.orgId` is not set. ## Warehouses ```sh curl -H "Authorization: Bearer $LAYER_API_KEY" \ "$LAYER_BASE_URL/v2/warehouses" ``` ```json { "warehouses": [ { "name": "prod-snowflake", "kind": "snowflake", "snowflake": { "account": "acme-xy12345", "user": "SVC_LAYER", "role": "SVC_LAYER_ROLE", "warehouse": "EXTRACT_WH", "keyPairSecretRef": { "name": "snowflake-rsa" }, "pool": { "size": 5, "timeout": "30s" } }, "verifyInterval": "1h", "status": { "phase": "Verified", "verifiedAt": "2026-06-10T00:00:00Z", "consumers": { "pipelines": 2, "apiKeys": 1 }, "conditions": [] } } ] } ``` `GET /v2/warehouses/{name}` returns one object in the same shape. `phase` is `Pending`, `Verified`, or `Failed`; failed warehouses include `status.failureReason`. `status.consumers` counts pipelines and API keys that still reference the warehouse. --- # Warm cache Source: https://hevlayer.com/docs/api/warm-cache import Upstream from "../../../components/docs/Upstream.astro"; import Callout from "../../../components/docs/Callout.astro"; import CodeTabs from "../../../components/docs/CodeTabs.astro"; Layer exposes two warm endpoints. `hint_cache_warm` is the turbopuffer-compatible hint; `warm` is the Layer-only shortcut that creates a gateway warm job. `GET /v1/namespaces/{ns}/hint_cache_warm` matches turbopuffer's warm-cache hint. The upstream call advises the index to pre-load. Layer additionally runs cache-warm steps on the gateway side. ## Hint-cache warm With no query parameters, the call is a raw passthrough: the gateway forwards it to turbopuffer unchanged and returns the upstream response verbatim. Existing turbopuffer clients keep their exact wire behavior. ```bash curl "$LAYER_GATEWAY_URL/v1/namespaces/products/hint_cache_warm" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` Supplying any warm option (`turbopuffer`, `documents`, `snapshots`, `page_size`) switches the call into Layer orchestration. Steps then default on; each is independently toggleable: | Step | What it does | | --- | --- | | `turbopuffer=true` | Forwards the warm hint upstream. | | `documents=true` | Starts an origin warm job to backfill the document cache. | | `snapshots=true` | Mirrors the latest S3 snapshot body into the cache. | ```python result = await client.hint_cache_warm( "products", turbopuffer=False, documents=False, snapshots=True, ) ``` ```typescript const result = await client.hintCacheWarm("products", { turbopuffer: false, documents: false, snapshots: true, }); ``` ```bash curl "$LAYER_GATEWAY_URL/v1/namespaces/products/hint_cache_warm?turbopuffer=false&documents=false&snapshots=true" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` The generated Go client omits `false` query parameters, so it cannot turn steps off — disable steps over REST (or the Python client) instead. The orchestrated response reports per-step status: ```json { "namespace": "products", "turbopuffer": { "enabled": true, "status": "completed" }, "documents": { "enabled": true, "status": "started", "job": { "id": "warm-job-uuid", "status": "running" } }, "snapshots": { "enabled": true, "status": "completed", "key": "snapshots/products/...", "watermark_ms": 1715600400000, "sha": "..." } } ``` If `documents` is enabled, the response includes a warm job; poll it through `/warm-jobs/{id}`. ## Layer warm `POST /v2/namespaces/{ns}/warm` creates an asynchronous job that pages through turbopuffer, backfills Aerospike, and refreshes `cache_warmed_through`. Use it when bootstrapping a namespace whose data was written outside the gateway. ```python job = await client.warm_cache("products", page_size=1000) ``` ```go job, err := client.WarmCache(ctx, "products", &hevlayer.WarmCacheParams{ PageSize: 1000, }) ``` ```typescript const job = await client.warmCache("products", { pageSize: 1000 }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/warm?page_size=1000" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` The response is `202 Accepted` with the warm job: ```json { "id": "warm-job-uuid", "namespace": "products", "status": "running", "progress": 0, "documents_scanned": 0, "created_at": "2026-05-26T10:00:00Z" } ``` Poll it through: ```python job = await client.get_warm_job("products", job.id) ``` ```go job, err := client.GetWarmJob(ctx, "products", jobID) ``` ```typescript const job = await client.getWarmJob("products", jobId); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces/products/warm-jobs/warm-job-uuid" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` ## Cache-cold behavior Warm jobs, cache scans, cache snapshot jobs, and pipeline chunk reads return 503 `cache_cold` when the document cache is unavailable. Fetch and fetch-many fall through to turbopuffer with `x-layer-cache: miss-on-error` instead. The split is deliberate. Fetch is correctness-first: a cache outage must not turn into a missing document. Warm is throughput-first: warming on a cold cache would be wasted work, so the gateway reports the cold state to the caller rather than silently no-op-ing. A bare `hint_cache_warm` passthrough never touches the gateway cache, so it succeeds even while the cache is cold. The orchestrated form returns 503 `cache_cold` only when `documents` or `snapshots` is requested. For how the cache recovers from an outage and the signals to watch, see the [failure-mode runbook](/docs/failure-modes#read). --- # Snapshot History Source: https://hevlayer.com/docs/api/snapshots import CodeTabs from "../../../components/docs/CodeTabs.astro"; Snapshots are materialized facet histograms for a namespace. They carry facet listings in `values[].v` and facet counts in `values[].n`, stored durably in S3 and mirrored into Aerospike for the latest body. Use `POST /snapshots` to materialize a field now. Use history and body routes to read the durable chronology written by the consistency watcher. ## Snapshot policy Configure automatic snapshot writes with the `Index.spec.snapshot` shape. Kubernetes operators put the shape on the namespace's `Index` CR: ```yaml apiVersion: hevlayer.com/v1 kind: Index metadata: name: products spec: backend: namespace: products snapshot: interval: 5m retention: 30d facetFields: - category - brand ``` | Field | Default | Behavior | | --- | --- | --- | | `facetFields` | `[]` | Facet fields to histogram. Empty or unset disables the automatic snapshot writer for the namespace, so history and activity stay empty. | | `interval` | `5m` | Minimum spacing between automatic snapshot writes. The writer fires on each upstream-stable advance; `interval` only floors how often a write lands. The gateway fallback is `LAYER_SNAPSHOT_MIN_INTERVAL_MS`. | | `retention` | `never` | `never` keeps all history. A duration such as `30d` prunes S3 bodies older than the window, while always keeping the most recent body. | Snapshots are event-driven, not scheduled: an idle namespace does not get a new snapshot just because `interval` elapsed. The gateway refreshes Index policy periodically, so edits take effect without a pod restart. API-only namespaces can set the same shape on the gateway: ```bash curl -X PUT "$LAYER_GATEWAY_URL/v2/namespaces/products/snapshot-policy" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "facetFields": ["category", "brand"], "interval": "5m", "retention": "30d" }' ``` The API policy is stored gateway-side and takes effect immediately for the automatic writer. `GET /v2/namespaces/{ns}/snapshot-policy` returns the API-managed policy for review and automation. Manual `POST /snapshots` jobs with `source: origin` and the automatic writer use the same shard fan-out path. Origin work is bounded by `spec.scan.threads`; stored and cache snapshot reads do not fan out. ## Routes | Route | Method | Behavior | | --- | --- | --- | | `POST /v2/namespaces/{ns}/snapshots` | POST | Create an on-demand snapshot job for one field. | | `GET /v2/namespaces/{ns}/snapshot-policy` | GET | Read the API-managed snapshot policy. | | `PUT /v2/namespaces/{ns}/snapshot-policy` | PUT | Set `facetFields`, `interval`, and `retention` using the `Index.spec.snapshot` shape. | | `GET /v2/namespaces/{ns}/snapshot-jobs` | GET | List in-memory snapshot jobs. | | `GET /v2/namespaces/{ns}/snapshot-jobs/{id}` | GET | Read one snapshot job. | | `GET /v2/namespaces/{ns}/history` | GET | Newest-first durable snapshot history. | | `GET /v2/namespaces/{ns}/snapshots/{sha}` | GET | Full snapshot body by full SHA or 7-char prefix. | | `GET /v2/activity/snapshots` | GET | Cross-namespace snapshot-write activity stream. | Need a named cut for a downstream app? Use [checkpoints](/docs/api/checkpoints) to label the newest durable snapshot watermark without running a scan. ## Manual snapshot ```python job = await client.create_snapshot("products", { "field": "category", "source": "auto", "filters": ["brand", "Eq", "Acme"], "page_size": 1000, }) ``` ```go job, err := client.CreateSnapshot(ctx, "products", &hevlayer.CreateSnapshotRequest{ Field: "category", Source: "auto", Filters: []interface{}{"brand", "Eq", "Acme"}, PageSize: 1000, }) ``` ```typescript const job = await client.createSnapshot("products", { field: "category", source: "auto", filters: ["brand", "Eq", "Acme"], page_size: 1000, }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/snapshots" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "field": "category", "source": "auto", "filters": ["brand", "Eq", "Acme"], "page_size": 1000 }' ``` Valid sources are `auto`, `stored`, `cache`, and `origin`. | Source | Reads from | Notes | | --- | --- | --- | | `auto` | Stored snapshot when possible, otherwise cache/origin policy | Default. Stored snapshots only support unfiltered configured fields. | | `stored` | Latest S3 snapshot body, with Aerospike mirror as a cache | Fastest path for configured facet fields. | | `cache` | Aerospike document cache | Supports filters the cache can evaluate. | | `origin` | turbopuffer paginated scan | Authoritative. Persists the computed snapshot body to S3. | The response is `202 Accepted`: ```json { "id": "snapshot-job-uuid", "namespace": "products", "field": "category", "source": "auto", "status": "running", "progress": 0, "documents_scanned": 0, "created_at": "2026-05-26T10:00:00Z" } ``` Poll the job: ```python job = await client.get_snapshot_job("products", job.id) ``` ```go job, err := client.GetSnapshotJob(ctx, "products", jobID) ``` ```typescript const job = await client.getSnapshotJob("products", jobId); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces/products/snapshot-jobs/snapshot-job-uuid" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` Completed jobs include `sha` when a body was materialized: ```json { "id": "snapshot-job-uuid", "namespace": "products", "field": "category", "source": "origin", "status": "completed", "documents_scanned": 12844, "sha": "3f9e8b21", "stable_as_of": 1747300000123 } ``` ## History ```python history = await client.list_namespace_history("products", limit=20) ``` ```go history, err := client.ListNamespaceHistory(ctx, "products", &hevlayer.ListNamespaceHistoryParams{Limit: 20}) ``` ```typescript const history = await client.listNamespaceHistory("products", { limit: 20 }); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces/products/history?limit=20" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` ```json [ {"watermark_ms": 1747300000123, "sha": "3f9e8b21...", "tags": ["pre-migration"]}, {"watermark_ms": 1747299600045, "sha": "a1c5b09f...", "tags": []} ] ``` | Query param | Default | Purpose | | --- | --- | --- | | `limit` | 50 | Maximum entries returned. Capped at 500. | | `before` | none | Return entries older than this SHA. 7-char prefixes are accepted. | The history endpoint lists S3 keys only; it does not read every snapshot body. `tags` is metadata for operator grouping and restore workflows; it is not part of the snapshot content hash. ## Snapshot body ```python body = await client.get_namespace_snapshot("products", "3f9e8b2") ``` ```go body, err := client.GetNamespaceSnapshot(ctx, "products", "3f9e8b2") ``` ```typescript const body = await client.getNamespaceSnapshot("products", "3f9e8b2"); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces/products/snapshots/3f9e8b2" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` ```json { "namespace": "products", "watermark_ms": 1747300000123, "sha": "3f9e8b21", "row_count": 12500, "fields": [ { "name": "category", "values": [ {"v": "books", "n": 1240}, {"v": "electronics", "n": 873} ] } ], "fields_skipped": [ { "name": "tags", "reason": "exceeded_cap", "distinct_observed": 247000, "cap": 10000 } ] } ``` `fields[].values[].v` is the facet listing. `fields[].values[].n` is the facet count. `row_count` is the number of rows scanned into the snapshot; for vector namespaces, [namespace metadata](/docs/api/namespace-metadata) compares it with the upstream namespace row count to report `indexed` and `index_lag_rows`. Fields present in `fields[]` are complete. Fields above the 10,000 distinct-value cap are listed in `fields_skipped[]` instead of being partially materialized. A skipped field is still enumerable on demand with a [values scan](/docs/api/scans#values-mode), which carries a 1,000,000-value cap instead. ## Activity ```python activity = await client.list_snapshot_activity(since=1747200000000, limit=50) ``` ```go activity, err := client.ListSnapshotActivity(ctx, &hevlayer.ListSnapshotActivityParams{Since: 1747200000000, Limit: 50}) ``` ```typescript const activity = await client.listSnapshotActivity({ since: 1747200000000, limit: 50, }); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/activity/snapshots?since=1747200000000&limit=50" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` | Query param | Required | Purpose | | --- | --- | --- | | `since` | yes | Epoch-ms lower bound on `ts_ms`. | | `limit` | no | Cap 500, default 50. | | `namespace` | no | Exact namespace filter. | | `cursor` | no | Pagination cursor from `next_cursor`. | Activity is snapshot lifecycle only. Search history and clickstream events have separate feeds. --- # Checkpoints Source: https://hevlayer.com/docs/api/checkpoints import CodeTabs from "../../../components/docs/CodeTabs.astro"; Checkpoints give an application a stable name for a known-good namespace cut. Creating a checkpoint labels the newest durable [snapshot](/docs/api/snapshots) body for the namespace and stores that small label record in S3. It does not run a scan or write rows. Use checkpoints when a downstream app needs to browse, diff, or drop data by a named catalog cut instead of by an ad hoc `catalog_run_id` filter. The checkpoint response includes the snapshot `watermark_ms`, content `sha`, and `row_count` added since the previous checkpoint. ## Routes | Route | Method | Behavior | | --- | --- | --- | | `POST /v2/namespaces/{ns}/checkpoints` | POST | Create or return an immutable checkpoint label. | | `GET /v2/namespaces/{ns}/checkpoints` | GET | List checkpoints newest first. | | `GET /v2/namespaces/{ns}/checkpoints/{label}` | GET | Resolve one checkpoint label. | ## Create ```python checkpoint = await client.create_checkpoint("products", { "label": "catalog-2026-06-15", }) ``` ```go checkpoint, err := client.CreateCheckpoint(ctx, "products", &hevlayer.CreateCheckpointRequest{Label: "catalog-2026-06-15"}) ``` ```typescript const checkpoint = await client.createCheckpoint("products", { label: "catalog-2026-06-15", }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/checkpoints" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"label": "catalog-2026-06-15"}' ``` Response: ```json { "namespace": "products", "label": "catalog-2026-06-15", "watermark_ms": 1749513600000, "sha": "3f9e8b21...", "row_count": 10000 } ``` Re-posting the same `label` returns the existing checkpoint unchanged, even if newer snapshots have landed. Labels are namespace-local and may contain ASCII letters, numbers, `-`, `_`, `.`, and `:`. The namespace must already have at least one durable snapshot. If no snapshot body exists yet, creation returns `412 precondition_failed`. ## List ```python page = await client.list_checkpoints("products", limit=20) ``` ```go page, err := client.ListCheckpoints(ctx, "products", &hevlayer.ListCheckpointsParams{Limit: 20}) ``` ```typescript const page = await client.listCheckpoints("products", { limit: 20 }); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces/products/checkpoints?limit=20" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` ```json { "checkpoints": [ { "namespace": "products", "label": "catalog-2026-06-15", "watermark_ms": 1749513600000, "sha": "3f9e8b21...", "row_count": 10000 } ], "next_cursor": null } ``` | Query param | Default | Purpose | | --- | --- | --- | | `limit` | 50 | Maximum entries returned. Capped at 500. | | `before` | none | Opaque cursor from the previous page's `next_cursor`. | ## Resolve ```python checkpoint = await client.get_checkpoint("products", "catalog-2026-06-15") ``` ```go checkpoint, err := client.GetCheckpoint(ctx, "products", "catalog-2026-06-15") ``` ```typescript const checkpoint = await client.getCheckpoint("products", "catalog-2026-06-15"); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces/products/checkpoints/catalog-2026-06-15" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` Resolve returns `404 not_found` when the label does not exist in that namespace. --- # Query History Source: https://hevlayer.com/docs/api/search-history import CodeTabs from "../../../components/docs/CodeTabs.astro"; Layer logs every query the gateway serves into a durable JSONL trail in S3, mirrored into Layer's hot cache for fast recent reads. Fetch events that downstream consumers tag back to a query land in a sibling clickstream feed. Together they make a search session reconstructable after the fact — for relevance tuning, A/B comparison, or incident review. Both feeds are Layer-only. ## Routes | Route | Behavior | | --- | --- | | `GET /v2/namespaces/{ns}/search-history` | Per-namespace query log, newest first. | | `GET /v2/namespaces/{ns}/clickstream` | Fetch events correlated to a search, newest first. | The `/v1/` versions of both routes are identical aliases held for client compatibility. ## Search history entry ```json { "entries": [ { "timestamp": "2026-05-22T08:00:00.000Z", "timestamp_nanos": 1747900800000000000, "namespace": "products", "trace_id": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6", "raw_query": "wireless headphones", "stable_as_of": 1747900700000, "query": {"vector": "[…]", "top_k": 10, "filters": "[…]"}, "top_result_ids": ["asin-B08N5WRWNW", "asin-B07PXGQC1Q"], "tags": ["app:hev-shop", "route:search", "surface:storefront"] } ], "next_cursor": "1747900799000000000" } ``` | Field | Meaning | | --- | --- | | `timestamp` / `timestamp_nanos` | Wall-clock and nanosecond timestamps. `timestamp_nanos` is the pagination cursor. | | `trace_id` | Trace context propagated or generated for the query. Joins to the clickstream feed. | | `raw_query` | Caller-supplied query string from the `x-hevlayer-search-query` header (e.g. the BM25 input). Omitted when the header is absent. | | `stable_as_of` | Epoch-ms namespace watermark used by the served response. Omitted on cold-start gateways before the namespace has a watermark. | | `query` | Structured query summary — vector shape, filters, ranking. | | `top_result_ids` | IDs from the served response, in rank order. | | `tags` | Caller-supplied labels propagated through request headers. Used for ad-hoc segmentation. | [Hybrid text](/docs/api/query#hybrid-text-fusion) queries log as a single entry whose `query` carries the `HybridText` expression, not the expanded legs, so re-issuing the logged query reproduces the whole expansion (tokenization, fuzzy legs, fusion) as a unit. [Routed](/docs/api/query#query-routing) queries additionally carry the routing decision (route, policy version, executed), so per-route engagement can be measured against the clickstream and a logged query can be replayed under a forced route. ### Writing metadata Set `x-hevlayer-search-query` on query requests to capture the human input, and set `x-hevlayer-tags` to a comma-separated list of segmentation tags. The Python client exposes these as the `raw_query` and `tags` keyword arguments; the Go client as the `WithSearchQuery` and `WithSearchTags` request options: ```python query = await client.query_namespace( "products", {"vector": embedding, "top_k": 10, "include_attributes": ["title"]}, raw_query="wireless headphones", tags=["app:hev-shop", "surface:storefront", "route:search", "page:first"], ) history = await client.list_search_history( "products", tags=["app:hev-shop", "route:search", "page:first"], limit=20, ) ``` ```go query, err := client.QueryNamespace(ctx, "products", &hevlayer.QueryRequest{Vector: embedding, TopK: 10, IncludeAttributes: []string{"title"}}, hevlayer.WithSearchQuery("wireless headphones"), hevlayer.WithSearchTags([]string{"app:hev-shop", "surface:storefront", "route:search", "page:first"}), ) history, err := client.ListSearchHistory(ctx, "products", &hevlayer.ListSearchHistoryParams{ Tag: []string{"app:hev-shop", "route:search", "page:first"}, Limit: 20, }) ``` ```typescript const query = await client.queryNamespace( "products", { vector: embedding, top_k: 10, include_attributes: ["title"] }, { searchQuery: "wireless headphones", tags: ["app:hev-shop", "surface:storefront", "route:search", "page:first"], }, ); const history = await client.listSearchHistory("products", { tags: ["app:hev-shop", "route:search", "page:first"], limit: 20, }); ``` ```bash curl -X POST "$LAYER_GATEWAY_URL/v2/namespaces/products/query" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -H "x-hevlayer-search-query: wireless headphones" \ -H "x-hevlayer-tags: app:hev-shop,surface:storefront,route:search,page:first" \ -d '{"vector": [0.0012, -0.043], "top_k": 10, "include_attributes": ["title"]}' curl "$LAYER_GATEWAY_URL/v2/namespaces/products/search-history?tag=app:hev-shop,route:search,page:first&limit=20" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` Keep the query text in `raw_query`; use tags for segmentation, not for duplicating the query string. ### Tag contract Layer splits `x-hevlayer-tags` and `?tag=` on commas, trims whitespace, drops empty values, then sorts and dedupes tags before storing or matching them. Commas are separators and cannot be escaped. Limits: | Limit | Value | | --- | --- | | Max tags | 32 unique tags per request or filter | | Max tag length | 128 bytes | | Allowed characters | ASCII letters, digits, `:`, `_`, `-`, `.`, `/`, `=`, `+` | The list filter uses AND semantics: `?tag=a,b` returns only entries that carry both `a` and `b`. ### Query parameters | Param | Purpose | | --- | --- | | `tag` | Comma-separated tag filter. AND semantics — every tag must match. | | `from` / `to` | RFC3339 time bounds. | | `before` | Pagination cursor; return entries strictly older than the given `timestamp_nanos`. | | `limit` | Cap 500, default 50. | ## Clickstream entry ```json { "events": [ { "timestamp": "2026-05-22T08:00:02.143Z", "timestamp_nanos": 1747900802143000000, "trace_id": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6", "namespace": "products", "doc_id": "asin-B08N5WRWNW", "tags": ["session:abc123"], "source": "fetch", "served_from": "cache" } ], "next_cursor": "1747900802142000000" } ``` `trace_id` joins to the search-history entry that produced the result; `served_from` distinguishes a cache hit from an upstream fetch. `trace_id` is also a supported query parameter so you can pull every event for a single search session: ```python events = await client.list_clickstream( "products", trace_id="f81d4fae-7dec-11d0-a765-00a0c91e6bf6", ) ``` ```go events, err := client.ListClickstream(ctx, "products", &hevlayer.ListClickstreamParams{ TraceID: "f81d4fae-7dec-11d0-a765-00a0c91e6bf6", }) ``` ```typescript const events = await client.listClickstream("products", { traceId: "f81d4fae-7dec-11d0-a765-00a0c91e6bf6", }); ``` ```bash curl "$LAYER_GATEWAY_URL/v2/namespaces/products/clickstream?trace_id=f81d4fae-7dec-11d0-a765-00a0c91e6bf6" \ -H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" ``` ## Storage ```text search-history/{namespace}/{YYYY-MM-DD}/{timestamp_nanos}.jsonl ``` Writes are best-effort and never block the query response. Aerospike holds a recent window for fast reads; S3 is the durable store. A cache outage degrades read latency but not durability — list calls walk the S3 prefix and merge inline.