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¶
Nothing, to run the server. It ships as a single static binary with no runtime dependencies.
- Go 1.26+ only if you embed the library or build from source.
- 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.
Install the server¶
Detects your OS and architecture, downloads the matching release binary and
puts it in ~/.local/bin. Set ROSTAM_INSTALL_DIR to place it elsewhere.
Multi-arch (amd64/arm64). The image binds 0.0.0.0, so it requires a
token. An environment variable keeps it off the command line and out of
ps, but it is not hidden: docker inspect shows it under Config.Env.
For anything beyond local use, mount a secret or use your orchestrator's
secret store.
The default command serves both REST (8080) and the binary TCP
protocol (7000) — publish only the ports you use. The command above maps
REST; add TCP for the Go client, which speaks only the binary protocol:
An unpublished listener is not reachable from the host (though other
containers on the same network still can reach it, and host networking
publishes everything). Either way the token guards every transport
identically — so exposing 7000 is no less safe than 8080. For gRPC, add
-grpc 0.0.0.0:9090 and -p 9090:9090.
Run it¶
rostam-server exposes the same engine over three transports: REST (-http),
gRPC (-grpc), and a compact binary TCP protocol (-tcp). Start it with REST
and TCP on loopback, persisting to ./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.
GET /v1/health and /v1/ready stay auth-exempt so orchestrator probes work
without the token.
Your first search¶
Create a collection, add a point, and search it. These target the loopback server from Run it, which needs no token.
Against the container, authenticate every call: add
-H 'Authorization: Bearer secret' to each curl, construct the Python client as
Rostam("http://localhost:8080", api_key="secret"), and set
ClientConfig.AuthToken in Go.
# 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 Filtering.
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}'
from rostam import Rostam, filters as f
c = Rostam("http://localhost:8080")
c.create_collection("docs", dim=4, metric="cosine")
c.upsert("docs", 1, [0.1, 0.2, 0.3, 0.4],
content="hello rostam",
metadata={"tenant": "acme"})
# search() returns ids, distances and scores.
hits = c.search("docs", [0.1, 0.2, 0.3, 0.4], k=3)
# search_docs() returns the stored content too, and takes a filter.
docs = c.search_docs("docs", [0.1, 0.2, 0.3, 0.4], k=3,
filter=f.eq("tenant", "acme"))
print([(d.id, d.content) for d in docs])
The client sends plain Python values — it applies the tagged metadata encoding for you. Full reference: Python client.
package main
import (
"context"
"fmt"
"log"
"github.com/rostamlabs/rostam"
"github.com/rostamlabs/rostam/vector"
)
func main() {
ctx := context.Background()
// The -tcp port, not -http: the Go remote client speaks the binary
// protocol only. This targets the loopback server from "Run it" (no
// token). Against the container, publish the port (-p 7000:7000) and set
// AuthToken to the same value as ROSTAM_API_KEY.
store, err := rostam.NewClient(rostam.ClientConfig{
Servers: []string{"127.0.0.1:7000"},
// AuthToken: "secret", // required when the server has -api-key set
})
if err != nil {
log.Fatal(err)
}
defer store.Close()
// M / EfConstruction / EfSearch default to 16 / 200 / 64 when omitted,
// as they do over REST and Python. Set here to keep the tuning visible.
if err := store.CreateCollection(ctx, "docs", rostam.VectorConfig{
Dim: 4, Metric: vector.Cosine,
M: 16, EfConstruction: 200, EfSearch: 64,
}); err != nil {
log.Fatal(err)
}
if err := store.VectorUpsert(ctx, "docs", 1,
[]float32{0.1, 0.2, 0.3, 0.4}, "hello rostam",
rostam.VectorInsertOpts{}); err != nil {
log.Fatal(err)
}
hits, err := store.VectorSearch(ctx, "docs", []float32{0.1, 0.2, 0.3, 0.4}, 3)
if err != nil {
log.Fatal(err)
}
fmt.Println(hits)
}
Full reference: Go client.
The full endpoint inventory is in the HTTP API reference. To add authentication and TLS, see Security; for multi-node clusters, see Clustering.
Large initial loads¶
For a first bulk import, bulk_stage(...) + bulk_build(...) ship vectors over
a binary wire and build the index on all cores — far faster than upserting point
by point:
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
// Optional — these default to 16 / 200 / 64. Shown so the recall/latency
// dials are visible in the example you copy from.
M: 16,
EfConstruction: 200,
EfSearch: 64,
})
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).