Skip to content

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
# 2. In another terminal, install the client:
pip install rostam-client
# 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:

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 for CGO_ENABLED=0 builds.
  • 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

curl -fsSL https://rostamlabs.com/install.sh | sh

Detects your OS and architecture, downloads the matching release binary and puts it in ~/.local/bin. Set ROSTAM_INSTALL_DIR to place it elsewhere.

docker run -p 127.0.0.1:8080:8080 -e ROSTAM_API_KEY=secret ghcr.io/rostamlabs/rostam

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.

go install github.com/rostamlabs/rostam/cmd/rostam-server@latest
git clone https://github.com/rostamlabs/rostam
cd rostam
go build -o rostam-server ./cmd/rostam-server

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:

rostam-server -http 127.0.0.1:8080 -tcp 127.0.0.1:7000 -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.

GET /v1/health and /v1/ready stay auth-exempt so orchestrator probes work without the token.

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}'
pip install rostam-client
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.

go mod init example.com/myapp    # modern Go needs a module first
go get github.com/rostamlabs/rostam
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:

c.bulk_stage("docs", ids, vectors)
c.bulk_build("docs")          # concurrent build, all cores

Full method reference: Python client.

Embedded vector search (Go library)

go get github.com/rostamlabs/rostam

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).