Quickstart¶
Rostam can run three ways: as a single-node server speaking HTTP/gRPC/TCP, as a replicated Raft cluster, or as a Go library embedded in your binary (no server at all). This page gets you from zero to a working search in each mode — the server and Python paths first, the Go embedding paths after.
Requirements¶
- Go 1.26+ for library use and building the server.
- The default full-module build requires cgo (the WASM stored-procedure
backend uses
wasmtime-go). The vector engine and cache packages are pure Go — see Development forCGO_ENABLED=0builds. - mmap persistence and the AVX2 kernels are Linux/amd64; everything has a portable fallback.
Run the server¶
rostam-server exposes the same engine over three transports: REST (-http),
gRPC (-grpc), and a compact binary TCP protocol (-tcp). From a clone of the
repo:
git clone https://github.com/rostamlabs/rostam
cd rostam
# Single node: REST on loopback :8080, persisted to ./data
go run ./cmd/rostam-server -http 127.0.0.1:8080 -data ./data
Non-loopback binds require authentication
The server refuses to start with no authentication on a network-reachable
address (e.g. -http :8080). Bind loopback for local development, set
ROSTAM_API_KEY, or pass -insecure to run open deliberately — see
Security.
Create a collection, insert a point, and search — with plain curl:
# Create a 4-dimensional cosine collection
curl -s localhost:8080/v1/collections \
-d '{"name":"docs","config":{"dim":4,"metric":"cosine"}}'
# Upsert a point (metadata values use a tagged encoding — see docs/vector/filtering.md)
curl -s localhost:8080/v1/collections/docs/points \
-d '{"id":1,"vector":[0.1,0.2,0.3,0.4],"content":"hello rostam",
"metadata":{"tenant":{"kind":"string","str":"acme"}},"upsert":true}'
# Search
curl -s localhost:8080/v1/collections/docs/points/search \
-d '{"query":[0.1,0.2,0.3,0.4],"k":3}'
The full endpoint inventory is in the HTTP API reference. To add authentication and TLS, see Security; for multi-node clusters, see Clustering.
Use it from Python¶
The Python client is a dependency-free REST wrapper (source lives under
clients/python):
from rostam import RostamClient
c = RostamClient("http://localhost:8080")
c.create_collection("docs", dim=4)
c.upsert("docs", 1, [0.1, 0.2, 0.3, 0.4], content="hello rostam",
metadata={"tenant": "acme"})
hits = c.search_docs("docs", [0.1, 0.2, 0.3, 0.4], k=3)
For large initial loads, c.bulk_stage(...) + c.bulk_build(...) ship vectors
over a binary wire and build the index on all cores. Full method reference:
Python client.
Embedded vector search (Go library)¶
The vector engine is a standalone package — no server, no cgo, no other Rostam dependencies:
package main
import (
"fmt"
"github.com/rostamlabs/rostam/vector"
)
func main() {
col, err := vector.NewCollection("docs", vector.Config{
Dim: 768,
Metric: vector.Cosine,
Quant: vector.QuantSQ8, // int8 codes: 4× smaller, ~98% recall retained
})
if err != nil {
panic(err)
}
defer col.Close()
embedding := make([]float32, 768) // your embedding model's output
query := make([]float32, 768)
// Insert is create-only (ErrDuplicateID on a live id); use Upsert to replace.
_ = col.Insert(1, embedding, 0, vector.Metadata{
"tenant": vector.NewString("acme"),
}, nil)
// Exact, fast filtered search — the payload index narrows to tenant=acme.
hits, _ := col.SearchFiltered(query, 10, vector.Filter{
Op: vector.FilterEq, Field: "tenant", Value: vector.NewString("acme"),
})
fmt.Println(hits)
// Diversified retrieval for RAG:
diverse, _ := col.SearchMMR(query, 10, vector.MMROpts{Lambda: 0.5})
_ = diverse
}
Where to go next: Search APIs, Filtering, Quantization.
Embedded key-value store (Go library)¶
The rostam.Store facade gives you KV and vector operations behind one
interface. NewDirect is the single-node, no-Raft backend — the fastest path:
package main
import (
"context"
"log"
"time"
"github.com/rostamlabs/rostam"
"github.com/rostamlabs/rostam/ops"
)
func main() {
ctx := context.Background()
reg := ops.NewRegistry()
if err := ops.RegisterBuiltins(reg); err != nil { // get/put/del/incr/expire + vector ops
log.Fatal(err)
}
store, err := rostam.NewDirect(rostam.DirectConfig{
Ops: reg, // required
// DataDir: "./data", // enable mmap persistence + warm restart
})
if err != nil {
log.Fatal(err)
}
defer store.Close()
_ = store.Put(ctx, []byte("user:42"), []byte(`{"coins":100}`), 5*time.Minute)
v, _ := store.Get(ctx, []byte("user:42"))
_ = v
// Atomic server-side read-modify-write, serialized per shard:
_, _ = store.Call(ctx, "incr", ops.EncodeIncrArgs([]byte("views:42"), 1))
_, _ = store.Call(ctx, "expire", ops.EncodeExpireArgs([]byte("user:42"), time.Hour))
}
Swap the constructor to change the backend — the Store interface is identical:
| Constructor | Backend | When |
|---|---|---|
rostam.NewDirect |
in-process, no Raft | single node, library use, fastest |
rostam.NewEmbedded |
in-process + per-shard Raft | replicated / multi-node durability |
rostam.NewClient |
TCP client to a remote cluster | talk to a running server |
Details: KV overview, Deployment modes.
Worked examples¶
examples/semantic-search— end-to-end RAG-style pipeline: OpenAI embeddings → upsert → dense vs hybrid search over the TCP client.examples/filtered-recall-cliff— a runnable demonstration of why the filter-first query planner exists (post-filtering recall collapse vs exact filter-first).