Indexing the Earth with Strabo
There is a TON of earth observation data, significantly more than the text-based datasets used to train LLMs. The number I’ve heard is ~800PB of satellite imagery alone and growing rapidly, but I don’t think anyone truly knows. This presents a challenge for companies like LGND; how can we wrangle so much data with a tiny fraction of the capital available to frontier labs?
This is the fundamental question that led us to create the Strabo geosimilarity engine. It’s built on the premise that the only datastore cheap enough to store and index geoembeddings at this scale is the blob store. A 512d embedding stored as float32 is 2048 bytes on disk. The table below shows how much these embeddings would cost to store across various storage solutions provided by AWS, assuming on-demand pricing. Keep in mind that this is just the embedding; we must also store additional metadata about each embedding such as location, date/time, and provenance back to the underlying satellite image.
S3 is roughly 3–5x cheaper than block storage and over 1,000x cheaper than GPU memory. So we designed Strabo around one rule: S3 is the source of truth, and everything else (caches, GPUs) is disposable. Strabo is the engine behind the LGND Geo MCP server, which lets AI agents search the surface of the Earth.
Data Structure
The Strabo storage format is very similar to cloud-native data formats like Parquet and Cloud Optimized GeoTIFF, which are optimized for remote access through HTTP GET Range requests. It’s more of a clever data partitioning scheme than a traditional vector index. The smallest unit in Strabo is a leaf, holding roughly 8,000 to 20,000 embeddings, compressed and stored on disk. Leaves organize the data into spatially, temporally, and semantically similar blocks for remote access.
A Strabo index is a collection of segments, and each segment holds many leaves.
A Strabo segment is a container for multiple leaves and the smallest unit for writes. Segments are responsible for encoding spatial and temporal overviews, downsampled aggregations of the embeddings in the leaves of each segment. A Strabo index is simply a collection of segments. Segments are appended onto an index as new imagery is embedded, while several dedicated microservices handle maintenance: compaction, re-clustering, and deletion. A top-level JSON file manages the state of the index, and we use standard compare-and-swap (CAS) updates to enable safe concurrent operations.
For readers familiar with other formats, here’s how Strabo’s concepts map:
Under the hood this looks a lot like Iceberg or Lance, with leaves, segments, tombstones, and compaction standing in for row groups, data files, deletion vectors, and rewrites. The difference is that Strabo organizes data by space and time, not just by row.
Serving
Blob storage is great for lowering storage cost and decoupling your data from your compute. The tradeoff is higher query latency, as each search against the index requires sending many HTTP requests to find the data relevant to that query. We’ve developed several serving architectures to overcome these challenges for different use cases.
CPU
Our first serving layer is a CPU node running a standard NVMe multi-tier read-through cache, an architecture first popularized by Turbopuffer. Cold queries that hit the S3 bucket take ~100s of ms to ~seconds, depending on the size of the query, while warm queries that hit the caching layer take ~10s to ~100s of ms. Spatio-temporal-semantic queries can get really big, 100s of times bigger than traditional text queries. We’ve built a dedicated query planner that offloads these large queries to a distributed pool of CPU nodes, letting us fetch and rerank leaves in parallel. This is the most cost-efficient but also the slowest serving method; it’s the serving layer you hit when using the LGND Geo MCP server’s free tier.
CPU serving: leaves are fetched from S3 with HTTP range requests into an NVMe and RAM cache, and large queries fan out to a distributed CPU pool.
GPU
We’ve also developed a serving layer capable of materializing a spatio-temporal slice of a Strabo index onto a GPU. This materialization happens at query time, adding only a few seconds of cold start, through a highly efficient CUDA-backed pipeline that maxes out the underlying node’s network bandwidth (100+ Gbps) to stream embeddings directly onto the GPU for search with cuVS. The state of Texas (~25M embeddings) takes ~2 seconds to fully materialize. Once materialized, query time drops to ~5–10ms. The index lives only as long as the session that needs it: we build it when a session starts (e.g., “find solar panels in Texas”) and throw it away when the session ends. Today we use this layer for internal high-throughput workloads, and it’s where agent sessions on the MCP server are headed. This gives us the best of both worlds;: cheap blob storage and blazing fast queries on the GPU.
GPU serving: a spatio-temporal slice of leaves is streamed from S3 straight into GPU memory and searched in place.
Distributed GPU
Materialized indexes often get large enough that they don’t fit on a single GPU. Strabo has a built-in understanding of the spatio-temporal-semantic layout of the data, which lets us route each leaf to each GPU while maintaining enough data locality for optimal query performance. A round-robin load balancer routes the incoming request to one query server, fetches relevant leaves from neighboring servers through a consistent hash, reranks locally, and returns the result to the caller. Scaling the cluster horizontally to materialize larger indexes is as simple as adding more GPU nodes and running a small rebalance. We’ve clocked download speeds of 1000+ Gbps across a cluster of 10 GPUs, roughly 100 Gbps per node, so throughput scales linearly with cluster size.
Distributed GPU serving: each node holds a share of the leaves; the coordinating query server (green) gathers results from its neighbors.
Try It: Strabo Powers LGND Geo MCP
LGND is optimizing for the trillion scale, that’s the mission. This post covers how we use Strabo to store and index absurd amounts of geospatial embeddings cheaply. Strabo is the engine behind the LGND products and Geo MCP server. Connect LGND Geo to Claude or any MCP-compatible agent and ask it to find something on Earth; you’ll be querying Strabo’s CPU serving layer on our free tier - get started here today.
We’ve been using the GPU serving layer internally for a number of high-throughput use cases. Please reach out to jeff@lgnd.ai if you’re interested in GPU serving or learning more about Strabo!