# HoloSphere

## HoloSphere

**HoloSphere** is the storage substrate of the Holons ecosystem. It is a JavaScript library that combines [H3 geospatial indexing](https://h3geo.org) for hierarchical addressing with [GunDB](https://gun.eco) for distributed, peer-to-peer storage. Every interface in [Harvest](/software/harvest-dashboard.md)—the web dashboard, the Telegram bot, the CLI, the AI agent, the [MCP server](/software/mcp-server.md)—writes to and reads from HoloSphere. That shared substrate is what makes the system **one source of truth** despite having many user-facing surfaces.

This page is the conceptual introduction. For the full API reference, see [HoloSphere API Reference](#api-reference) below.

### The shape of the substrate

HoloSphere is organized around three concepts:

* **Holons** — units identified by an H3 cell (or, in non-geographic use, by any string ID). A holon is a folder; everything else lives inside it.
* **Lenses** — named categories of data within a holon (`tasks`, `expenses`, `rea_events`, `dna_sequence`, `proposals`, …). Each lens is independent: writing to one doesn't touch the others.
* **Items** — individual records inside a lens, each keyed by its own `id`.

```
holon (H3 cell or string ID)
├── lens "tasks"
│   ├── item "task-abc123"
│   └── item "task-def456"
├── lens "expenses"
│   └── item "expense-789"
├── lens "rea_events"
│   └── …
└── lens "dna_sequence"
    └── …
```

Every domain in `@holons/core` is the authority over one or more lenses—[Tasks](/software/tasks.md) owns `tasks`, [Council](/software/council.md) owns `proposals`, [REA Accounting](/software/rea-accounting.md) owns `rea_events`, and so on. HoloSphere itself is **content-agnostic**: it doesn't know what's in a lens, only how to store it, propagate it, and validate it against a schema if one is set.

### Why H3

H3 is Uber's hierarchical hexagonal geospatial indexing system. It maps any `(lat, lng)` to a hex cell at any of 16 resolutions, and provides constant-time parent/child/neighbor lookups.

HoloSphere uses H3 because:

* **Hierarchy is free.** A neighborhood holon's parent is a city holon; its children are block-level holons. No bespoke region trees.
* **Aggregation is natural.** "Roll up all environmental readings for this region" maps directly to `cellToChildren()` followed by `getAll()` on each child.
* **Neighbors are addressable.** Adjacent holons can subscribe to each other's lenses without configuring relationships individually.
* **Stable IDs.** Two clients computing the H3 index for the same `(lat, lng, resolution)` always get the same cell, so they always end up writing to the same holon.

A holon doesn't have to be geographic. A community, a project, a dataset—anything can be a holon if you give it an ID. H3 is the **default** addressing scheme, not a requirement.

### Why GunDB

GunDB is a decentralized, real-time, peer-to-peer database. HoloSphere uses it because:

* **No central server.** Holons can run on relays, on phones, on Raspberry Pis—the data finds its way.
* **Real-time by default.** Every read can subscribe; updates propagate to all peers.
* **Conflict-free for the common case.** GunDB's CRDT model handles concurrent writes without bespoke conflict resolution.
* **Offline-tolerant.** A peer that goes offline picks up missed writes when it reconnects.

The trade-off is that GunDB is eventually consistent and not transactional. HoloSphere works with this rather than around it: domains are designed so that "out-of-order writes" produce sensible end states.

### Multi-scale operations

The combination of H3 + GunDB lets HoloSphere support operations across scales naturally:

* **Localized** — query one holon, get its own data.
* **Delocalized / aggregated** — query a parent, get a roll-up across children.
* **Hybrid** — subscribe to a holon and propagate computed summaries upward (e.g., "regenerative practices reported per bioregion").

This is what makes [federation](/software/holosphere/federation.md) practical: a network of holons can share data along the H3 hierarchy or along explicit federation edges, with one consistent API.

### Lenses, schemas, and validation

A lens can be left untyped or constrained to a JSON Schema:

```javascript
await sphere.setSchema('temperature', {
  type: 'object',
  properties: {
    id: { type: 'string' },
    temperature: { type: 'number' },
    timestamp: { type: 'number' },
  },
  required: ['id', 'temperature', 'timestamp'],
});
```

In **strict mode**, writes that violate the schema are rejected at the substrate layer. This is what lets domains evolve independently without stepping on each other—the `tasks` schema doesn't know or care what shape `expenses` is.

Lenses without a schema accept arbitrary JSON. This is the **default**, and is appropriate for most domain data, which is typed at the `@holons/core` layer instead.

### Identity-aware writes

In the Holons monorepo, raw HoloSphere puts and gets are wrapped by the **identity-aware layer** in `@holons/core/holosphere`:

* `writeWithIdentity(holonId, lens, item, actor)` — attaches the writing actor to the record, used for attribution in scoring and federation.
* `canWriteToHolon(holonId, actor)` — checks whether an actor has standing to write to a holon (e.g., is a member, is in the right zone).

The MCP server and AI UI both resolve their actor through this layer, so every write is attributable regardless of which interface emitted it.

### Federation

Federation is the system that lets two HoloSphere spaces share data while keeping a single source of truth. A space adds another to its **federation list**; the other space adds the first to its **notify list**. Reads on federated lenses resolve transparently across the relationship.

Federation supports **soul references** (lightweight pointers) by default, so propagating data between spaces doesn't duplicate storage—the original lives in one space, and the federated copy resolves to it on read.

For the full federation model, see [Federation](/software/holosphere/federation.md).

### Installation

```bash
npm install holosphere
```

```javascript
import HoloSphere from 'holosphere';

const sphere = new HoloSphere('my-app');
const holon = await sphere.getHolon(40.7128, -74.0060, 7); // NYC at resolution 7

await sphere.put(holon, 'observations', {
  id: 'obs-001',
  temperature: 22.5,
  timestamp: Date.now(),
});
```

In a Holons monorepo deployment, you don't usually instantiate HoloSphere yourself—`@holons/core/holosphere` provides a factory that every UI shares, configured by `HOLONS_PEER` and `HOLONS_APP` env vars (see [MCP Server](/software/mcp-server.md#holosphere-connection)).

### See also

* [Federation](/software/holosphere/federation.md) — cross-space data sharing
* [Harvest](/software/harvest-dashboard.md) — the monorepo whose interfaces all share one HoloSphere namespace
* [MCP Server](/software/mcp-server.md) — the MCP layer that exposes the substrate to external agents

***

## API Reference

The remainder of this page is the canonical API reference for the standalone HoloSphere library.

### Holonic Architecture

HoloSphere implements holonic architecture in two ways:

**1. Spatial Hierarchy**

```javascript
// Get holons at different scales for a location
const holon = await sphere.getHolon(lat, lng, 7);  // City level
const parent = h3.cellToParent(holon, 6);          // Region level
const children = h3.cellToChildren(holon, 8);       // Neighborhood level

// Get entire hierarchy
const scales = sphere.getHolonScalespace(holon);    // All containing holons
```

**2. Data Organization**

```javascript
// Store data in different aspects (lenses) of a holon
await sphere.put(holon, 'environment', {
    id: 'air-001',
    temperature: 22.5,
    humidity: 65
});

await sphere.put(holon, 'social', {
    id: 'event-001',
    type: 'gathering',
    participants: 50
});
```

### Use Cases

#### Localized structures

```javascript
async function monitorLocalAir(lat, lng) {
    const neighborhood = await sphere.getHolon(lat, lng, 9);

    await sphere.put(neighborhood, 'air-quality', {
        id: `reading-${Date.now()}`,
        pm25: 12.5,
        timestamp: Date.now()
    });

    const readings = await sphere.getAll(neighborhood, 'air-quality');
}
```

#### Delocalized structures

```javascript
async function coordinateResources(region) {
    const localities = h3.cellToChildren(region, h3.getResolution(region) + 1);

    const resources = await Promise.all(
        localities.map(async locality => {
            return sphere.getAll(locality, 'resources');
        })
    );

    await sphere.compute(region, 'resources', 'summarize');
}
```

#### Hybrid structures

```javascript
async function coordinateEmergency(incident) {
    const epicenter = await sphere.getHolon(incident.lat, incident.lng, 8);
    const region = h3.cellToParent(epicenter, 6);

    await sphere.put(epicenter, 'emergencies', {
        id: incident.id,
        type: incident.type,
        severity: incident.severity
    });

    const nearbyResources = await sphere.getAll(region, 'resources');

    sphere.subscribe(epicenter, 'emergencies', (data) => {
        updateResponsePlan(data);
    });
}
```

### Constructor

```javascript
new HoloSphere(
    appName,    // String: Namespace for your application
    strict,     // Boolean: Enable strict schema validation (default: false)
    openaikey   // String: Optional OpenAI API key for AI features
)
```

### Core methods

* `async getHolon(lat, lng, resolution)` — Get H3 index for coordinates
* `async put(holon, lens, data)` — Store data
* `async get(holon, lens, key)` — Retrieve specific data
* `async getAll(holon, lens)` — Retrieve all data
* `async delete(holon, lens, key)` — Delete specific data
* `async deleteAll(holon, lens)` — Delete all data
* `async setSchema(lens, schema)` — Set JSON schema for validation
* `async getSchema(lens)` — Get current schema
* `subscribe(holon, lens, callback)` — Listen for changes

### Data validation

```javascript
const sphere = new HoloSphere('validated-data', true);

const measurementSchema = {
    type: 'object',
    properties: {
        id: { type: 'string' },
        value: { type: 'number' },
        unit: { type: 'string' },
        accuracy: { type: 'number' },
        timestamp: { type: 'number' }
    },
    required: ['id', 'value', 'unit'],
    additionalProperties: false
};

await sphere.setSchema('measurements', measurementSchema);
```

In strict mode, writes that don't match the schema are rejected.

### Federation API

#### `federate(spaceId1, spaceId2, password1, password2, bidirectional)`

Creates a federation relationship between two spaces.

```javascript
await holosphere.federate('space1', 'space2', 'pass1', 'pass2');
```

This sets up:

* `space1.federation` includes `space2`
* `space2.notify` includes `space1`

#### `propagate(holon, lens, data, options)`

Propagates data to federated spaces.

```javascript
await holosphere.propagate('space1', 'items', data, {
  useReferences: true,         // default: uses soul references
  targetSpaces: ['space2']     // optional: specific targets
});
```

Or use auto-propagation on every write:

```javascript
await holosphere.put('space1', 'items', data, null, {
  autoPropagate: true,
});
```

#### Soul references

When using the default `useReferences: true`:

1. Only a lightweight reference is stored in the federated space.
2. The reference contains the original item's ID and soul path.
3. On access, the reference is resolved to the original data.
4. Changes to the original are immediately visible through references.

Single source of truth, no storage duplication.

#### Federation structure

```javascript
{
    id: string,
    name: string,
    federation: string[],  // Source spaces this holon federates with
    notify: string[],      // Target spaces to notify of changes
    timestamp: number
}
```

#### Message federation

```javascript
// Track a federated message across chats
await holosphere.federateMessage('chat1', 'msg1', 'chat2', 'msg2', 'quest');

// Look up all federated copies
const messages = await holosphere.getFederatedMessages('chat1', 'msg1');

// Update across all federated chats
await holosphere.updateFederatedMessages('chat1', 'msg1', async (chatId, messageId) => {
    await updateMessageInChat(chatId, messageId);
});
```

### Dependencies

* `h3-js` — Uber's H3 geospatial indexing
* `gun` — Decentralized database
* `ajv` — JSON Schema validation
* `openai` — AI capabilities (optional)

### License

GPL-3.0-or-later


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.holons.io/software/holosphere.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
