holosphere 1.1.20 → 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 +483 -367
- 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 -980
- 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 -33256
- package/holosphere-bundle.js +0 -33287
- package/holosphere-bundle.min.js +0 -39
- package/holosphere.d.ts +0 -601
- package/holosphere.js +0 -719
- package/node.js +0 -246
- package/schema.js +0 -139
- package/utils.js +0 -302
package/README.md
CHANGED
|
@@ -1,471 +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
|
-
- Regional data aggregation
|
|
72
|
-
- Cross-boundary collaboration
|
|
73
|
-
- Distributed decision making
|
|
74
|
-
- Resource flow tracking
|
|
76
|
+
## Local-First Architecture
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
// Example: Regional resource coordination
|
|
78
|
-
async function coordinateResources(region) {
|
|
79
|
-
// Get all sub-holons
|
|
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
|
-
}
|
|
92
|
-
```
|
|
78
|
+
HoloSphere implements true local-first principles: your data lives on your device first, with Nostr relays serving as the sync layer.
|
|
93
79
|
|
|
94
|
-
|
|
95
|
-
- Adaptive governance systems
|
|
96
|
-
- Scalable social networks
|
|
97
|
-
- Emergency response coordination
|
|
98
|
-
- Supply chain management
|
|
80
|
+
### How It Works
|
|
99
81
|
|
|
82
|
+
**Reads are instant:**
|
|
100
83
|
```javascript
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const region = h3.cellToParent(epicenter, 6);
|
|
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
|
-
}
|
|
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');
|
|
121
87
|
```
|
|
122
88
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
5. **Adaptability**: Structures can evolve based on changing needs
|
|
130
|
-
|
|
131
|
-
## Installation
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
npm install holosphere
|
|
89
|
+
**Writes are guaranteed:**
|
|
90
|
+
```javascript
|
|
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
|
|
135
95
|
```
|
|
136
96
|
|
|
137
|
-
|
|
97
|
+
### Offline Behavior
|
|
138
98
|
|
|
139
|
-
|
|
140
|
-
|
|
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 |
|
|
141
104
|
|
|
142
|
-
|
|
143
|
-
const sphere = new HoloSphere('my-app');
|
|
105
|
+
### Configuration
|
|
144
106
|
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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)
|
|
151
118
|
});
|
|
152
119
|
```
|
|
153
120
|
|
|
154
|
-
|
|
121
|
+
See [LOCALFIRST.md](./LOCALFIRST.md) for detailed architecture documentation.
|
|
155
122
|
|
|
156
|
-
|
|
123
|
+
## Core Concepts
|
|
157
124
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const tempSchema = {
|
|
161
|
-
type: 'object',
|
|
162
|
-
properties: {
|
|
163
|
-
id: { type: 'string' },
|
|
164
|
-
temperature: { type: 'number' },
|
|
165
|
-
humidity: { type: 'number' },
|
|
166
|
-
timestamp: { type: 'number' }
|
|
167
|
-
},
|
|
168
|
-
required: ['id', 'temperature', 'timestamp']
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
// Set up the system
|
|
172
|
-
const sphere = new HoloSphere('env-monitor', true); // strict mode enabled
|
|
173
|
-
await sphere.setSchema('temperature', tempSchema);
|
|
174
|
-
|
|
175
|
-
// Store a reading
|
|
176
|
-
async function storeReading(lat, lng, temp, humidity) {
|
|
177
|
-
const holon = await sphere.getHolon(lat, lng, 8);
|
|
178
|
-
const reading = {
|
|
179
|
-
id: `temp-${Date.now()}`,
|
|
180
|
-
temperature: temp,
|
|
181
|
-
humidity: humidity,
|
|
182
|
-
timestamp: Date.now()
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
return sphere.put(holon, 'temperature', reading);
|
|
186
|
-
}
|
|
125
|
+
### Holons
|
|
126
|
+
Locations in either **geographic space** (H3 hexagonal cells) or **noospheric space** (URI-identified concepts):
|
|
187
127
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
128
|
+
```javascript
|
|
129
|
+
// Geographic holon (H3 cell at resolution 9)
|
|
130
|
+
const geoHolon = await hs.toHolon(37.7749, -122.4194, 9);
|
|
131
|
+
// => "8928342e20fffff"
|
|
193
132
|
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
const holon = sphere.getHolon(lat, lng, 8);
|
|
197
|
-
sphere.subscribe(holon, 'temperature', callback);
|
|
198
|
-
}
|
|
133
|
+
// Noospheric holon (arbitrary string ID)
|
|
134
|
+
const conceptHolon = "topic/bitcoin";
|
|
199
135
|
```
|
|
200
136
|
|
|
201
|
-
###
|
|
137
|
+
### Lenses
|
|
138
|
+
Categorical data collections within a holon:
|
|
202
139
|
|
|
203
140
|
```javascript
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
type: 'object',
|
|
210
|
-
properties: {
|
|
211
|
-
id: { type: 'string' },
|
|
212
|
-
title: { type: 'string' },
|
|
213
|
-
content: { type: 'string' },
|
|
214
|
-
author: { type: 'string' },
|
|
215
|
-
timestamp: { type: 'number' }
|
|
216
|
-
},
|
|
217
|
-
required: ['id', 'title', 'content', 'author']
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
await sphere.setSchema('articles', contentSchema);
|
|
221
|
-
|
|
222
|
-
// Post content for a location
|
|
223
|
-
async function postLocalContent(lat, lng, title, content, author) {
|
|
224
|
-
const holon = await sphere.getHolon(lat, lng, 7);
|
|
225
|
-
const article = {
|
|
226
|
-
id: `article-${Date.now()}`,
|
|
227
|
-
title,
|
|
228
|
-
content,
|
|
229
|
-
author,
|
|
230
|
-
timestamp: Date.now()
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
await sphere.put(holon, 'articles', article);
|
|
234
|
-
|
|
235
|
-
// Generate summaries for parent areas
|
|
236
|
-
await sphere.compute(holon, 'articles', 'summarize');
|
|
237
|
-
}
|
|
141
|
+
await hs.write(holonId, 'events', {
|
|
142
|
+
id: 'concert-001',
|
|
143
|
+
name: 'Jazz Night',
|
|
144
|
+
venue: 'Blue Note SF'
|
|
145
|
+
});
|
|
238
146
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const content = {};
|
|
245
|
-
for (const scale of scales) {
|
|
246
|
-
content[scale] = await sphere.getAll(scale, 'articles');
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return content;
|
|
250
|
-
}
|
|
147
|
+
await hs.write(holonId, 'sensors', {
|
|
148
|
+
id: 'temp-001',
|
|
149
|
+
value: 22.5,
|
|
150
|
+
unit: 'celsius'
|
|
151
|
+
});
|
|
251
152
|
```
|
|
252
153
|
|
|
253
|
-
###
|
|
154
|
+
### Holograms
|
|
155
|
+
Lightweight references to data that can exist in multiple locations while maintaining a single source of truth:
|
|
254
156
|
|
|
255
157
|
```javascript
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
additionalProperties: false
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
await sphere.setSchema('measurements', measurementSchema);
|
|
273
|
-
|
|
274
|
-
// This will succeed
|
|
275
|
-
await sphere.put(holon, 'measurements', {
|
|
276
|
-
id: 'measure-1',
|
|
277
|
-
value: 42.5,
|
|
278
|
-
unit: 'celsius',
|
|
279
|
-
accuracy: 0.1,
|
|
280
|
-
timestamp: Date.now()
|
|
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
|
|
281
171
|
});
|
|
282
172
|
|
|
283
|
-
//
|
|
284
|
-
await
|
|
285
|
-
|
|
286
|
-
value: "invalid", // wrong type
|
|
287
|
-
extra: "field" // not allowed
|
|
288
|
-
});
|
|
173
|
+
// Resolution merges source data with local overrides
|
|
174
|
+
const resolved = await hs.resolveHologram(hologram);
|
|
175
|
+
// => { ...sourceData, x: 100, y: 200, pinned: true }
|
|
289
176
|
```
|
|
290
177
|
|
|
291
|
-
|
|
178
|
+
### Federation (Within Holosphere)
|
|
179
|
+
Bidirectional data sharing between holons:
|
|
292
180
|
|
|
293
|
-
### Constructor
|
|
294
181
|
```javascript
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
+
});
|
|
191
|
+
|
|
192
|
+
// Query regional holon sees local data via holograms
|
|
193
|
+
const events = await hs.getFederatedData(regionalHolon, 'events');
|
|
300
194
|
```
|
|
301
195
|
|
|
302
|
-
###
|
|
196
|
+
### Cross-Holosphere Federation
|
|
197
|
+
Multi-instance partnerships with capability token exchange:
|
|
303
198
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
- `async setSchema(lens, schema)` - Set JSON schema for validation
|
|
311
|
-
- `async getSchema(lens)` - Get current schema
|
|
312
|
-
- `subscribe(holon, lens, callback)` - Listen for changes
|
|
199
|
+
```javascript
|
|
200
|
+
// Add a federated partner by their public key
|
|
201
|
+
await hs.addFederatedHolosphere(partnerPubKey, {
|
|
202
|
+
name: 'Partner Holosphere',
|
|
203
|
+
permissions: ['read', 'write']
|
|
204
|
+
});
|
|
313
205
|
|
|
314
|
-
|
|
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
|
+
});
|
|
315
212
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
+
);
|
|
229
|
+
```
|
|
320
230
|
|
|
321
|
-
|
|
231
|
+
### Discovery Protocol (Nostr-Based)
|
|
232
|
+
Automated federation handshake via Nostr events:
|
|
322
233
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
234
|
+
```javascript
|
|
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
|
+
});
|
|
327
241
|
|
|
328
|
-
|
|
242
|
+
// Listen for incoming federation requests
|
|
243
|
+
hs.subscribeFederationRequests((request) => {
|
|
244
|
+
console.log('Federation request from:', request.authorPubKey);
|
|
329
245
|
|
|
330
|
-
|
|
246
|
+
// Accept the request
|
|
247
|
+
hs.acceptFederationRequest(request, {
|
|
248
|
+
grantedPermissions: ['read'],
|
|
249
|
+
grantedScopes: [{ holonId: '*', lensName: 'events' }]
|
|
250
|
+
});
|
|
251
|
+
});
|
|
331
252
|
|
|
332
|
-
|
|
253
|
+
// Subscribe to acceptance/decline responses
|
|
254
|
+
hs.subscribeFederationAcceptances((acceptance) => {
|
|
255
|
+
console.log('Federation accepted by:', acceptance.authorPubKey);
|
|
256
|
+
});
|
|
257
|
+
```
|
|
333
258
|
|
|
334
|
-
|
|
259
|
+
### Hierarchical Aggregation
|
|
260
|
+
Automatic propagation to parent holons:
|
|
335
261
|
|
|
336
|
-
|
|
262
|
+
```javascript
|
|
263
|
+
await hs.upcast(localHolon, 'sensors', 'temp-001', {
|
|
264
|
+
maxLevel: 3, // Propagate 3 levels up hierarchy
|
|
265
|
+
operation: 'summarize' // or 'aggregate', 'concatenate'
|
|
266
|
+
});
|
|
267
|
+
```
|
|
337
268
|
|
|
338
|
-
|
|
339
|
-
- Create relationships between spaces with clear source-target connections
|
|
340
|
-
- Use soul references to maintain a single source of truth
|
|
341
|
-
- Control data propagation between federated spaces
|
|
342
|
-
- Manage notification settings for each space
|
|
269
|
+
## Authentication & Cryptography
|
|
343
270
|
|
|
344
|
-
###
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
271
|
+
### Key Management
|
|
272
|
+
```javascript
|
|
273
|
+
// Derive public key from private key
|
|
274
|
+
const pubKey = hs.getPublicKey(privateKey);
|
|
348
275
|
|
|
349
|
-
|
|
276
|
+
// Sign content
|
|
277
|
+
const signature = await hs.sign(content, privateKey);
|
|
350
278
|
|
|
351
|
-
|
|
279
|
+
// Verify signature
|
|
280
|
+
const isValid = await hs.verify(content, signature, pubKey);
|
|
281
|
+
```
|
|
352
282
|
|
|
353
|
-
|
|
354
|
-
|
|
283
|
+
### Capability Tokens
|
|
284
|
+
Fine-grained permission control with scope-based access:
|
|
355
285
|
|
|
356
286
|
```javascript
|
|
357
|
-
|
|
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
|
+
}
|
|
358
305
|
```
|
|
359
306
|
|
|
360
|
-
|
|
361
|
-
-
|
|
362
|
-
-
|
|
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
|
|
363
313
|
|
|
364
|
-
|
|
365
|
-
- `spaceId1`: First space ID (source space)
|
|
366
|
-
- `spaceId2`: Second space ID (target space)
|
|
367
|
-
- `password1`: Optional password for first space
|
|
368
|
-
- `password2`: Optional password for second space
|
|
369
|
-
- `bidirectional`: Whether to set up bidirectional notifications (default: true, but generally not needed)
|
|
370
|
-
|
|
371
|
-
### Data Propagation
|
|
314
|
+
## API Reference
|
|
372
315
|
|
|
373
|
-
|
|
374
|
-
Propagates data to federated spaces.
|
|
316
|
+
### Configuration Options
|
|
375
317
|
|
|
376
318
|
```javascript
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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)
|
|
380
340
|
});
|
|
381
341
|
```
|
|
382
342
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
|
390
478
|
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
479
|
+
```
|
|
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
|
|
395
493
|
```
|
|
396
494
|
|
|
397
|
-
###
|
|
398
|
-
|
|
399
|
-
#### `federateMessage(originalChatId, messageId, federatedChatId, federatedMessageId, type)`
|
|
400
|
-
Tracks a federated message across different chats.
|
|
495
|
+
### Hologram Structure
|
|
401
496
|
|
|
402
497
|
```javascript
|
|
403
|
-
|
|
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
|
+
}
|
|
404
515
|
```
|
|
405
516
|
|
|
406
|
-
|
|
407
|
-
Gets all federated messages for a given original message.
|
|
517
|
+
## Development
|
|
408
518
|
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
|
|
519
|
+
```bash
|
|
520
|
+
# Install dependencies
|
|
521
|
+
npm install
|
|
412
522
|
|
|
413
|
-
|
|
414
|
-
|
|
523
|
+
# Run tests
|
|
524
|
+
npm test
|
|
415
525
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
// Update message in this chat
|
|
419
|
-
});
|
|
420
|
-
```
|
|
526
|
+
# Watch mode
|
|
527
|
+
npm run test:watch
|
|
421
528
|
|
|
422
|
-
|
|
529
|
+
# Coverage
|
|
530
|
+
npm run test:coverage
|
|
423
531
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
await holosphere.federate('space1', 'space2');
|
|
532
|
+
# Build
|
|
533
|
+
npm run build
|
|
427
534
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
await holosphere.put('space1', 'items', data);
|
|
535
|
+
# Lint
|
|
536
|
+
npm run lint
|
|
431
537
|
|
|
432
|
-
|
|
433
|
-
|
|
538
|
+
# Format
|
|
539
|
+
npm run format
|
|
540
|
+
```
|
|
434
541
|
|
|
435
|
-
|
|
436
|
-
const item = await holosphere.get('space2', 'items', 'item1');
|
|
542
|
+
## Testing
|
|
437
543
|
|
|
438
|
-
|
|
439
|
-
|
|
544
|
+
- **375+ tests**: Contract, integration, and unit tests
|
|
545
|
+
- TDD approach: Tests written before implementation
|
|
440
546
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
547
|
+
## Error Handling
|
|
548
|
+
|
|
549
|
+
```javascript
|
|
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
|
+
}
|
|
445
561
|
```
|
|
446
562
|
|
|
447
|
-
##
|
|
563
|
+
## License
|
|
448
564
|
|
|
449
|
-
|
|
565
|
+
MIT
|
|
450
566
|
|
|
451
|
-
|
|
452
|
-
2. The reference contains the original item's ID and soul path
|
|
453
|
-
3. When accessed, the reference is automatically resolved to the original data
|
|
454
|
-
4. Changes to the original data are immediately visible through references
|
|
567
|
+
## Links
|
|
455
568
|
|
|
456
|
-
|
|
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)
|
|
457
576
|
|
|
458
|
-
##
|
|
577
|
+
## Contributing
|
|
459
578
|
|
|
460
|
-
|
|
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
|
|
461
584
|
|
|
462
|
-
|
|
463
|
-
{
|
|
464
|
-
id: string,
|
|
465
|
-
name: string,
|
|
466
|
-
federation: string[], // Source spaces this holon federates with
|
|
467
|
-
notify: string[], // Target spaces to notify of changes
|
|
468
|
-
timestamp: number
|
|
469
|
-
}
|
|
470
|
-
```
|
|
585
|
+
---
|
|
471
586
|
|
|
587
|
+
Generated with [Claude Code](https://claude.com/claude-code)
|