# GeoLibre Repository File System Exploration: Complete Architecture and Code Guide

> Explore the GeoLibre repository file system architecture and code. Discover how this monorepo integrates React, Tauri, FastAPI, and Jupyter for a unified geospatial platform.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: architecture
- Published: 2026-08-18

---

**GeoLibre is a modular, npm workspaces-based monorepo that unifies a React/MapLibre frontend, Tauri desktop client, FastAPI Python sidecar, and Jupyter anywidget into a single geospatial platform with shared state management.**

The [opengeos/GeoLibre](https://github.com/opengeos/GeoLibre) repository implements a full-stack geospatial architecture designed for extensibility. Its file system organization reflects strict separation between UI applications, reusable packages, and background workers—while maintaining tight integration through a central Zustand store. This guide walks through the repository structure, key components, and practical patterns for working with GeoLibre's codebase.

---

## Workspace Structure and Root Configuration

GeoLibre uses **npm workspaces** to partition the codebase into three distinct zones. The root [[`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json)](https://github.com/opengeos/GeoLibre/blob/main/package.json) defines these boundaries:

```json
{
  "name": "geolibre",
  "version": "0.0.0-dev",
  "private": true,
  "type": "module",
  "workspaces": [
    "apps/*",
    "packages/*",
    "workers/*"
  ],
  "scripts": {
    "dev": "npm run -w apps/geolibre-desktop dev",
    "tauri:dev": "npm run -w apps/geolibre-desktop dev",
    "build": "npm run -w apps/geolibre-desktop build",
    "lite:build": "npm run -w apps/geolibre-desktop build --lite"
  }
}

```

This configuration enables:

- **Unified dependency installation** – Run `npm install` once at the root
- **Cross-workspace imports** – Packages reference each other via `@geolibre/*` scope
- **Targeted scripts** – `-w` flag runs commands in specific workspaces

The `type: "module"` declaration enforces ES modules throughout, eliminating CommonJS friction in modern tooling.

---

## Apps, Packages, and Workers Deep-Dive

### Apps Directory (`apps/*`)

The `apps/` workspace contains deployable user interfaces:

| Application | Purpose | Key Path |
|-------------|---------|----------|
| `geolibre-desktop` | Tauri-based desktop client | [`apps/geolibre-desktop/src/App.tsx`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/App.tsx) |
| `geolibre-web` | Browser-deployable build (shared core) | [`apps/geolibre-web/src/main.tsx`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-web/src/main.tsx) |

The desktop and web apps share identical React component trees. The critical difference lies in native integrations—file system access, shell execution, and the Python sidecar launcher—which are gated through Tauri's command API.

---

### Packages Directory (`packages/*`)

Reusable libraries form the architectural backbone:

#### `@geolibre/core` – State Management

The central **Zustand store** holds the canonical project state. Located in [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts), it manages:

- Project schema ([`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) structure)
- Layer definitions and visibility
- Plugin state and registration
- UI settings (theme, layout, active tools)
- Undo/redo history stack

**Critical rule**: All MapLibre mutations must flow through store actions. Direct map manipulation bypasses the synchronization layer and breaks undo/redo consistency.

#### `@geolibre/map` – Rendering Engine

This package bridges the store to **MapLibre-GL JS**. The `MapController.syncLayers` method (in [`packages/map/src/MapCanvas.tsx`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/MapCanvas.tsx)) observes store changes and translates them into MapLibre source/layer operations. It also composites **Deck.gl** overlays for 3D data—point clouds, terrain meshes, and particle systems.

#### `@geolibre/ui` – Component Primitives

Built on shadcn/ui patterns, this workspace exports accessible React components: menus, dialogs, toolbars, and the layer tree. These primitives are theme-aware and respect the store's UI settings.

#### `@geolibre/processing` – Client-Side Computation

Vector operations, raster conversions, and format translations execute here. Heavy algorithms delegate to **geolibre-wasm**, a Rust-compiled WebAssembly module. The TypeScript bindings in [`packages/processing/src/wasm-convert.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/processing/src/wasm-convert.ts) expose synchronous and async interfaces to WASM functions.

#### `@geolibre/plugins` – Extensibility System

Built-in plugins reside in `packages/plugins/src/plugins/`. Each plugin exports a manifest satisfying the `GeoLibrePlugin` interface:

```ts
interface GeoLibrePlugin {
  id: string;
  name: string;
  icon?: string;
  run: (store: GeoLibreStore) => Promise<void> | void;
}

```

Registration happens in [`apps/geolibre-desktop/src/hooks/usePlugins.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/hooks/usePlugins.ts):

```ts
// apps/geolibre-desktop/src/hooks/usePlugins.ts
import { terrainPlugin } from "@geolibre/plugins";
import { measurementPlugin } from "@geolibre/plugins";
// Additional imports...

export const builtInPlugins = [
  terrainPlugin,
  measurementPlugin,
  // ...remaining plugins
];

```

External plugins load dynamically via ZIP files containing a [`plugin.json`](https://github.com/opengeos/GeoLibre/blob/main/plugin.json) manifest and bundled assets.

---

### Workers Directory (`workers/*`)

Background processing units handle:

- **Viewer worker** – Decodes specialized formats (e.g., proprietary sensor data) off-main-thread
- **Processing worker** – Executes long-running WASM computations without blocking UI

Workers communicate with the main thread through `postMessage` and structured cloning, with state updates funneled back into the central store.

---

## Python Sidecar Architecture

The `backend/` directory houses a **FastAPI** service providing heavyweight geospatial operations:

| Component | Path | Responsibility |
|-----------|------|----------------|
| FastAPI app | [`backend/geolibre_server/app/main.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/app/main.py) | HTTP entry point, route definitions |
| Whitebox integration | [`backend/geolibre_server/tools/whitebox.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/tools/whitebox.py) | Raster/terrain analysis tools |
| GDAL bindings | [`backend/geolibre_server/tools/gdal_ops.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/tools/gdal_ops.py) | Format conversion, reprojection |
| GeoPandas pipeline | [`backend/geolibre_server/tools/vector_ops.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/tools/vector_ops.py) | Advanced vector processing |

The desktop app launches this sidecar as a subprocess on demand. The web build proxies requests to `/sidecar`, enabling identical API usage across deployment targets.

---

## Python Anywidget Package

The `python/` workspace packages the compiled GeoLibre UI as a **Jupyter anywidget**:

```python

# python/src/geolibre/__init__.py (conceptual structure)

import anywidget
import traitlets

class GeoLibreWidget(anywidget.AnyWidget):
    _esm = "index.js"  # Bundled from npm build

    _css = "styles.css"
    
    # Project state synchronized bi-directionally

    project = traitlets.Dict().tag(sync=True)
    active_layer = traitlets.Unicode().tag(sync=True)

```

Running `npm run build:embed` compiles the frontend and embeds it into the Python wheel, enabling `pip install geolibre` for notebook environments.

---

## Data Flow: From File to Map

Understanding the **ingestion pipeline** clarifies how components interact:

1. **Source ingestion** – Native file dialog (Tauri) or remote URL triggers loading
2. **Format detection** – Extension/MIME type mapped to appropriate reader
3. **Client-side conversion** – DuckDB-WASM Spatial executes `ST_Read` for Parquet/GeoJSON/Shapefile; fallback to `shpjs` for exotic formats
4. **Store insertion** – New `GeoLibreLayer` record created with generated ID, source configuration, and default styling
5. **Map synchronization** – `MapController` diff creates/updates MapLibre sources and layers
6. **Rendering** – MapLibre draws base layers; Deck.gl composites overlays

This unidirectional flow—**UI → Action → Store → Map**—guarantees deterministic state and enables features like time-travel debugging through the undo stack.

---

## Development Commands Reference

| Command | Target Workspace | Effect |
|---------|-----------------|--------|
| `npm run dev` | `apps/geolibre-desktop` | Vite dev server on `localhost:5173` |
| `npm run tauri:dev` | `apps/geolibre-desktop` | Desktop window with HMR |
| `npm run build` | `apps/geolibre-desktop` | Production web bundle |
| `npm run ci` | All | Build, lint, test, coverage gates |
| `npm run test:frontend` | Multiple | Node-based unit tests |
| `npm run test:backend` | `backend/geolibre_server` | Pytest suite |
| `npm run test:e2e` | Built app | Playwright smoke tests |

---

## Regenerating Generated Artifacts

Dependency bumps—especially for `geolibre-wasm`—require cache regeneration:

```bash

# Update Whitebox tool catalog after WASM version change

node scripts/gen-whitebox-menu-catalog.mjs

```

This script inspects the WASM binary's exported functions and generates the processing menu JSON consumed by the UI.

---

## Code Examples for Common Tasks

### Adding a GeoJSON Layer Programmatically

```ts
import { useStore } from "@geolibre/core";
import { addGeoJsonLayer } from "@geolibre/map";

async function ingestRemoteGeoJSON(url: string) {
  const response = await fetch(url);
  const geojson = await response.json();
  
  const store = useStore.getState();
  const layerId = `layer-${crypto.randomUUID()}`;
  
  // Insert into canonical store
  store.addLayer({
    id: layerId,
    type: "geojson",
    source: { data: geojson },
    paint: {
      "fill-color": "#3a86ff",
      "fill-opacity": 0.5,
      "fill-outline-color": "#1a1a2e"
    },
    layout: { visibility: "visible" }
  });
  
  // Trigger MapLibre synchronization
  addGeoJsonLayer(layerId, geojson);
}

```

### Creating a Minimal Plugin

```ts
// packages/plugins/src/plugins/bufferTool.ts
import { GeoLibrePlugin } from "@geolibre/core";

export const bufferTool: GeoLibrePlugin = {
  id: "buffer-analysis",
  name: "Buffer Analysis",
  icon: "⭕",
  
  run: async (store) => {
    const selectedLayer = store.getSelectedLayer();
    if (!selectedLayer || selectedLayer.type !== "geojson") {
      store.notify("Select a vector layer first");
      return;
    }
    
    // Execute turf.js buffer or delegate to WASM
    const buffered = await store.processing.buffer(selectedLayer, 1000); // meters
    
    store.addLayer({
      id: `${selectedLayer.id}-buffered`,
      type: "geojson",
      source: { data: buffered },
      paint: { "fill-color": "#ff006e", "fill-opacity": 0.3 }
    });
  }
};

```

Register in [`apps/geolibre-desktop/src/hooks/usePlugins.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/hooks/usePlugins.ts):

```ts
import { bufferTool } from "@geolibre/plugins";

export const builtInPlugins = [
  bufferTool,
  // ...existing plugins
];

```

### Calling Backend Raster Processing

```ts
interface RasterConversionParams {
  inputPath: string;
  outputFormat: "PMTiles" | "COG" | "Mbtiles";
  maxZoom?: number;
  compression?: "deflate" | "zstd";
}

async function convertRaster(params: RasterConversionParams): Promise<string> {
  const response = await fetch("/sidecar/conversion/raster", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(params)
  });
  
  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Conversion failed: ${error.detail}`);
  }
  
  const { outputPath, fileSize } = await response.json();
  console.log(`Generated ${outputPath} (${fileSize} bytes)`);
  return outputPath;
}

```

---

## Testing and Coverage Requirements

GeoLibre enforces strict quality gates:

| Suite | Line Coverage | Branch Coverage | Function Coverage |
|-------|-------------|---------------|-----------------|
| Frontend | ≥ 78% | ≥ 78% | ≥ 63% |
| Backend | ≥ 55% | — | — |

Test files colocate with features: [`tests/vector-layer-sync.test.ts`](https://github.com/opengeos/GeoLibre/blob/main/tests/vector-layer-sync.test.ts) validates store-to-map synchronization. Import structure matters—testing leaf modules prevents denominator inflation that artificially depresses coverage percentages.

---

## Key Files Reference

| File | Purpose | Direct Link |
|------|---------|-------------|
| [`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json) | Workspace definitions, root scripts | [View source](https://github.com/opengeos/GeoLibre/blob/main/package.json) |
| [`apps/geolibre-desktop/src/hooks/usePlugins.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/hooks/usePlugins.ts) | Plugin registration hub | [View source](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/hooks/usePlugins.ts) |
| [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts) | Zustand store implementation | [View source](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts) |
| [`packages/map/src/MapCanvas.tsx`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/MapCanvas.tsx) | MapLibre React host component | [View source](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/MapCanvas.tsx) |
| [`packages/processing/src/wasm-convert.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/processing/src/wasm-convert.ts) | WASM bindings for heavy computation | [View source](https://github.com/opengeos/GeoLibre/blob/main/packages/processing/src/wasm-convert.ts) |
| [`backend/geolibre_server/app/main.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/app/main.py) | FastAPI sidecar entry point | [View source](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/app/main.py) |
| [`python/src/geolibre/__init__.py`](https://github.com/opengeos/GeoLibre/blob/main/python/src/geolibre/__init__.py) | Jupyter anywidget entry | [View source](https://github.com/opengeos/GeoLibre/blob/main/python/src/geolibre/__init__.py) |
| `scripts/gen-whitebox-menu-catalog.mjs` | WASM catalog generator | [View source](https://github.com/opengeos/GeoLibre/blob/main/scripts/gen-whitebox-menu-catalog.mjs) |
| [`CLAUDE.md`](https://github.com/opengeos/GeoLibre/blob/main/CLAUDE.md) | Contributor guidance and architecture notes | [View source](https://github.com/opengeos/GeoLibre/blob/main/CLAUDE.md) |

---

## Summary

- **Monorepo structure** – Three workspace zones (`apps`, `packages`, `workers`) unified by npm workspaces and `@geolibre/*` scope
- **Centralized state** – Zustand store in `@geolibre/core` enforces unidirectional data flow and enables undo/redo
- **Rendering stack** – MapLibre-GL JS base with Deck.gl overlays, synchronized through `MapController`
- **Extensibility** – Plugin system with built-in registration in [`usePlugins.ts`](https://github.com/opengeos/GeoLibre/blob/main/usePlugins.ts) and dynamic external loading
- **Polyglot backend** – FastAPI sidecar for Python geospatial heavy lifting, transparently proxied for web builds
- **Jupyter integration** – Anywidget package embeds the full UI in notebooks via `build:embed`
- **Strict quality gates** – Coverage floors and colocated tests maintain reliability across workspaces

---

## Frequently Asked Questions

### How do I add a new workspace to the GeoLibre repository?

Create the directory under `apps/`, `packages/`, or `workers/` and include a [`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json) with `name` prefixed by `@geolibre/`. Add the workspace path to the root [`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json) workspaces array, then run `npm install` to establish symlinks. The new package can immediately import sibling workspaces using scoped imports.

### Why does GeoLibre prohibit direct MapLibre manipulation?

Direct map mutations bypass the Zustand store, breaking **undo/redo consistency** and **cross-component synchronization**. All rendering changes must flow through store actions so that `MapController` can diff and apply minimal updates, while other UI components (layer tree, property panels) receive reactive updates through the same source of truth.

### What triggers the `gen-whitebox-menu-catalog.mjs` script?

Regenerate this catalog after any **geolibre-wasm version bump** or when Whitebox tool signatures change. The script introspects the WASM binary's exported functions and rebuilds the JSON menu structure consumed by the processing UI. Run it manually or configure your CI to detect WASM changes and execute automatically.

### Can I use GeoLibre components outside the monorepo?

The `@geolibre/*` packages are designed for internal consumption, but you can **vendor specific packages** by copying the source and adjusting import paths. For Jupyter environments, install the published `geolibre` Python wheel which bundles the complete frontend. External React usage would require building the packages and publishing to a private registry, as the repository does not currently ship to npm public.