KyroDB docs
Quickstart
Connect pgvector, retrieve a fresh scoped ContextPacket, send change events, and inspect evidence.
KyroDB is a context runtime. It sits between your agent service and an existing knowledge store, retrieves scoped context, and returns a ContextPacket with items, provenance, omissions, warnings, trace id, and freshness proof.
It is not a vector database, embedding service, reranker, or agent framework. You keep your knowledge base. KyroDB makes context retrieval fresh, scoped, provable, and observable so stale context, context pollution, and wrong-scope memory do not silently enter the model prompt.
The developer flow
- Create a KyroDB project in the console.
- Prepare a pgvector-backed Postgres table with tenant, namespace, id, embedding, metadata, content, and watermark columns.
- Connect the pgvector DSN in Console -> Runtime.
- Wait until the Runtime page shows a ready endpoint.
- Create a one-time backend env command and run it from your server project.
- Call KyroDB from your backend before assembling the model prompt.
- Send change events whenever source knowledge changes.
- Use traces, diagnosis, proof reports, and replay workflows to debug and validate behavior.
Browser code should never hold runtime bearer tokens. Use the SDKs only in backend services, route handlers, workers, or agent servers.
Prepare pgvector
KyroDB needs one vector column and executable scope metadata. Scope must be enforced by SQL filters, not comments, naming conventions, or dashboard labels.
create extension if not exists vector;
create table if not exists documents (
tenant_id text not null,
namespace text not null,
id text not null,
content text,
metadata jsonb not null default '{}'::jsonb,
embedding vector(1536) not null,
updated_at timestamptz not null default now(),
primary key (tenant_id, namespace, id)
);
create index if not exists documents_scope_idx
on documents (tenant_id, namespace, updated_at desc);
create index if not exists documents_embedding_hnsw_idx
on documents using hnsw (embedding vector_cosine_ops);If updated_at is the watermark column, your application or database must update it on every content, metadata, or embedding change. A minimal trigger:
create or replace function set_documents_updated_at()
returns trigger
language plpgsql
as $$
begin
new.updated_at = now();
return new;
end;
$$;
drop trigger if exists documents_set_updated_at on documents;
create trigger documents_set_updated_at
before update on documents
for each row
execute function set_documents_updated_at();Use the same embedding dimension your agent already uses. KyroDB validates dimensions at runtime startup and rejects bad query vectors before customer traffic. Use the vector operator class that matches your runtime distance function; the console quickstart uses cosine distance.
Connect it in the console
Open console.kyrodb.com, create or select a project, then go to Runtime.
For pgvector, paste the Postgres connection string and confirm:
- table name, usually
documents; - embedding dimension, such as
1536; - tenant column, usually
tenant_id; - namespace column, usually
namespace.
The current managed console flow assumes these pgvector defaults:
| Field | Default |
|---|---|
| Document id column | id |
| Embedding column | embedding |
| Vector type | vector |
| Content column | content |
| Metadata column | metadata |
| Watermark column | updated_at |
| Distance function | cosine |
Leave Content column blank only if your table is metadata-only and your backend does not expect KyroDB to return document text. Leave Namespace column blank only for a single-namespace table; managed setup will pin that runtime to the console default namespace, default.
For managed or non-local Postgres, include sslmode=require or stronger in the DSN. Use a least-privilege database role with SELECT for retrieval and INSERT/UPDATE/DELETE only if write-through mutations are enabled.
KyroDB stores the DSN in a server-side secret store, materializes runtime config, and queues managed setup. Treat the runtime as callable only after the Runtime page shows a ready endpoint. Ready means the retained runtime health is healthy or degraded; degraded runtimes are callable, but review the health summary before depending on them for production traffic.
Get backend credentials
After the runtime is ready, open Console -> Runtime -> Backend environment and click Create env command. Run the generated command from your backend project:
(
set -e
tmp="$(mktemp .env.kyrodb.XXXXXX)"
request="$(mktemp .kyrodb-bootstrap.XXXXXX)"
trap 'rm -f "$tmp" "$request"' EXIT
chmod 600 "$tmp" "$request"
printf '%s' '{"code":"kyrb_one_time_code"}' > "$request"
if ! http_status="$(curl -sS -o "$tmp" -w '%{http_code}' -X POST 'https://console.kyrodb.com/api/runtime/bootstrap/exchange' \
-H 'Content-Type: application/json' \
-H 'Accept: text/plain' \
--data-binary "@$request")"; then
printf 'KyroDB bootstrap exchange failed before receiving a response.\n' >&2
exit 1
fi
case "$http_status" in
2??) ;;
*)
printf "KyroDB bootstrap exchange failed (HTTP $http_status):\n" >&2
cat "$tmp" >&2
exit 1
;;
esac
mv "$tmp" .env.kyrodb
rm -f "$request"
trap - EXIT
)The generated command writes the one-time bootstrap request and the returned KYRODB_BASE_URL, KYRODB_DATA_PLANE_TOKEN, KYRODB_OBSERVABILITY_TOKEN, and KYRODB_EMBEDDING_DIMENSIONS through private temporary files, cleans those files up on failure, removes the request file after success, then publishes .env.kyrodb only after the exchange succeeds. KYRODB_EMBEDDING_DIMENSIONS is non-secret metadata that helps smoke tests and examples build vectors with the provisioned connector dimension. The other values are server-only. In Next.js, load them only in Route Handlers, Server Actions, background jobs, or other backend code. Never expose them through NEXT_PUBLIC_ variables, client components, browser fetches, localStorage, or analytics payloads.
Load .env.kyrodb into your backend process before calling KyroDBClient.fromEnv() or KyroDBClient.from_env(). For local Node development:
node --env-file=.env.kyrodb server.mjsSDKs read environment variables from the process, not from .env.kyrodb directly. In deployed environments, copy these values into your server, worker, or secret-manager configuration instead of shipping the file.
Retrieve context
TypeScript:
import { KyroDBClient, filters } from "kyrodb";
const kyro = KyroDBClient.fromEnv();
const question = "How do refunds work for annual plans?";
const requestId = "retrieve-req-2026-05-01-001"; // unique per retryable request
const user = { tenantId: "acme" };
// queryEmbedding comes from your embedding pipeline and must match
// the runtime connector dimension configured in the console.
const queryEmbedding = await embedUserQuestion(question);
// user is your authenticated application user mapped into KyroDB scope.
const packet = await kyro.retrieve(
{
query_embedding: queryEmbedding,
scope: { tenant_id: user.tenantId, namespace: "support" },
filters: filters.exact("category", "billing"),
top_k: 8,
freshness_mode: "strict",
include_content: true
},
{ idempotencyKey: requestId }
);
if (packet.status === "stale_blocked") {
throw new Error("KyroDB refused to serve stale context");
}Python:
from kyrodb import KyroDBClient, RuntimeFilter, Scope
kyro = KyroDBClient.from_env()
question = "How do refunds work for annual plans?"
request_id = "retrieve-req-2026-05-01-001" # unique per retryable request
tenant_id = "acme"
# embedding comes from your embedding pipeline and must match
# the runtime connector dimension configured in the console.
embedding = embed_user_question(question)
# user is your authenticated application user mapped into KyroDB scope.
packet = kyro.retrieve(
query_embedding=embedding,
scope=Scope(tenant_id=tenant_id, namespace="support"),
filters=RuntimeFilter.exact("category", "billing"),
top_k=8,
freshness_mode="strict",
include_content=True,
idempotency_key=request_id,
)
if packet.status == "stale_blocked":
raise RuntimeError("KyroDB refused to serve stale context")Tell KyroDB when knowledge changes
If your app writes to Postgres directly, send a scoped change event so KyroDB invalidates stale reusable context:
await kyro.ingestChangeEvent({
target: { type: "namespace", namespace: "support" },
change_type: "content_updated",
scope: { tenant_id: "acme", namespace: "support" },
source_event_id: "cms-evt-2026-05-01-001",
timestamp: new Date().toISOString()
});In production, source_event_id is required and is the event-feed idempotency key. Reuse it only for identical retries. Reusing the same source_event_id with a different scoped payload is rejected.
The pgvector console path is event-feed first: your app writes to Postgres, then sends KyroDB a scoped change event. If a deployment is explicitly certified for write-through mutation ACKs, you can use upsertDocument / deleteDocument so mutation acknowledgement and freshness evidence stay tied to the runtime trace.
Inspect evidence
Use the trace_id from a packet to inspect why context was served, omitted, degraded, or blocked:
const trace = await kyro.observability.getTrace(packet.trace_id);
const diagnosis = await kyro.observability.diagnoseTrace(packet.trace_id);
const proof = await kyro.observability.contextProofReport({ recent: 100 });The first milestone is not "the request returned documents." It is "the request returned the right scoped documents, and KyroDB can prove why."