Quickstart

Get Firn running locally in under two minutes. All you need is Docker.

Prerequisites

1. Clone and start the stack

This launches MinIO (local S3) and the Firn API server together.

git clone https://github.com/gordonmurray/firnflow
cd firnflow
docker compose up --build

Once you see listening on 0.0.0.0:3000, the API is ready. MinIO is available at localhost:9000 (API) and localhost:9001 (console, credentials: minioadmin / minioadmin).

2. Check health

curl http://localhost:3000/health

Expected response:

ok

3. Upsert vectors

Insert a few vectors into the demo namespace. Firn auto-detects the vector dimension from the first upsert.

curl -X POST http://localhost:3000/ns/demo/upsert \
  -H 'Content-Type: application/json' \
  -d '{
    "rows": [
      {"id": 1, "vector": [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]},
      {"id": 2, "vector": [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]},
      {"id": 3, "vector": [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]}
    ]
  }'

Response:

{"upserted": 3}

4. Query for nearest neighbours

Search the demo namespace for the 2 closest vectors.

curl -X POST http://localhost:3000/ns/demo/query \
  -H 'Content-Type: application/json' \
  -d '{"vector": [1.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "k": 2}'

Response (first query hits S3, populates the cache):

{
  "query_id": "a1b2c3d4",
  "results": [
    {"id": 1, "score": 0.01, "vector": [1.0, 0.0, ...], "text": null},
    {"id": 2, "score": 0.99, "vector": [0.0, 1.0, ...], "text": null}
  ]
}
Cache in action
Run the same query again. The second call returns from cache with zero S3 requests. You can verify this at /metrics.

5. Add text for full-text search

Upsert rows with both vectors and text, then build an FTS index.

# Upsert with text
curl -X POST http://localhost:3000/ns/articles/upsert \
  -H 'Content-Type: application/json' \
  -d '{
    "rows": [
      {"id": 1, "vector": [1.0, 0.0, 0.0, 0.0], "text": "Introduction to vector databases"},
      {"id": 2, "vector": [0.0, 1.0, 0.0, 0.0], "text": "Full-text search with BM25 scoring"},
      {"id": 3, "vector": [0.0, 0.0, 1.0, 0.0], "text": "Hybrid search combines vector and text"}
    ]
  }'

# Build the FTS index (async, returns 202)
curl -X POST http://localhost:3000/ns/articles/fts-index

Then run a text search:

curl -X POST http://localhost:3000/ns/articles/query \
  -H 'Content-Type: application/json' \
  -d '{"text": "vector databases", "k": 2}'

Or a hybrid search (vector + text together):

curl -X POST http://localhost:3000/ns/articles/query \
  -H 'Content-Type: application/json' \
  -d '{
    "vector": [0.9, 0.1, 0.0, 0.0],
    "text": "vector databases",
    "k": 2
  }'

6. Check the metrics

See exactly how many S3 requests Firn has saved you.

curl -s http://localhost:3000/metrics | grep firnflow

Key lines to look for:

firnflow_cache_hits_total{namespace="demo"} 1
firnflow_cache_misses_total{namespace="demo"} 1
firnflow_s3_requests_total{namespace="demo",operation="query"} 1

The cache miss count stays at 1 no matter how many times you repeat the same query. Every subsequent hit is free.

Next steps