Operations

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.

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

FieldPurpose
backend.storeRefOptional 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.namespaceOptional upstream namespace override. Defaults to the Index name.
backend.distanceMetricVector 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: the gateway merges those by distance, which is only meaningful when every namespace in the set shares one embedding space.

FieldPurpose
embedding.modelModel identity and version, e.g. voyage-3-large@v1. Treated as an opaque token compared for equality across a namespace set.
embedding.outputDimVector 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.normalizationVector 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). 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.

// 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" }
    }
  }
}
Fieldtpuf-compatiblePurpose
embed (string)Simple form. A provider-namespaced model id such as voyage/voyage-4-lite.
embed.modelExtended form’s provider-namespaced model id.
embed.dimsOutput dimensionality via Matryoshka truncation.
embed.attributeOverride the derived vector attribute name. Omitting it derives embed_<attr>.
embed.serving.preferLayer extensionnative, autoscaler, or blended. Defaults to native. Layer consumes this field before forwarding the native wire.
embed.revisionLayer extensionPin a Hugging Face model revision. It participates in profile identity and cache keys.
embed.instructionsLayer extensionOptional document and query prefixes for asymmetric embedding models.
embed.modalityLayer extensiontext (default) or image. Image values are URLs or base64 strings for a CLIP-family checkpoint; query-time Embed still uses its text tower.
embed.chunkLayer extensionSplit or fan out the source before embedding. See Chunking.

Layer extensions require prefer: autoscaler or prefer: blended; native serving rejects them with 422. The gateway consumes every extension field and sends only concrete vectors to the active store. 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_<attr> — for example, embed_title for source attribute title. The source attribute is stored alongside the vector.

Model ids must be provider-namespaced. The native menu includes the Voyage 4 family, cohere/embed-v4.0, google/gemini-embedding-*, qwen/qwen3-embedding-*, and baai/bge-m3. Bare ids such as voyage-3-lite are rejected.

The autoscaler leg does not apply a gateway model allowlist. It accepts any provider-namespaced Hugging Face repo id that the configured inference provider can serve, including mixed-case ids such as Snowflake/snowflake-arctic-embed-m-v1.5 and CLIP-family 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:

"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.

Model-serving policy

Per model, embed.serving.prefer chooses how vectors are computed.

preferBehavior
native (default)On Turbopuffer, transparently forward the compatible wire to tpuf’s managed service. On hev search, Layer resolves through its credentialed Turbopuffer provider and sends only concrete vectors to search.
autoscalerResolve with Turbopuffer native embeddings through the gateway. The active store never receives embed: or Embed; writes contain derived vectors and queries contain a concrete ANN vector.
blendedUses the same provider handoff as autoscaler. The traffic-shift policy is independent of the embed wire and does not change request or response shapes.

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

FieldDefaultPurpose
snapshot.facetFields[]Fields the gateway materializes into durable facet snapshots. Empty disables the automatic writer.
snapshot.interval5mMinimum spacing between automatic snapshot writes after upstream-stable advances.
snapshot.retentionnevernever 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.

FieldDefaultPurpose
search.fullTextfalseBuild 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.

esc