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). If you just want to see it work, start with the five-minute path below; the rest of the page covers every install method, the three run modes, and the Go embedding paths.
Try it in 5 minutes¶
Zero to a filtered vector search — one container, the Python client, no build.
# 1. Start the server (Docker), published on loopback only for local use. The
# image binds 0.0.0.0 inside the container so it needs a token; 'secret' is a
# demo placeholder — use a generated token + TLS for anything remote.
docker run -p 127.0.0.1:8080:8080 -e ROSTAM_API_KEY=secret ghcr.io/rostamlabs/rostam
# 3. Create a collection, add a point, search it:
from rostam import Rostam, filters as f
c = Rostam("http://localhost:8080", api_key="secret")
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"})
hits = c.search_docs("docs", [0.1, 0.2, 0.3, 0.4], k=3,
filter=f.eq("tenant", "acme"))
print([(h.id, h.content) for h in hits]) # -> [(1, 'hello rostam')]
That's a running server and an exact, filter-first vector search. Real data uses
a real embedding model — swap the 4-number vectors for your model's output and
set dim to match. From here:
- No Docker? Install the binary directly — Install the server.
- Embed it in Go instead of running a server — Embedded vector search.
- Bulk-load a large dataset fast — Large initial loads.
The sections below go deeper on each.
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 works on Linux and 64-bit Windows (windows/amd64); the AVX2 kernels are Linux/amd64. Everything else has a portable fallback. On Windows a shard file is fully allocated when it is created rather than growing from a hole, so a data directory takes its configured size on disk up front.
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:
docker run -p 127.0.0.1:8080:8080 -p 127.0.0.1:7000:7000 -e ROSTAM_API_KEY=secret \
ghcr.io/rostamlabs/rostam
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).