holosphere 1.1.19 → 2.0.0-alpha0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +36 -0
- package/.eslintrc.json +16 -0
- package/.prettierrc.json +7 -0
- package/README.md +476 -531
- package/bin/holosphere-activitypub.js +158 -0
- package/cleanup-test-data.js +204 -0
- package/examples/demo.html +1333 -0
- package/examples/example-bot.js +197 -0
- package/package.json +47 -87
- package/scripts/check-bundle-size.js +54 -0
- package/scripts/check-quest-ids.js +77 -0
- package/scripts/import-holons.js +578 -0
- package/scripts/publish-to-relay.js +101 -0
- package/scripts/read-example.js +186 -0
- package/scripts/relay-diagnostic.js +59 -0
- package/scripts/relay-example.js +179 -0
- package/scripts/resync-to-relay.js +245 -0
- package/scripts/revert-import.js +196 -0
- package/scripts/test-hybrid-mode.js +108 -0
- package/scripts/test-local-storage.js +63 -0
- package/scripts/test-nostr-direct.js +55 -0
- package/scripts/test-read-data.js +45 -0
- package/scripts/test-write-read.js +63 -0
- package/scripts/verify-import.js +95 -0
- package/scripts/verify-relay-data.js +139 -0
- package/src/ai/aggregation.js +319 -0
- package/src/ai/breakdown.js +511 -0
- package/src/ai/classifier.js +217 -0
- package/src/ai/council.js +228 -0
- package/src/ai/embeddings.js +279 -0
- package/src/ai/federation-ai.js +324 -0
- package/src/ai/h3-ai.js +955 -0
- package/src/ai/index.js +112 -0
- package/src/ai/json-ops.js +225 -0
- package/src/ai/llm-service.js +205 -0
- package/src/ai/nl-query.js +223 -0
- package/src/ai/relationships.js +353 -0
- package/src/ai/schema-extractor.js +218 -0
- package/src/ai/spatial.js +293 -0
- package/src/ai/tts.js +194 -0
- package/src/content/social-protocols.js +168 -0
- package/src/core/holosphere.js +273 -0
- package/src/crypto/secp256k1.js +259 -0
- package/src/federation/discovery.js +334 -0
- package/src/federation/hologram.js +1042 -0
- package/src/federation/registry.js +386 -0
- package/src/hierarchical/upcast.js +110 -0
- package/src/index.js +2669 -0
- package/src/schema/validator.js +91 -0
- package/src/spatial/h3-operations.js +110 -0
- package/src/storage/backend-factory.js +125 -0
- package/src/storage/backend-interface.js +142 -0
- package/src/storage/backends/activitypub/server.js +653 -0
- package/src/storage/backends/activitypub-backend.js +272 -0
- package/src/storage/backends/gundb-backend.js +233 -0
- package/src/storage/backends/nostr-backend.js +136 -0
- package/src/storage/filesystem-storage-browser.js +41 -0
- package/src/storage/filesystem-storage.js +138 -0
- package/src/storage/global-tables.js +81 -0
- package/src/storage/gun-async.js +281 -0
- package/src/storage/gun-wrapper.js +221 -0
- package/src/storage/indexeddb-storage.js +122 -0
- package/src/storage/key-storage-simple.js +76 -0
- package/src/storage/key-storage.js +136 -0
- package/src/storage/memory-storage.js +59 -0
- package/src/storage/migration.js +338 -0
- package/src/storage/nostr-async.js +811 -0
- package/src/storage/nostr-client.js +939 -0
- package/src/storage/nostr-wrapper.js +211 -0
- package/src/storage/outbox-queue.js +208 -0
- package/src/storage/persistent-storage.js +109 -0
- package/src/storage/sync-service.js +164 -0
- package/src/subscriptions/manager.js +142 -0
- package/test-ai-real-api.js +202 -0
- package/tests/unit/ai/aggregation.test.js +295 -0
- package/tests/unit/ai/breakdown.test.js +446 -0
- package/tests/unit/ai/classifier.test.js +294 -0
- package/tests/unit/ai/council.test.js +262 -0
- package/tests/unit/ai/embeddings.test.js +384 -0
- package/tests/unit/ai/federation-ai.test.js +344 -0
- package/tests/unit/ai/h3-ai.test.js +458 -0
- package/tests/unit/ai/index.test.js +304 -0
- package/tests/unit/ai/json-ops.test.js +307 -0
- package/tests/unit/ai/llm-service.test.js +390 -0
- package/tests/unit/ai/nl-query.test.js +383 -0
- package/tests/unit/ai/relationships.test.js +311 -0
- package/tests/unit/ai/schema-extractor.test.js +384 -0
- package/tests/unit/ai/spatial.test.js +279 -0
- package/tests/unit/ai/tts.test.js +279 -0
- package/tests/unit/content.test.js +332 -0
- package/tests/unit/contract/core.test.js +88 -0
- package/tests/unit/contract/crypto.test.js +198 -0
- package/tests/unit/contract/data.test.js +223 -0
- package/tests/unit/contract/federation.test.js +181 -0
- package/tests/unit/contract/hierarchical.test.js +113 -0
- package/tests/unit/contract/schema.test.js +114 -0
- package/tests/unit/contract/social.test.js +217 -0
- package/tests/unit/contract/spatial.test.js +110 -0
- package/tests/unit/contract/subscriptions.test.js +128 -0
- package/tests/unit/contract/utils.test.js +159 -0
- package/tests/unit/core.test.js +152 -0
- package/tests/unit/crypto.test.js +328 -0
- package/tests/unit/federation.test.js +234 -0
- package/tests/unit/gun-async.test.js +252 -0
- package/tests/unit/hierarchical.test.js +399 -0
- package/tests/unit/integration/scenario-01-geographic-storage.test.js +74 -0
- package/tests/unit/integration/scenario-02-federation.test.js +76 -0
- package/tests/unit/integration/scenario-03-subscriptions.test.js +102 -0
- package/tests/unit/integration/scenario-04-validation.test.js +129 -0
- package/tests/unit/integration/scenario-05-hierarchy.test.js +125 -0
- package/tests/unit/integration/scenario-06-social.test.js +135 -0
- package/tests/unit/integration/scenario-07-persistence.test.js +130 -0
- package/tests/unit/integration/scenario-08-authorization.test.js +161 -0
- package/tests/unit/integration/scenario-09-cross-dimensional.test.js +139 -0
- package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +357 -0
- package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +410 -0
- package/tests/unit/integration/scenario-12-capability-federated-read.test.js +719 -0
- package/tests/unit/performance/benchmark.test.js +85 -0
- package/tests/unit/schema.test.js +213 -0
- package/tests/unit/spatial.test.js +158 -0
- package/tests/unit/storage.test.js +195 -0
- package/tests/unit/subscriptions.test.js +328 -0
- package/tests/unit/test-data-permanence-debug.js +197 -0
- package/tests/unit/test-data-permanence.js +340 -0
- package/tests/unit/test-key-persistence-fixed.js +148 -0
- package/tests/unit/test-key-persistence.js +172 -0
- package/tests/unit/test-relay-permanence.js +376 -0
- package/tests/unit/test-second-node.js +95 -0
- package/tests/unit/test-simple-write.js +89 -0
- package/vite.config.js +49 -0
- package/vitest.config.js +20 -0
- package/FEDERATION.md +0 -213
- package/compute.js +0 -298
- package/content.js +0 -1022
- package/federation.js +0 -1234
- package/global.js +0 -736
- package/hexlib.js +0 -335
- package/hologram.js +0 -183
- package/holosphere-bundle.esm.js +0 -34549
- package/holosphere-bundle.js +0 -34580
- package/holosphere-bundle.min.js +0 -49
- package/holosphere.d.ts +0 -604
- package/holosphere.js +0 -739
- package/node.js +0 -246
- package/schema.js +0 -139
- package/utils.js +0 -302
package/README.md
CHANGED
|
@@ -1,642 +1,587 @@
|
|
|
1
1
|
# HoloSphere
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Holonic Geospatial Communication Infrastructure**
|
|
4
|
+
|
|
5
|
+
A JavaScript library implementing holonic geospatial communication infrastructure by combining H3 hexagonal geospatial indexing with Nostr relay-based distributed peer-to-peer storage.
|
|
6
|
+
|
|
7
|
+
[](https://bundlephobia.com)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Local-First Architecture**: Reads return instantly from persistent storage; writes guaranteed to sync via OutboxQueue
|
|
14
|
+
- **Geographic & Noospheric Organization**: H3 hexagonal indexing (16 resolution levels) + URI-based conceptual spaces
|
|
15
|
+
- **Distributed P2P Storage**: Nostr relay-based persistence with local-first reads and background sync
|
|
16
|
+
- **Offline Support**: Full read/write functionality offline; automatic sync when connectivity returns
|
|
17
|
+
- **Holonic Architecture**: Autonomous holons with hierarchical parent-child relationships
|
|
18
|
+
- **Holograms**: Lightweight references to data with local overrides and automatic resolution
|
|
19
|
+
- **Real-time Subscriptions**: Live data updates with throttling, filtering, and hologram resolution
|
|
20
|
+
- **Federation**: Bidirectional data sharing within and across HoloSphere instances
|
|
21
|
+
- **Cross-Holosphere Federation**: Multi-instance partnerships with capability token exchange
|
|
22
|
+
- **Discovery Protocol**: Automated Nostr-based federation handshake
|
|
23
|
+
- **Schema Validation**: JSON Schema 2019 with strict/permissive modes and URI references
|
|
24
|
+
- **Cryptographic Security**: secp256k1 signatures, capability tokens with permissions and scopes
|
|
25
|
+
- **Social Protocols**: Unified interface for Nostr (NIP-01) and ActivityPub
|
|
26
|
+
- **Hierarchical Aggregation**: Automatic upcast to parent holons (summarize, aggregate, concatenate)
|
|
4
27
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
A holon (from Greek 'holos' meaning whole, with suffix 'on' meaning part) is simultaneously a whole and a part. In HoloSphere, holons are implemented as hierarchical geographic cells that can:
|
|
28
|
+
## Installation
|
|
8
29
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
30
|
+
### npm
|
|
31
|
+
```bash
|
|
32
|
+
npm install holosphere
|
|
33
|
+
```
|
|
13
34
|
|
|
14
|
-
###
|
|
35
|
+
### CDN
|
|
36
|
+
```html
|
|
37
|
+
<script src="https://cdn.jsdelivr.net/npm/holosphere@latest/dist/cdn/holosphere.min.js"></script>
|
|
38
|
+
```
|
|
15
39
|
|
|
16
|
-
|
|
40
|
+
## Quick Start
|
|
17
41
|
|
|
18
|
-
#### 1. Spatial Hierarchy
|
|
19
42
|
```javascript
|
|
20
|
-
|
|
21
|
-
const holon = await sphere.getHolon(lat, lng, 7); // City level
|
|
22
|
-
const parent = h3.cellToParent(holon, 6); // Region level
|
|
23
|
-
const children = h3.cellToChildren(holon, 8); // Neighborhood level
|
|
24
|
-
|
|
25
|
-
// Get entire hierarchy
|
|
26
|
-
const scales = sphere.getHolonScalespace(holon); // All containing holons
|
|
27
|
-
```
|
|
43
|
+
import HoloSphere from 'holosphere';
|
|
28
44
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
humidity: 65
|
|
45
|
+
// Initialize with Nostr relays
|
|
46
|
+
const hs = new HoloSphere({
|
|
47
|
+
appName: 'myapp',
|
|
48
|
+
relays: ['wss://relay.damus.io', 'wss://nos.lol'],
|
|
49
|
+
privateKey: 'your-hex-private-key', // Optional: auto-generated if not provided
|
|
50
|
+
logLevel: 'INFO'
|
|
36
51
|
});
|
|
37
52
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
53
|
+
// Convert coordinates to H3 holon (San Francisco, resolution 9 ≈ 1km²)
|
|
54
|
+
const holonId = await hs.toHolon(37.7749, -122.4194, 9);
|
|
55
|
+
|
|
56
|
+
// Write data to a lens
|
|
57
|
+
await hs.write(holonId, 'temperature', {
|
|
58
|
+
id: 'sensor-001',
|
|
59
|
+
celsius: 18.5,
|
|
60
|
+
timestamp: Date.now()
|
|
42
61
|
});
|
|
43
|
-
```
|
|
44
62
|
|
|
45
|
-
|
|
63
|
+
// Read data
|
|
64
|
+
const data = await hs.read(holonId, 'temperature', 'sensor-001');
|
|
65
|
+
console.log(data); // => { id: 'sensor-001', celsius: 18.5, ... }
|
|
46
66
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
- Municipal service management
|
|
67
|
+
// Subscribe to real-time updates
|
|
68
|
+
const subscription = hs.subscribe(holonId, 'temperature', (data, key) => {
|
|
69
|
+
console.log('Temperature updated:', data);
|
|
70
|
+
});
|
|
52
71
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
async function monitorLocalAir(lat, lng) {
|
|
56
|
-
const neighborhood = await sphere.getHolon(lat, lng, 9);
|
|
57
|
-
|
|
58
|
-
// Store reading
|
|
59
|
-
await sphere.put(neighborhood, 'air-quality', {
|
|
60
|
-
id: `reading-${Date.now()}`,
|
|
61
|
-
pm25: 12.5,
|
|
62
|
-
timestamp: Date.now()
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Get local trends
|
|
66
|
-
const readings = await sphere.getAll(neighborhood, 'air-quality');
|
|
67
|
-
}
|
|
72
|
+
// Unsubscribe when done
|
|
73
|
+
subscription.unsubscribe();
|
|
68
74
|
```
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
## Local-First Architecture
|
|
77
|
+
|
|
78
|
+
HoloSphere implements true local-first principles: your data lives on your device first, with Nostr relays serving as the sync layer.
|
|
79
|
+
|
|
80
|
+
### How It Works
|
|
75
81
|
|
|
82
|
+
**Reads are instant:**
|
|
76
83
|
```javascript
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const localities = h3.cellToChildren(region, h3.getResolution(region) + 1);
|
|
81
|
-
|
|
82
|
-
// Gather resource data from each locality
|
|
83
|
-
const resources = await Promise.all(
|
|
84
|
-
localities.map(async locality => {
|
|
85
|
-
return sphere.getAll(locality, 'resources');
|
|
86
|
-
})
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
// Compute summaries for parent holon
|
|
90
|
-
await sphere.compute(region, 'resources', 'summarize');
|
|
91
|
-
}
|
|
84
|
+
// Data returns immediately from local storage
|
|
85
|
+
// Background refresh updates from relays (non-blocking)
|
|
86
|
+
const data = await hs.read(holonId, 'sensors', 'temp-001');
|
|
92
87
|
```
|
|
93
88
|
|
|
94
|
-
|
|
95
|
-
- Adaptive governance systems
|
|
96
|
-
- Scalable social networks
|
|
97
|
-
- Emergency response coordination
|
|
98
|
-
- Supply chain management
|
|
99
|
-
|
|
89
|
+
**Writes are guaranteed:**
|
|
100
90
|
```javascript
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// Local response
|
|
107
|
-
await sphere.put(epicenter, 'emergencies', {
|
|
108
|
-
id: incident.id,
|
|
109
|
-
type: incident.type,
|
|
110
|
-
severity: incident.severity
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// Regional coordination
|
|
114
|
-
const nearbyResources = await sphere.getAll(region, 'resources');
|
|
115
|
-
|
|
116
|
-
// Subscribe to updates
|
|
117
|
-
sphere.subscribe(epicenter, 'emergencies', (data) => {
|
|
118
|
-
updateResponsePlan(data);
|
|
119
|
-
});
|
|
120
|
-
}
|
|
91
|
+
// Write returns after local persist (~10ms)
|
|
92
|
+
// OutboxQueue ensures delivery to relays with retry
|
|
93
|
+
await hs.write(holonId, 'sensors', { id: 'temp-001', value: 22.5 });
|
|
94
|
+
// Works offline - syncs automatically when online
|
|
121
95
|
```
|
|
122
96
|
|
|
123
|
-
###
|
|
97
|
+
### Offline Behavior
|
|
124
98
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
99
|
+
| Operation | Offline | When Online |
|
|
100
|
+
|-----------|---------|-------------|
|
|
101
|
+
| Read | Returns local data | Background refresh |
|
|
102
|
+
| Write | Persists locally | OutboxQueue syncs with retry |
|
|
103
|
+
| Subscribe | Local events only | Receives relay updates |
|
|
130
104
|
|
|
131
|
-
|
|
105
|
+
### Configuration
|
|
132
106
|
|
|
133
|
-
```
|
|
134
|
-
|
|
107
|
+
```javascript
|
|
108
|
+
const hs = new HoloSphere({
|
|
109
|
+
appName: 'myapp',
|
|
110
|
+
relays: ['wss://relay.damus.io'],
|
|
111
|
+
|
|
112
|
+
// Local-first options
|
|
113
|
+
persistence: true, // Persistent storage (default: true)
|
|
114
|
+
backgroundSync: true, // Enable SyncService (default: true)
|
|
115
|
+
syncInterval: 10000, // Retry interval in ms (default: 10s)
|
|
116
|
+
maxRetries: 5, // Max delivery attempts (default: 5)
|
|
117
|
+
failedTTL: 86400000, // Failed event TTL (default: 24h)
|
|
118
|
+
});
|
|
135
119
|
```
|
|
136
120
|
|
|
137
|
-
|
|
121
|
+
See [LOCALFIRST.md](./LOCALFIRST.md) for detailed architecture documentation.
|
|
122
|
+
|
|
123
|
+
## Core Concepts
|
|
138
124
|
|
|
139
|
-
###
|
|
125
|
+
### Holons
|
|
126
|
+
Locations in either **geographic space** (H3 hexagonal cells) or **noospheric space** (URI-identified concepts):
|
|
140
127
|
|
|
141
128
|
```javascript
|
|
142
|
-
|
|
129
|
+
// Geographic holon (H3 cell at resolution 9)
|
|
130
|
+
const geoHolon = await hs.toHolon(37.7749, -122.4194, 9);
|
|
131
|
+
// => "8928342e20fffff"
|
|
143
132
|
|
|
144
|
-
//
|
|
145
|
-
const
|
|
133
|
+
// Noospheric holon (arbitrary string ID)
|
|
134
|
+
const conceptHolon = "topic/bitcoin";
|
|
135
|
+
```
|
|
146
136
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
file: './my-data', // Custom storage directory
|
|
150
|
-
radisk: true, // Enable disk persistence
|
|
151
|
-
retry: 3, // Retry failed operations
|
|
152
|
-
timeout: 5000 // Operation timeout
|
|
153
|
-
});
|
|
137
|
+
### Lenses
|
|
138
|
+
Categorical data collections within a holon:
|
|
154
139
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
timestamp: Date.now()
|
|
140
|
+
```javascript
|
|
141
|
+
await hs.write(holonId, 'events', {
|
|
142
|
+
id: 'concert-001',
|
|
143
|
+
name: 'Jazz Night',
|
|
144
|
+
venue: 'Blue Note SF'
|
|
161
145
|
});
|
|
162
146
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const stats = sphere.getRadiskStats();
|
|
169
|
-
console.log('Storage stats:', stats);
|
|
147
|
+
await hs.write(holonId, 'sensors', {
|
|
148
|
+
id: 'temp-001',
|
|
149
|
+
value: 22.5,
|
|
150
|
+
unit: 'celsius'
|
|
151
|
+
});
|
|
170
152
|
```
|
|
171
153
|
|
|
172
|
-
###
|
|
154
|
+
### Holograms
|
|
155
|
+
Lightweight references to data that can exist in multiple locations while maintaining a single source of truth:
|
|
173
156
|
|
|
174
|
-
|
|
157
|
+
```javascript
|
|
158
|
+
// Create a hologram (reference) in target holon pointing to source data
|
|
159
|
+
await hs.propagateData(
|
|
160
|
+
sourceData,
|
|
161
|
+
sourceHolonId,
|
|
162
|
+
targetHolonId,
|
|
163
|
+
'events'
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// Holograms support local overrides (position, pinned status, etc.)
|
|
167
|
+
await hs.updateHologramOverrides(targetHolonId, 'events', hologramId, {
|
|
168
|
+
x: 100,
|
|
169
|
+
y: 200,
|
|
170
|
+
pinned: true
|
|
171
|
+
});
|
|
175
172
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
<head>
|
|
180
|
-
<title>HoloSphere Browser Example</title>
|
|
181
|
-
</head>
|
|
182
|
-
<body>
|
|
183
|
-
<!-- Load HoloSphere Bundle -->
|
|
184
|
-
<script src="https://unpkg.com/holosphere@1.1.18/holosphere-bundle.js"></script>
|
|
185
|
-
|
|
186
|
-
<script>
|
|
187
|
-
// Radisk is enabled by default in browser
|
|
188
|
-
const sphere = new HoloSphere('browser-app');
|
|
189
|
-
|
|
190
|
-
// Store data (persisted via IndexedDB)
|
|
191
|
-
async function storeData() {
|
|
192
|
-
const holon = await sphere.getHolon(40.7128, -74.0060, 7);
|
|
193
|
-
await sphere.put(holon, 'observations', {
|
|
194
|
-
id: 'browser-obs-001',
|
|
195
|
-
temperature: 22.5,
|
|
196
|
-
timestamp: Date.now()
|
|
197
|
-
});
|
|
198
|
-
console.log('Data stored and persisted!');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Retrieve data (from IndexedDB)
|
|
202
|
-
async function retrieveData() {
|
|
203
|
-
const holon = await sphere.getHolon(40.7128, -74.0060, 7);
|
|
204
|
-
const data = await sphere.get(holon, 'observations', 'browser-obs-001');
|
|
205
|
-
console.log('Retrieved data:', data);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Check radisk status
|
|
209
|
-
const stats = sphere.getRadiskStats();
|
|
210
|
-
console.log('Radisk stats:', stats);
|
|
211
|
-
</script>
|
|
212
|
-
</body>
|
|
213
|
-
</html>
|
|
173
|
+
// Resolution merges source data with local overrides
|
|
174
|
+
const resolved = await hs.resolveHologram(hologram);
|
|
175
|
+
// => { ...sourceData, x: 100, y: 200, pinned: true }
|
|
214
176
|
```
|
|
215
177
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
- ✅ Data persists across page reloads via IndexedDB
|
|
219
|
-
- ✅ Works offline with local storage
|
|
220
|
-
- ✅ No additional configuration needed
|
|
221
|
-
- ✅ Compatible with all modern browsers
|
|
222
|
-
|
|
223
|
-
See `examples/radisk-browser-example.html` for a complete working example.
|
|
224
|
-
|
|
225
|
-
### Svelte/SvelteKit Usage
|
|
226
|
-
|
|
227
|
-
HoloSphere works seamlessly with Svelte and SvelteKit applications:
|
|
228
|
-
|
|
229
|
-
```svelte
|
|
230
|
-
<script>
|
|
231
|
-
import { onMount } from 'svelte';
|
|
232
|
-
import { browser } from '$app/environment'; // SvelteKit only
|
|
233
|
-
import HoloSphere from 'holosphere';
|
|
234
|
-
|
|
235
|
-
let hs = null;
|
|
236
|
-
|
|
237
|
-
async function initHoloSphere() {
|
|
238
|
-
// Client-side only for SvelteKit
|
|
239
|
-
if (typeof browser !== 'undefined' && !browser) return;
|
|
240
|
-
|
|
241
|
-
hs = new HoloSphere('my-app', false, null, {
|
|
242
|
-
radisk: true,
|
|
243
|
-
file: './app-radata'
|
|
244
|
-
});
|
|
245
|
-
}
|
|
178
|
+
### Federation (Within Holosphere)
|
|
179
|
+
Bidirectional data sharing between holons:
|
|
246
180
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
181
|
+
```javascript
|
|
182
|
+
// Get parent holon (larger geographic region)
|
|
183
|
+
const parents = await hs.getParents(localHolon);
|
|
184
|
+
const regionalHolon = parents[0];
|
|
185
|
+
|
|
186
|
+
// Establish federation - data in localHolon is visible from regionalHolon
|
|
187
|
+
await hs.federate(localHolon, regionalHolon, 'events', {
|
|
188
|
+
direction: 'outbound',
|
|
189
|
+
mode: 'reference' // Creates holograms instead of copies
|
|
190
|
+
});
|
|
251
191
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
Store Data
|
|
255
|
-
</button>
|
|
256
|
-
</main>
|
|
192
|
+
// Query regional holon sees local data via holograms
|
|
193
|
+
const events = await hs.getFederatedData(regionalHolon, 'events');
|
|
257
194
|
```
|
|
258
195
|
|
|
259
|
-
|
|
260
|
-
-
|
|
261
|
-
- ✅ SSR-safe initialization
|
|
262
|
-
- ✅ Reactive data binding
|
|
263
|
-
- ✅ Component lifecycle management
|
|
264
|
-
- ✅ Persistent storage across navigation
|
|
265
|
-
|
|
266
|
-
See `examples/svelte-holosphere-example.svelte` and `examples/sveltekit-holosphere-example/+page.svelte` for complete examples.
|
|
267
|
-
|
|
268
|
-
## Real-World Examples
|
|
269
|
-
|
|
270
|
-
### Environmental Monitoring System
|
|
196
|
+
### Cross-Holosphere Federation
|
|
197
|
+
Multi-instance partnerships with capability token exchange:
|
|
271
198
|
|
|
272
199
|
```javascript
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
temperature: { type: 'number' },
|
|
279
|
-
humidity: { type: 'number' },
|
|
280
|
-
timestamp: { type: 'number' }
|
|
281
|
-
},
|
|
282
|
-
required: ['id', 'temperature', 'timestamp']
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
// Set up the system
|
|
286
|
-
const sphere = new HoloSphere('env-monitor', true); // strict mode enabled
|
|
287
|
-
await sphere.setSchema('temperature', tempSchema);
|
|
288
|
-
|
|
289
|
-
// Store a reading
|
|
290
|
-
async function storeReading(lat, lng, temp, humidity) {
|
|
291
|
-
const holon = await sphere.getHolon(lat, lng, 8);
|
|
292
|
-
const reading = {
|
|
293
|
-
id: `temp-${Date.now()}`,
|
|
294
|
-
temperature: temp,
|
|
295
|
-
humidity: humidity,
|
|
296
|
-
timestamp: Date.now()
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
return sphere.put(holon, 'temperature', reading);
|
|
300
|
-
}
|
|
200
|
+
// Add a federated partner by their public key
|
|
201
|
+
await hs.addFederatedHolosphere(partnerPubKey, {
|
|
202
|
+
name: 'Partner Holosphere',
|
|
203
|
+
permissions: ['read', 'write']
|
|
204
|
+
});
|
|
301
205
|
|
|
302
|
-
//
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
206
|
+
// Issue capability token for the partner
|
|
207
|
+
const capability = await hs.issueCapabilityForFederation(partnerPubKey, {
|
|
208
|
+
permissions: ['read', 'write'],
|
|
209
|
+
scope: { holonId: '*', lensName: 'events' },
|
|
210
|
+
expiresIn: 86400000 // 24 hours
|
|
211
|
+
});
|
|
307
212
|
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
213
|
+
// Read data from a federated partner
|
|
214
|
+
const remoteData = await hs.readFromFederatedSource(
|
|
215
|
+
partnerPubKey,
|
|
216
|
+
holonId,
|
|
217
|
+
'events',
|
|
218
|
+
dataId
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
// Create a hologram pointing to remote data
|
|
222
|
+
await hs.createCrossHolosphereHologram(
|
|
223
|
+
partnerPubKey,
|
|
224
|
+
remoteHolonId,
|
|
225
|
+
'events',
|
|
226
|
+
remoteDataId,
|
|
227
|
+
localHolonId
|
|
228
|
+
);
|
|
313
229
|
```
|
|
314
230
|
|
|
315
|
-
###
|
|
231
|
+
### Discovery Protocol (Nostr-Based)
|
|
232
|
+
Automated federation handshake via Nostr events:
|
|
316
233
|
|
|
317
234
|
```javascript
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
properties: {
|
|
325
|
-
id: { type: 'string' },
|
|
326
|
-
title: { type: 'string' },
|
|
327
|
-
content: { type: 'string' },
|
|
328
|
-
author: { type: 'string' },
|
|
329
|
-
timestamp: { type: 'number' }
|
|
330
|
-
},
|
|
331
|
-
required: ['id', 'title', 'content', 'author']
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
await sphere.setSchema('articles', contentSchema);
|
|
335
|
-
|
|
336
|
-
// Post content for a location
|
|
337
|
-
async function postLocalContent(lat, lng, title, content, author) {
|
|
338
|
-
const holon = await sphere.getHolon(lat, lng, 7);
|
|
339
|
-
const article = {
|
|
340
|
-
id: `article-${Date.now()}`,
|
|
341
|
-
title,
|
|
342
|
-
content,
|
|
343
|
-
author,
|
|
344
|
-
timestamp: Date.now()
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
await sphere.put(holon, 'articles', article);
|
|
348
|
-
|
|
349
|
-
// Generate summaries for parent areas
|
|
350
|
-
await sphere.compute(holon, 'articles', 'summarize');
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Get content for multiple zoom levels
|
|
354
|
-
async function getAreaContent(lat, lng) {
|
|
355
|
-
const holon = await sphere.getHolon(lat, lng, 7);
|
|
356
|
-
const scales = sphere.getHolonScalespace(holon);
|
|
357
|
-
|
|
358
|
-
const content = {};
|
|
359
|
-
for (const scale of scales) {
|
|
360
|
-
content[scale] = await sphere.getAll(scale, 'articles');
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
return content;
|
|
364
|
-
}
|
|
365
|
-
```
|
|
235
|
+
// Send federation request to another holosphere
|
|
236
|
+
await hs.sendFederationRequest(targetPubKey, {
|
|
237
|
+
proposedPermissions: ['read', 'write'],
|
|
238
|
+
proposedScopes: [{ holonId: '*', lensName: 'events' }],
|
|
239
|
+
message: 'Requesting federation partnership'
|
|
240
|
+
});
|
|
366
241
|
|
|
367
|
-
|
|
242
|
+
// Listen for incoming federation requests
|
|
243
|
+
hs.subscribeFederationRequests((request) => {
|
|
244
|
+
console.log('Federation request from:', request.authorPubKey);
|
|
368
245
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
type: 'object',
|
|
375
|
-
properties: {
|
|
376
|
-
id: { type: 'string' },
|
|
377
|
-
value: { type: 'number' },
|
|
378
|
-
unit: { type: 'string' },
|
|
379
|
-
accuracy: { type: 'number' },
|
|
380
|
-
timestamp: { type: 'number' }
|
|
381
|
-
},
|
|
382
|
-
required: ['id', 'value', 'unit'],
|
|
383
|
-
additionalProperties: false
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
await sphere.setSchema('measurements', measurementSchema);
|
|
387
|
-
|
|
388
|
-
// This will succeed
|
|
389
|
-
await sphere.put(holon, 'measurements', {
|
|
390
|
-
id: 'measure-1',
|
|
391
|
-
value: 42.5,
|
|
392
|
-
unit: 'celsius',
|
|
393
|
-
accuracy: 0.1,
|
|
394
|
-
timestamp: Date.now()
|
|
246
|
+
// Accept the request
|
|
247
|
+
hs.acceptFederationRequest(request, {
|
|
248
|
+
grantedPermissions: ['read'],
|
|
249
|
+
grantedScopes: [{ holonId: '*', lensName: 'events' }]
|
|
250
|
+
});
|
|
395
251
|
});
|
|
396
252
|
|
|
397
|
-
//
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
value: "invalid", // wrong type
|
|
401
|
-
extra: "field" // not allowed
|
|
253
|
+
// Subscribe to acceptance/decline responses
|
|
254
|
+
hs.subscribeFederationAcceptances((acceptance) => {
|
|
255
|
+
console.log('Federation accepted by:', acceptance.authorPubKey);
|
|
402
256
|
});
|
|
403
257
|
```
|
|
404
258
|
|
|
405
|
-
|
|
259
|
+
### Hierarchical Aggregation
|
|
260
|
+
Automatic propagation to parent holons:
|
|
406
261
|
|
|
407
|
-
### Constructor
|
|
408
262
|
```javascript
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
)
|
|
263
|
+
await hs.upcast(localHolon, 'sensors', 'temp-001', {
|
|
264
|
+
maxLevel: 3, // Propagate 3 levels up hierarchy
|
|
265
|
+
operation: 'summarize' // or 'aggregate', 'concatenate'
|
|
266
|
+
});
|
|
414
267
|
```
|
|
415
268
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
- `async getHolon(lat, lng, resolution)` - Get H3 index for coordinates
|
|
419
|
-
- `async put(holon, lens, data)` - Store data
|
|
420
|
-
- `async get(holon, lens, key)` - Retrieve specific data
|
|
421
|
-
- `async getAll(holon, lens)` - Retrieve all data
|
|
422
|
-
- `async delete(holon, lens, key)` - Delete specific data
|
|
423
|
-
- `async deleteAll(holon, lens)` - Delete all data
|
|
424
|
-
- `async setSchema(lens, schema)` - Set JSON schema for validation
|
|
425
|
-
- `async getSchema(lens)` - Get current schema
|
|
426
|
-
- `subscribe(holon, lens, callback)` - Listen for changes
|
|
427
|
-
|
|
428
|
-
## Storage Architecture
|
|
429
|
-
|
|
430
|
-
Data in HoloSphere is organized by:
|
|
431
|
-
- **Holons**: H3 geographic indexes
|
|
432
|
-
- **Lenses**: Data categories/types
|
|
433
|
-
- **Items**: Individual data entries with unique IDs
|
|
434
|
-
|
|
435
|
-
## Dependencies
|
|
436
|
-
|
|
437
|
-
- h3-js: Uber's H3 geospatial indexing
|
|
438
|
-
- gun: Decentralized database
|
|
439
|
-
- ajv: JSON Schema validation
|
|
440
|
-
- openai: AI capabilities (optional)
|
|
441
|
-
|
|
442
|
-
## License
|
|
443
|
-
|
|
444
|
-
GPL-3.0-or-later
|
|
445
|
-
|
|
446
|
-
# HoloSphere Federation
|
|
447
|
-
|
|
448
|
-
HoloSphere provides a federation system that allows spaces to share data and messages across different holons. This document outlines the federation functionality and how to use it.
|
|
449
|
-
|
|
450
|
-
## Core Federation Features
|
|
451
|
-
|
|
452
|
-
### Space Federation
|
|
453
|
-
- Create relationships between spaces with clear source-target connections
|
|
454
|
-
- Use soul references to maintain a single source of truth
|
|
455
|
-
- Control data propagation between federated spaces
|
|
456
|
-
- Manage notification settings for each space
|
|
457
|
-
|
|
458
|
-
### Message Federation
|
|
459
|
-
- Track messages across federated spaces
|
|
460
|
-
- Maintain message relationships between original and federated copies
|
|
461
|
-
- Update messages consistently across all federated spaces
|
|
462
|
-
|
|
463
|
-
## API Reference
|
|
464
|
-
|
|
465
|
-
### Space Federation
|
|
466
|
-
|
|
467
|
-
#### `federate(spaceId1, spaceId2, password1, password2, bidirectional)`
|
|
468
|
-
Creates a federation relationship between two spaces.
|
|
269
|
+
## Authentication & Cryptography
|
|
469
270
|
|
|
271
|
+
### Key Management
|
|
470
272
|
```javascript
|
|
471
|
-
|
|
472
|
-
|
|
273
|
+
// Derive public key from private key
|
|
274
|
+
const pubKey = hs.getPublicKey(privateKey);
|
|
473
275
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
- space2.notify includes space1
|
|
276
|
+
// Sign content
|
|
277
|
+
const signature = await hs.sign(content, privateKey);
|
|
477
278
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
- `password1`: Optional password for first space
|
|
482
|
-
- `password2`: Optional password for second space
|
|
483
|
-
- `bidirectional`: Whether to set up bidirectional notifications (default: true, but generally not needed)
|
|
484
|
-
|
|
485
|
-
### Data Propagation
|
|
279
|
+
// Verify signature
|
|
280
|
+
const isValid = await hs.verify(content, signature, pubKey);
|
|
281
|
+
```
|
|
486
282
|
|
|
487
|
-
|
|
488
|
-
|
|
283
|
+
### Capability Tokens
|
|
284
|
+
Fine-grained permission control with scope-based access:
|
|
489
285
|
|
|
490
286
|
```javascript
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
})
|
|
287
|
+
// Issue a capability token
|
|
288
|
+
const token = await hs.issueCapability(
|
|
289
|
+
['read', 'write'], // Permissions
|
|
290
|
+
{ holonId: '*', lensName: 'events' }, // Scope (supports wildcards)
|
|
291
|
+
recipientPubKey, // Recipient
|
|
292
|
+
{ expiresIn: 3600000 } // Options (1 hour expiry)
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
// Verify a capability token
|
|
296
|
+
const result = await hs.verifyCapability(
|
|
297
|
+
token,
|
|
298
|
+
'write', // Required permission
|
|
299
|
+
{ holonId: holonId, lensName: 'events' } // Requested scope
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
if (result.valid) {
|
|
303
|
+
// Authorized
|
|
304
|
+
}
|
|
495
305
|
```
|
|
496
306
|
|
|
497
|
-
|
|
498
|
-
-
|
|
499
|
-
- `
|
|
500
|
-
- `
|
|
501
|
-
-
|
|
307
|
+
**Capability Features:**
|
|
308
|
+
- JWT-like base64 encoding
|
|
309
|
+
- Permissions: `read`, `write`, `delete`
|
|
310
|
+
- Scope wildcards: `{ holonId: "*", lensName: "*" }`
|
|
311
|
+
- Automatic expiration
|
|
312
|
+
- Cryptographic signing with secp256k1
|
|
502
313
|
|
|
503
|
-
|
|
314
|
+
## API Reference
|
|
315
|
+
|
|
316
|
+
### Configuration Options
|
|
504
317
|
|
|
505
318
|
```javascript
|
|
506
|
-
|
|
507
|
-
|
|
319
|
+
const hs = new HoloSphere({
|
|
320
|
+
appName: 'myapp', // Application namespace (default: 'holosphere')
|
|
321
|
+
relays: [ // Nostr relay servers
|
|
322
|
+
'wss://relay.damus.io',
|
|
323
|
+
'wss://nos.lol'
|
|
324
|
+
],
|
|
325
|
+
privateKey: 'hex...', // secp256k1 private key (auto-generated if omitted)
|
|
326
|
+
logLevel: 'WARN', // 'ERROR' | 'WARN' | 'INFO' | 'DEBUG'
|
|
327
|
+
hybridMode: true, // Query both local cache and relays (default: true)
|
|
328
|
+
enableReconnect: true, // Auto-reconnect to relays (default: true)
|
|
329
|
+
enablePing: true, // Keep-alive pings (default: true)
|
|
330
|
+
|
|
331
|
+
// Local-first options
|
|
332
|
+
persistence: true, // Enable persistent storage (default: true)
|
|
333
|
+
dataDir: './data', // Storage directory for persistence
|
|
334
|
+
backgroundSync: true, // Enable SyncService (default: true)
|
|
335
|
+
syncInterval: 10000, // Background sync interval in ms (default: 10000)
|
|
336
|
+
maxRetries: 5, // Max retry attempts for failed writes (default: 5)
|
|
337
|
+
retryBaseDelay: 1000, // Base delay for exponential backoff (default: 1000ms)
|
|
338
|
+
retryMaxDelay: 60000, // Max delay cap for retries (default: 60000ms)
|
|
339
|
+
failedTTL: 86400000, // TTL for failed events before purge (default: 24h)
|
|
508
340
|
});
|
|
509
341
|
```
|
|
510
342
|
|
|
511
|
-
###
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
343
|
+
### Spatial Operations
|
|
344
|
+
| Method | Description |
|
|
345
|
+
|--------|-------------|
|
|
346
|
+
| `toHolon(lat, lng, resolution)` | Convert coordinates to H3 holon ID |
|
|
347
|
+
| `getParents(holonId, maxResolution?)` | Get parent holons up hierarchy |
|
|
348
|
+
| `getChildren(holonId)` | Get 7 child holons at next resolution |
|
|
349
|
+
| `isValidH3(holonId)` | Validate H3 format |
|
|
350
|
+
|
|
351
|
+
### Data Operations
|
|
352
|
+
| Method | Description |
|
|
353
|
+
|--------|-------------|
|
|
354
|
+
| `write(holonId, lensName, data, options?)` | Write data to lens |
|
|
355
|
+
| `read(holonId, lensName, dataId?)` | Read data (single or all) |
|
|
356
|
+
| `update(holonId, lensName, dataId, updates, options?)` | Partial update |
|
|
357
|
+
| `delete(holonId, lensName, dataId, options?)` | Delete data |
|
|
358
|
+
| `getAll(holonId, lensName)` | Get all data in lens |
|
|
359
|
+
|
|
360
|
+
### Global Table Operations
|
|
361
|
+
| Method | Description |
|
|
362
|
+
|--------|-------------|
|
|
363
|
+
| `writeGlobal(table, data)` | Write non-location-specific data |
|
|
364
|
+
| `readGlobal(table, key?)` | Read global data |
|
|
365
|
+
| `updateGlobal(table, key, updates)` | Update global data |
|
|
366
|
+
| `deleteGlobal(table, key)` | Delete global data |
|
|
367
|
+
| `getAllGlobal(table)` | Get all data in table |
|
|
368
|
+
| `deleteAllGlobal(table)` | Clear entire table |
|
|
369
|
+
|
|
370
|
+
### Hologram Operations
|
|
371
|
+
| Method | Description |
|
|
372
|
+
|--------|-------------|
|
|
373
|
+
| `createHologram(sourceHolon, sourceLens, sourceId, targetHolon)` | Create reference |
|
|
374
|
+
| `resolveHologram(hologram)` | Resolve to actual data with overrides |
|
|
375
|
+
| `updateHologramOverrides(holon, lens, id, overrides)` | Update local fields |
|
|
376
|
+
| `deleteHologram(holon, lens, id)` | Delete with cleanup |
|
|
377
|
+
| `getActiveHolograms(holon, lens, id)` | Get all refs to data |
|
|
378
|
+
| `cleanupCircularHolograms(holon, lens)` | Remove circular refs |
|
|
379
|
+
|
|
380
|
+
### Federation Operations
|
|
381
|
+
| Method | Description |
|
|
382
|
+
|--------|-------------|
|
|
383
|
+
| `federate(source, target, lens, options?)` | Establish federation |
|
|
384
|
+
| `federateHolon(source, target, options?)` | Holon-level federation |
|
|
385
|
+
| `getFederatedData(holonId, lens, options?)` | Query federated data |
|
|
386
|
+
| `unfederate(source, target, lens)` | Remove federation |
|
|
387
|
+
| `propagateData(data, source, target, lens)` | Propagate to target |
|
|
388
|
+
|
|
389
|
+
### Cross-Holosphere Operations
|
|
390
|
+
| Method | Description |
|
|
391
|
+
|--------|-------------|
|
|
392
|
+
| `addFederatedHolosphere(pubKey, options)` | Add partner instance |
|
|
393
|
+
| `removeFederatedHolosphere(pubKey)` | Remove partner |
|
|
394
|
+
| `getFederatedHolospheres()` | List all partners |
|
|
395
|
+
| `getFederationRegistry()` | Get full registry |
|
|
396
|
+
| `issueCapabilityForFederation(pubKey, options)` | Grant access |
|
|
397
|
+
| `storeInboundCapability(pubKey, token)` | Store received token |
|
|
398
|
+
| `readFromFederatedSource(pubKey, holon, lens, id)` | Read remote data |
|
|
399
|
+
| `createCrossHolosphereHologram(pubKey, holon, lens, id, target)` | Remote reference |
|
|
400
|
+
|
|
401
|
+
### Discovery Protocol Operations
|
|
402
|
+
| Method | Description |
|
|
403
|
+
|--------|-------------|
|
|
404
|
+
| `sendFederationRequest(targetPubKey, options)` | Propose federation |
|
|
405
|
+
| `subscribeFederationRequests(callback)` | Listen for requests |
|
|
406
|
+
| `acceptFederationRequest(request, options)` | Accept proposal |
|
|
407
|
+
| `declineFederationRequest(request, reason)` | Decline proposal |
|
|
408
|
+
| `subscribeFederationAcceptances(callback)` | Track acceptances |
|
|
409
|
+
| `subscribeFederationDeclines(callback)` | Track declines |
|
|
410
|
+
| `getPendingFederationRequests(options)` | Query pending |
|
|
411
|
+
|
|
412
|
+
### Schema Operations
|
|
413
|
+
| Method | Description |
|
|
414
|
+
|--------|-------------|
|
|
415
|
+
| `setSchema(lensName, schema, strict?)` | Define JSON Schema validation |
|
|
416
|
+
| `getSchema(lensName)` | Retrieve schema |
|
|
417
|
+
| `clearSchema(lensName)` | Remove schema |
|
|
418
|
+
|
|
419
|
+
### Subscription Operations
|
|
420
|
+
| Method | Description |
|
|
421
|
+
|--------|-------------|
|
|
422
|
+
| `subscribe(holonId, lens, callback, options?)` | Real-time updates |
|
|
423
|
+
| `subscribeGlobal(table, key, callback)` | Global table subscriptions |
|
|
424
|
+
|
|
425
|
+
**Subscription Options:**
|
|
426
|
+
- `throttle`: Minimum ms between callbacks
|
|
427
|
+
- `filter`: Function to filter events
|
|
428
|
+
- `resolveHolograms`: Auto-resolve hologram references (default: true)
|
|
429
|
+
- `realtimeOnly`: Skip historical data
|
|
430
|
+
|
|
431
|
+
### Cryptographic Operations
|
|
432
|
+
| Method | Description |
|
|
433
|
+
|--------|-------------|
|
|
434
|
+
| `getPublicKey(privateKey)` | Derive public key |
|
|
435
|
+
| `sign(content, privateKey)` | Sign content |
|
|
436
|
+
| `verify(content, signature, publicKey)` | Verify signature |
|
|
437
|
+
| `issueCapability(permissions, scope, recipient, options?)` | Issue token |
|
|
438
|
+
| `verifyCapability(token, permission, scope)` | Verify token |
|
|
439
|
+
|
|
440
|
+
### Social Protocol Operations
|
|
441
|
+
| Method | Description |
|
|
442
|
+
|--------|-------------|
|
|
443
|
+
| `publishNostr(event, holonId, lens?)` | Publish Nostr NIP-01 event |
|
|
444
|
+
| `publishActivityPub(object, holonId, lens?)` | Publish ActivityPub object |
|
|
445
|
+
| `querySocial(holonId, options?)` | Query with protocol/access filtering |
|
|
446
|
+
| `verifyNostrEvent(event)` | Verify event signature |
|
|
447
|
+
|
|
448
|
+
### Hierarchical Operations
|
|
449
|
+
| Method | Description |
|
|
450
|
+
|--------|-------------|
|
|
451
|
+
| `upcast(holonId, lens, dataId, options?)` | Propagate to parent holons |
|
|
452
|
+
|
|
453
|
+
**Upcast Operations:**
|
|
454
|
+
- `summarize`: Condense data to summary
|
|
455
|
+
- `aggregate`: Combine multiple items
|
|
456
|
+
- `concatenate`: Combine all data
|
|
457
|
+
|
|
458
|
+
### Utility Operations
|
|
459
|
+
| Method | Description |
|
|
460
|
+
|--------|-------------|
|
|
461
|
+
| `metrics()` | Get operation counts and stats |
|
|
462
|
+
|
|
463
|
+
## Architecture
|
|
464
|
+
|
|
465
|
+
HoloSphere follows constitutional principles:
|
|
466
|
+
|
|
467
|
+
- **Spatial-First**: H3 as universal spatial reference (resolutions 0-15)
|
|
468
|
+
- **Decentralization**: P2P architecture via Nostr relays, no central authority
|
|
469
|
+
- **Single Source of Truth**: Holograms (references) instead of data duplication
|
|
470
|
+
- **Lens-Based Organization**: Independent schemas and federation per category
|
|
471
|
+
- **Real-time Reactive**: Built-in subscriptions with throttling and filtering
|
|
472
|
+
- **Cryptographic Security**: secp256k1 signatures, capability tokens with scopes
|
|
473
|
+
- **Cross-Instance Federation**: Multi-holosphere partnerships via public keys
|
|
474
|
+
- **Schema Flexibility**: Optional strict JSON Schema validation
|
|
475
|
+
- **Hybrid Queries**: Combined local cache + relay queries for performance
|
|
476
|
+
|
|
477
|
+
### Data Model
|
|
515
478
|
|
|
516
|
-
```javascript
|
|
517
|
-
await holosphere.federateMessage('chat1', 'msg1', 'chat2', 'msg2', 'quest');
|
|
518
479
|
```
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
480
|
+
appName/
|
|
481
|
+
├── holonId/ # Geographic (H3) or noospheric ID
|
|
482
|
+
│ └── lensName/ # Data category
|
|
483
|
+
│ └── dataId # Individual data item
|
|
484
|
+
│ ├── ...data fields
|
|
485
|
+
│ └── _meta/ # Metadata
|
|
486
|
+
│ ├── timestamp
|
|
487
|
+
│ ├── creator
|
|
488
|
+
│ ├── source # Original location (for copies)
|
|
489
|
+
│ └── activeHolograms[] # Tracking refs
|
|
490
|
+
└── _global/ # Non-spatial data
|
|
491
|
+
└── tableName/
|
|
492
|
+
└── key
|
|
525
493
|
```
|
|
526
494
|
|
|
527
|
-
|
|
528
|
-
Updates a message across all federated chats.
|
|
495
|
+
### Hologram Structure
|
|
529
496
|
|
|
530
497
|
```javascript
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
498
|
+
{
|
|
499
|
+
id: 'hologram-uuid',
|
|
500
|
+
_isHologram: true,
|
|
501
|
+
soul: 'appname/sourceHolon/lens/dataId', // Source path
|
|
502
|
+
sourceHolonId: 'source-holon',
|
|
503
|
+
sourceLensName: 'events',
|
|
504
|
+
sourceDataId: 'original-id',
|
|
505
|
+
|
|
506
|
+
// Cross-holosphere (optional)
|
|
507
|
+
authorPubKey: 'source-pubkey',
|
|
508
|
+
embeddedCapability: 'base64-token',
|
|
509
|
+
|
|
510
|
+
// Local overrides
|
|
511
|
+
x: 100,
|
|
512
|
+
y: 200,
|
|
513
|
+
pinned: true
|
|
514
|
+
}
|
|
534
515
|
```
|
|
535
516
|
|
|
536
|
-
##
|
|
517
|
+
## Development
|
|
537
518
|
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
// Store data in space1
|
|
543
|
-
const data = { id: 'item1', value: 42 };
|
|
544
|
-
await holosphere.put('space1', 'items', data);
|
|
545
|
-
|
|
546
|
-
// Propagate to federated spaces
|
|
547
|
-
await holosphere.propagate('space1', 'items', data);
|
|
548
|
-
|
|
549
|
-
// Retrieve data from space2 (will resolve the reference)
|
|
550
|
-
const item = await holosphere.get('space2', 'items', 'item1');
|
|
551
|
-
|
|
552
|
-
// Track a federated message
|
|
553
|
-
await holosphere.federateMessage('chat1', 'msg1', 'chat2', 'msg2', 'quest');
|
|
554
|
-
|
|
555
|
-
// Update message across all federated chats
|
|
556
|
-
await holosphere.updateFederatedMessages('chat1', 'msg1', async (chatId, messageId) => {
|
|
557
|
-
await updateMessageInChat(chatId, messageId);
|
|
558
|
-
});
|
|
559
|
-
```
|
|
519
|
+
```bash
|
|
520
|
+
# Install dependencies
|
|
521
|
+
npm install
|
|
560
522
|
|
|
561
|
-
|
|
523
|
+
# Run tests
|
|
524
|
+
npm test
|
|
562
525
|
|
|
563
|
-
|
|
526
|
+
# Watch mode
|
|
527
|
+
npm run test:watch
|
|
564
528
|
|
|
565
|
-
|
|
529
|
+
# Coverage
|
|
530
|
+
npm run test:coverage
|
|
566
531
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
sphere.configureRadisk({
|
|
570
|
-
file: './my-data', // Storage directory
|
|
571
|
-
radisk: true, // Enable disk persistence
|
|
572
|
-
retry: 3, // Retry failed operations
|
|
573
|
-
timeout: 5000 // Operation timeout in ms
|
|
574
|
-
});
|
|
575
|
-
```
|
|
532
|
+
# Build
|
|
533
|
+
npm run build
|
|
576
534
|
|
|
577
|
-
|
|
535
|
+
# Lint
|
|
536
|
+
npm run lint
|
|
578
537
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
console.log(stats);
|
|
582
|
-
// Output:
|
|
583
|
-
// {
|
|
584
|
-
// enabled: true,
|
|
585
|
-
// filePath: './my-data',
|
|
586
|
-
// retry: 3,
|
|
587
|
-
// timeout: 5000,
|
|
588
|
-
// until: null,
|
|
589
|
-
// peers: ['https://gun.holons.io/gun'],
|
|
590
|
-
// localStorage: false
|
|
591
|
-
// }
|
|
538
|
+
# Format
|
|
539
|
+
npm run format
|
|
592
540
|
```
|
|
593
541
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
- `file`: Directory for storing data files (default: `'./radata'`)
|
|
597
|
-
- `radisk`: Enable/disable disk persistence (default: `true`)
|
|
598
|
-
- `retry`: Number of retries for failed operations (default: `3`)
|
|
599
|
-
- `timeout`: Operation timeout in milliseconds (default: `5000`)
|
|
600
|
-
- `until`: Timestamp until which to keep data (optional)
|
|
542
|
+
## Testing
|
|
601
543
|
|
|
602
|
-
|
|
544
|
+
- **375+ tests**: Contract, integration, and unit tests
|
|
545
|
+
- TDD approach: Tests written before implementation
|
|
603
546
|
|
|
604
|
-
|
|
547
|
+
## Error Handling
|
|
605
548
|
|
|
606
549
|
```javascript
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
550
|
+
import { ValidationError, AuthorizationError, HolosphereError } from 'holosphere';
|
|
551
|
+
|
|
552
|
+
try {
|
|
553
|
+
await hs.write(holonId, 'events', invalidData);
|
|
554
|
+
} catch (error) {
|
|
555
|
+
if (error instanceof ValidationError) {
|
|
556
|
+
console.log('Schema validation failed:', error.message);
|
|
557
|
+
} else if (error instanceof AuthorizationError) {
|
|
558
|
+
console.log('Permission denied:', error.message);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
616
561
|
```
|
|
617
562
|
|
|
618
|
-
##
|
|
563
|
+
## License
|
|
619
564
|
|
|
620
|
-
|
|
565
|
+
MIT
|
|
621
566
|
|
|
622
|
-
|
|
623
|
-
2. The reference contains the original item's ID and soul path
|
|
624
|
-
3. When accessed, the reference is automatically resolved to the original data
|
|
625
|
-
4. Changes to the original data are immediately visible through references
|
|
567
|
+
## Links
|
|
626
568
|
|
|
627
|
-
|
|
569
|
+
- [Local-First Architecture](./LOCALFIRST.md)
|
|
570
|
+
- [FOSDEM Proposal](./FOSDEM_PROPOSAL.md)
|
|
571
|
+
- [Full API Documentation](./specs/001-holosphere-holonic-geospatial/contracts/api-interface.md)
|
|
572
|
+
- [Quickstart Guide](./specs/001-holosphere-holonic-geospatial/quickstart.md)
|
|
573
|
+
- [Implementation Plan](./specs/001-holosphere-holonic-geospatial/plan.md)
|
|
574
|
+
- [Data Model](./specs/001-holosphere-holonic-geospatial/data-model.md)
|
|
575
|
+
- [Constitution](./specify/memory/constitution.md)
|
|
628
576
|
|
|
629
|
-
##
|
|
577
|
+
## Contributing
|
|
630
578
|
|
|
631
|
-
|
|
579
|
+
Contributions welcome! Please ensure:
|
|
580
|
+
1. All tests pass (`npm test`)
|
|
581
|
+
2. Code is formatted (`npm run format`)
|
|
582
|
+
3. Linting passes (`npm run lint`)
|
|
583
|
+
4. Constitutional principles are followed
|
|
632
584
|
|
|
633
|
-
|
|
634
|
-
{
|
|
635
|
-
id: string,
|
|
636
|
-
name: string,
|
|
637
|
-
federation: string[], // Source spaces this holon federates with
|
|
638
|
-
notify: string[], // Target spaces to notify of changes
|
|
639
|
-
timestamp: number
|
|
640
|
-
}
|
|
641
|
-
```
|
|
585
|
+
---
|
|
642
586
|
|
|
587
|
+
Generated with [Claude Code](https://claude.com/claude-code)
|