Getting started
Quickstart
Start a server, index a handful of documents, flush them into a segment, and get a BM25-scored result back. Everything below runs against a local Docker container with no S3 and no Postgres.
Run a server
Multi-arch images are published to GHCR on every merge to main.
The container listens on 8080 and needs nothing else to start.
docker run --rm -p 8080:8080 ghcr.io/decover-tech/kosha:latest
curl localhost:8080/v1/healthz
# {"status":"ok"}
To bring up MinIO alongside the server — an S3-compatible endpoint on
:9000 with the dev bucket pre-created — clone the repo and use the
compose file instead:
git clone https://github.com/decover-tech/kosha
cd kosha
docker compose up --build
Point a client at it
Both the CLI and the Python client read KOSHA_HOST and
KOSHA_API_KEY, so setting them once covers every command on this
page.
export KOSHA_HOST=http://localhost:8080
export KOSHA_API_KEY=sk-kosha-dev
cargo install --path crates/kosha-cli # or: cargo run -p kosha-cli -- …
kosha health
The key resolves to a tenant, and that tenant id is prefixed onto every namespace you name. Nothing else in this guide has to think about it.
Index documents
A document is an id and a list of typed fields. Values are always sent as
strings — the field_type is what tells Kosha how to index and
compare them.
{
"documents": [
{
"id": "d1",
"fields": [
{"name": "title", "field_type": "Text", "value": "Breach notification policy"},
{"name": "body", "field_type": "Text", "value": "Report any breach within 72 hours."},
{"name": "status", "field_type": "Keyword", "value": "published"},
{"name": "year", "field_type": "Integer", "value": "2026"}
]
}
]
}
{"indexed_count": 1, "namespace": "acme-corp/quickstart-demo"}
The namespace does not need to be created first — indexing into a name that does not exist registers it. A namespace with no documents costs a metadata row, which is why creating one per customer, per matter, or per tenant is a normal thing to do here.
From the CLI, the same thing from a JSONL file:
kosha index -n quickstart-demo --file crates/kosha-cli/examples/docs.jsonl
The CLI also accepts a shorthand line format and infers types from JSON
values — strings become Text, integers Integer,
floats Float, booleans Boolean:
{"id":"d1","title":"Breach notification policy","year":2026}
Flush
Indexed documents land in a write buffer first. They become visible to search when that buffer is written out as a segment — which happens automatically once it crosses the flush threshold, or immediately when you ask:
{}
# → {"status": "flushed"}
This is the one place Kosha's consistency model shows up in day-to-day use: read-your-writes is bounded by the flush, not immediate. In a test, flush explicitly. In production, let the threshold do it and treat freshness as eventual — the same trade OpenSearch makes with its refresh interval.
Search
One endpoint covers the whole read path. The smallest useful body is a query string:
{
"query_text": "breach notification",
"max_results": 10
}
{
"results": [
{
"doc_id": "d1",
"score": 2.4079,
"fields": [
{"name": "title", "field_type": "Text", "value": "Breach notification policy"}
]
}
],
"total_hits": 1,
"total_hits_relation": "eq"
}
Every other feature is another key in that same body:
filter, knn, aggs, sort,
highlight, wildcard, match_phrase. They
combine in one round trip.
kosha search -n quickstart-demo "breach"
kosha search -n quickstart-demo --body @query.json --json
Check the state
Stats tell you how many documents are live, how many segments they are spread across, and which manifest version is current. Segment count is the number that matters for query fan-out.
{
"namespace": "quickstart-demo",
"documents": 1,
"segments": 1,
"version": 2
}
From Python
The Python client is OpenSearch-shaped on purpose — it exists so an existing OpenSearch integration can be pointed at Kosha without rewriting its call sites.
from kosha_client import KoshaClient
client = KoshaClient(
hosts="http://localhost:8080",
api_key="sk-kosha-dev",
)
client.ping() # True
Next
Read Schema & field types to decide how your documents should be shaped, then Full-text search for what the scorer actually does with them.