holosphere 2.0.0-alpha0 → 2.0.0-alpha2
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/LICENSE +162 -38
- package/dist/cjs/holosphere.cjs +2 -0
- package/dist/cjs/holosphere.cjs.map +1 -0
- package/dist/esm/holosphere.js +56 -0
- package/dist/esm/holosphere.js.map +1 -0
- package/dist/index-CDfIuXew.js +15974 -0
- package/dist/index-CDfIuXew.js.map +1 -0
- package/dist/index-ifOgtDvd.cjs +3 -0
- package/dist/index-ifOgtDvd.cjs.map +1 -0
- package/dist/indexeddb-storage-CMW4qRQS.js +96 -0
- package/dist/indexeddb-storage-CMW4qRQS.js.map +1 -0
- package/dist/indexeddb-storage-DLZOgetM.cjs +2 -0
- package/dist/indexeddb-storage-DLZOgetM.cjs.map +1 -0
- package/dist/memory-storage-DQzcAZlf.js +47 -0
- package/dist/memory-storage-DQzcAZlf.js.map +1 -0
- package/dist/memory-storage-DmePEP2q.cjs +2 -0
- package/dist/memory-storage-DmePEP2q.cjs.map +1 -0
- package/dist/secp256k1-CP0ZkpAx.cjs +13 -0
- package/dist/secp256k1-CP0ZkpAx.cjs.map +1 -0
- package/dist/secp256k1-vOXp40Fx.js +2281 -0
- package/dist/secp256k1-vOXp40Fx.js.map +1 -0
- package/docs/FOSDEM_PROPOSAL.md +388 -0
- package/docs/LOCALFIRST.md +266 -0
- package/docs/contracts/api-interface.md +793 -0
- package/docs/data-model.md +476 -0
- package/docs/gun-async-usage.md +338 -0
- package/docs/plan.md +349 -0
- package/docs/quickstart.md +674 -0
- package/docs/research.md +362 -0
- package/docs/spec.md +244 -0
- package/docs/storage-backends.md +326 -0
- package/docs/tasks.md +947 -0
- package/package.json +1 -1
- package/tests/unit/ai/aggregation.test.js +0 -295
- package/tests/unit/ai/breakdown.test.js +0 -446
- package/tests/unit/ai/classifier.test.js +0 -294
- package/tests/unit/ai/council.test.js +0 -262
- package/tests/unit/ai/embeddings.test.js +0 -384
- package/tests/unit/ai/federation-ai.test.js +0 -344
- package/tests/unit/ai/h3-ai.test.js +0 -458
- package/tests/unit/ai/index.test.js +0 -304
- package/tests/unit/ai/json-ops.test.js +0 -307
- package/tests/unit/ai/llm-service.test.js +0 -390
- package/tests/unit/ai/nl-query.test.js +0 -383
- package/tests/unit/ai/relationships.test.js +0 -311
- package/tests/unit/ai/schema-extractor.test.js +0 -384
- package/tests/unit/ai/spatial.test.js +0 -279
- package/tests/unit/ai/tts.test.js +0 -279
- package/tests/unit/content.test.js +0 -332
- package/tests/unit/contract/core.test.js +0 -88
- package/tests/unit/contract/crypto.test.js +0 -198
- package/tests/unit/contract/data.test.js +0 -223
- package/tests/unit/contract/federation.test.js +0 -181
- package/tests/unit/contract/hierarchical.test.js +0 -113
- package/tests/unit/contract/schema.test.js +0 -114
- package/tests/unit/contract/social.test.js +0 -217
- package/tests/unit/contract/spatial.test.js +0 -110
- package/tests/unit/contract/subscriptions.test.js +0 -128
- package/tests/unit/contract/utils.test.js +0 -159
- package/tests/unit/core.test.js +0 -152
- package/tests/unit/crypto.test.js +0 -328
- package/tests/unit/federation.test.js +0 -234
- package/tests/unit/gun-async.test.js +0 -252
- package/tests/unit/hierarchical.test.js +0 -399
- package/tests/unit/integration/scenario-01-geographic-storage.test.js +0 -74
- package/tests/unit/integration/scenario-02-federation.test.js +0 -76
- package/tests/unit/integration/scenario-03-subscriptions.test.js +0 -102
- package/tests/unit/integration/scenario-04-validation.test.js +0 -129
- package/tests/unit/integration/scenario-05-hierarchy.test.js +0 -125
- package/tests/unit/integration/scenario-06-social.test.js +0 -135
- package/tests/unit/integration/scenario-07-persistence.test.js +0 -130
- package/tests/unit/integration/scenario-08-authorization.test.js +0 -161
- package/tests/unit/integration/scenario-09-cross-dimensional.test.js +0 -139
- package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +0 -357
- package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +0 -410
- package/tests/unit/integration/scenario-12-capability-federated-read.test.js +0 -719
- package/tests/unit/performance/benchmark.test.js +0 -85
- package/tests/unit/schema.test.js +0 -213
- package/tests/unit/spatial.test.js +0 -158
- package/tests/unit/storage.test.js +0 -195
- package/tests/unit/subscriptions.test.js +0 -328
- package/tests/unit/test-data-permanence-debug.js +0 -197
- package/tests/unit/test-data-permanence.js +0 -340
- package/tests/unit/test-key-persistence-fixed.js +0 -148
- package/tests/unit/test-key-persistence.js +0 -172
- package/tests/unit/test-relay-permanence.js +0 -376
- package/tests/unit/test-second-node.js +0 -95
- package/tests/unit/test-simple-write.js +0 -89
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
# HoloSphere API Contract
|
|
2
|
+
|
|
3
|
+
**Version**: 1.0.0
|
|
4
|
+
**Date**: 2025-10-07
|
|
5
|
+
**Format**: JavaScript Library API (not REST/HTTP)
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
This document defines the public API surface for HoloSphere. All methods are async (return Promises) and follow the constitutional design patterns.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. Core Initialization
|
|
13
|
+
|
|
14
|
+
### `new HoloSphere(config)`
|
|
15
|
+
Creates a new HoloSphere instance.
|
|
16
|
+
|
|
17
|
+
**Parameters**:
|
|
18
|
+
```typescript
|
|
19
|
+
config?: {
|
|
20
|
+
appName: string; // Default: 'holosphere'
|
|
21
|
+
backend?: 'nostr' | 'gundb' | 'activitypub'; // Default: 'nostr'
|
|
22
|
+
logLevel?: 'ERROR' | 'WARN' | 'INFO' | 'DEBUG'; // Default: 'WARN'
|
|
23
|
+
|
|
24
|
+
// Nostr-specific (default backend)
|
|
25
|
+
relays?: string[]; // Nostr relay URLs
|
|
26
|
+
privateKey?: string; // Hex-encoded private key
|
|
27
|
+
|
|
28
|
+
// GunDB-specific
|
|
29
|
+
gundb?: {
|
|
30
|
+
peers?: string[]; // GunDB peer relay URLs
|
|
31
|
+
radisk?: boolean; // Enable radisk (default: true)
|
|
32
|
+
localStorage?: boolean; // Enable localStorage (default: true)
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// ActivityPub-specific
|
|
36
|
+
activitypub?: {
|
|
37
|
+
serverUrl: string; // ActivityPub server URL (required)
|
|
38
|
+
apiKey?: string; // Server API key for authentication
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Common options
|
|
42
|
+
persistence?: boolean; // Enable local persistence (default: true)
|
|
43
|
+
dataDir?: string; // Custom data directory path
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Returns**: `HoloSphere` instance
|
|
48
|
+
|
|
49
|
+
**Example**:
|
|
50
|
+
```javascript
|
|
51
|
+
// Default (Nostr backend)
|
|
52
|
+
const hs = new HoloSphere({
|
|
53
|
+
appName: 'myapp',
|
|
54
|
+
relays: ['wss://relay.damus.io'],
|
|
55
|
+
logLevel: 'INFO'
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// GunDB backend
|
|
59
|
+
const hsGun = new HoloSphere({
|
|
60
|
+
appName: 'myapp',
|
|
61
|
+
backend: 'gundb',
|
|
62
|
+
gundb: {
|
|
63
|
+
peers: ['https://gun.example.com/gun'],
|
|
64
|
+
radisk: true
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ActivityPub backend (requires running server)
|
|
69
|
+
const hsAP = new HoloSphere({
|
|
70
|
+
appName: 'myapp',
|
|
71
|
+
backend: 'activitypub',
|
|
72
|
+
activitypub: {
|
|
73
|
+
serverUrl: 'http://localhost:3000',
|
|
74
|
+
apiKey: 'your-api-key'
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Errors**:
|
|
80
|
+
- Throws `TypeError` if config is invalid
|
|
81
|
+
- Throws `Error` if GunDB initialization fails
|
|
82
|
+
|
|
83
|
+
**Contract Test**: Create instance with default and custom configs
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 2. Spatial Operations
|
|
88
|
+
|
|
89
|
+
### `holosphere.toHolon(lat, lng, resolution)`
|
|
90
|
+
Convert geographic coordinates to H3 holon ID.
|
|
91
|
+
|
|
92
|
+
**Parameters**:
|
|
93
|
+
- `lat: number` - Latitude (-90 to 90)
|
|
94
|
+
- `lng: number` - Longitude (-180 to 180)
|
|
95
|
+
- `resolution: number` - H3 resolution (0-15)
|
|
96
|
+
|
|
97
|
+
**Returns**: `Promise<string>` - H3 cell ID
|
|
98
|
+
|
|
99
|
+
**Example**:
|
|
100
|
+
```javascript
|
|
101
|
+
const holonId = await hs.toHolon(37.7749, -122.4194, 9);
|
|
102
|
+
// => "8928342e20fffff"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Errors**:
|
|
106
|
+
- Throws `RangeError` if lat/lng out of bounds
|
|
107
|
+
- Throws `RangeError` if resolution not 0-15
|
|
108
|
+
|
|
109
|
+
**Contract Test**: Valid coords → valid H3, invalid coords → error
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### `holosphere.getParents(holonId, maxResolution?)`
|
|
114
|
+
Get all parent holons up the hierarchy.
|
|
115
|
+
|
|
116
|
+
**Parameters**:
|
|
117
|
+
- `holonId: string` - H3 cell ID
|
|
118
|
+
- `maxResolution?: number` - Stop at this resolution (default: 0)
|
|
119
|
+
|
|
120
|
+
**Returns**: `Promise<string[]>` - Array of parent H3 IDs (ascending hierarchy)
|
|
121
|
+
|
|
122
|
+
**Example**:
|
|
123
|
+
```javascript
|
|
124
|
+
const parents = await hs.getParents("8928342e20fffff", 5);
|
|
125
|
+
// => ["8828342e2ffffff", "8728342e2ffffff", ...]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Errors**:
|
|
129
|
+
- Throws `Error` if holonId is invalid H3 format
|
|
130
|
+
|
|
131
|
+
**Contract Test**: Valid H3 → parent array, invalid H3 → error
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### `holosphere.getChildren(holonId)`
|
|
136
|
+
Get all child holons at next resolution level.
|
|
137
|
+
|
|
138
|
+
**Parameters**:
|
|
139
|
+
- `holonId: string` - H3 cell ID
|
|
140
|
+
|
|
141
|
+
**Returns**: `Promise<string[]>` - Array of child H3 IDs (7 children per hexagon)
|
|
142
|
+
|
|
143
|
+
**Example**:
|
|
144
|
+
```javascript
|
|
145
|
+
const children = await hs.getChildren("8828342e2ffffff");
|
|
146
|
+
// => ["8928342e20fffff", "8928342e21fffff", ...]
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Errors**:
|
|
150
|
+
- Throws `Error` if holonId is invalid or already at resolution 15
|
|
151
|
+
|
|
152
|
+
**Contract Test**: Valid H3 → 7 children, res 15 → error
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 3. Data Operations
|
|
157
|
+
|
|
158
|
+
### `holosphere.write(holonId, lensName, data, options?)`
|
|
159
|
+
Write data to a holon/lens.
|
|
160
|
+
|
|
161
|
+
**Parameters**:
|
|
162
|
+
- `holonId: string` - H3 cell ID or noospheric URI
|
|
163
|
+
- `lensName: string` - Lens name
|
|
164
|
+
- `data: object` - Data to write (must include `id` or will be auto-generated)
|
|
165
|
+
- `options?: object` - Optional configuration
|
|
166
|
+
|
|
167
|
+
**Options**:
|
|
168
|
+
```typescript
|
|
169
|
+
{
|
|
170
|
+
validate?: boolean; // Validate against schema (default: true if schema exists)
|
|
171
|
+
strict?: boolean; // Strict validation mode (default: false)
|
|
172
|
+
capability?: string; // Capability token for authorization
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Returns**: `Promise<boolean>` - True if write succeeded
|
|
177
|
+
|
|
178
|
+
**Example**:
|
|
179
|
+
```javascript
|
|
180
|
+
const success = await hs.write(
|
|
181
|
+
"8928342e20fffff",
|
|
182
|
+
"temperature",
|
|
183
|
+
{ id: "sensor-1", value: 72.5, unit: "F" }
|
|
184
|
+
);
|
|
185
|
+
// => true
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Errors**:
|
|
189
|
+
- Throws `ValidationError` if strict mode enabled and data invalid
|
|
190
|
+
- Throws `AuthorizationError` if capability token invalid
|
|
191
|
+
- Returns `false` if write fails (Gun error)
|
|
192
|
+
|
|
193
|
+
**Contract Test**: Valid data → true, invalid data (strict) → ValidationError
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### `holosphere.read(holonId, lensName, dataId?)`
|
|
198
|
+
Read data from a holon/lens.
|
|
199
|
+
|
|
200
|
+
**Parameters**:
|
|
201
|
+
- `holonId: string` - H3 cell ID or noospheric URI
|
|
202
|
+
- `lensName: string` - Lens name
|
|
203
|
+
- `dataId?: string` - Optional: specific data item ID
|
|
204
|
+
|
|
205
|
+
**Returns**: `Promise<object | object[] | null>`
|
|
206
|
+
- If `dataId` provided: single object or null
|
|
207
|
+
- If `dataId` omitted: array of all objects in lens
|
|
208
|
+
|
|
209
|
+
**Example**:
|
|
210
|
+
```javascript
|
|
211
|
+
// Read specific item
|
|
212
|
+
const temp = await hs.read("8928342e20fffff", "temperature", "sensor-1");
|
|
213
|
+
// => { id: "sensor-1", value: 72.5, unit: "F" }
|
|
214
|
+
|
|
215
|
+
// Read all items in lens
|
|
216
|
+
const allTemps = await hs.read("8928342e20fffff", "temperature");
|
|
217
|
+
// => [{ id: "sensor-1", ... }, { id: "sensor-2", ... }]
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Errors**:
|
|
221
|
+
- Returns `null` if data not found
|
|
222
|
+
- Throws `Error` if holonId or lensName invalid
|
|
223
|
+
|
|
224
|
+
**Contract Test**: Existing data → object, non-existent → null
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
### `holosphere.delete(holonId, lensName, dataId, options?)`
|
|
229
|
+
Delete data from a holon/lens.
|
|
230
|
+
|
|
231
|
+
**Parameters**:
|
|
232
|
+
- `holonId: string` - H3 cell ID or noospheric URI
|
|
233
|
+
- `lensName: string` - Lens name
|
|
234
|
+
- `dataId: string` - Data item ID
|
|
235
|
+
- `options?: object` - Optional configuration
|
|
236
|
+
|
|
237
|
+
**Options**:
|
|
238
|
+
```typescript
|
|
239
|
+
{
|
|
240
|
+
capability?: string; // Required if deleting data created by others
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Returns**: `Promise<boolean>` - True if delete succeeded
|
|
245
|
+
|
|
246
|
+
**Example**:
|
|
247
|
+
```javascript
|
|
248
|
+
const deleted = await hs.delete("8928342e20fffff", "temperature", "sensor-1");
|
|
249
|
+
// => true
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Errors**:
|
|
253
|
+
- Throws `AuthorizationError` if deleting others' data without capability token
|
|
254
|
+
- Returns `false` if delete fails
|
|
255
|
+
|
|
256
|
+
**Contract Test**: Own data → true, others' data without token → AuthorizationError
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
### `holosphere.update(holonId, lensName, dataId, updates, options?)`
|
|
261
|
+
Update existing data (merge).
|
|
262
|
+
|
|
263
|
+
**Parameters**:
|
|
264
|
+
- `holonId: string`
|
|
265
|
+
- `lensName: string`
|
|
266
|
+
- `dataId: string`
|
|
267
|
+
- `updates: object` - Fields to merge
|
|
268
|
+
- `options?: object` - Same as write()
|
|
269
|
+
|
|
270
|
+
**Returns**: `Promise<boolean>`
|
|
271
|
+
|
|
272
|
+
**Example**:
|
|
273
|
+
```javascript
|
|
274
|
+
await hs.update("8928342e20fffff", "temperature", "sensor-1", { value: 73.0 });
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Errors**: Same as write()
|
|
278
|
+
|
|
279
|
+
**Contract Test**: Valid updates → true, non-existent item → false
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## 4. Schema Operations
|
|
284
|
+
|
|
285
|
+
### `holosphere.setSchema(lensName, schemaUri, strict?)`
|
|
286
|
+
Define validation schema for a lens.
|
|
287
|
+
|
|
288
|
+
**Parameters**:
|
|
289
|
+
- `lensName: string`
|
|
290
|
+
- `schemaUri: string` - URI pointing to JSON Schema 2019
|
|
291
|
+
- `strict?: boolean` - Validation mode (default: false)
|
|
292
|
+
|
|
293
|
+
**Returns**: `Promise<void>`
|
|
294
|
+
|
|
295
|
+
**Example**:
|
|
296
|
+
```javascript
|
|
297
|
+
await hs.setSchema(
|
|
298
|
+
"temperature",
|
|
299
|
+
"https://schema.example.com/temperature.json",
|
|
300
|
+
true // Strict mode
|
|
301
|
+
);
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Errors**:
|
|
305
|
+
- Throws `Error` if schema fetch fails
|
|
306
|
+
- Throws `ValidationError` if schema is invalid JSON Schema format
|
|
307
|
+
|
|
308
|
+
**Contract Test**: Valid schema → no error, invalid schema → ValidationError
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
### `holosphere.getSchema(lensName)`
|
|
313
|
+
Retrieve schema for a lens.
|
|
314
|
+
|
|
315
|
+
**Parameters**:
|
|
316
|
+
- `lensName: string`
|
|
317
|
+
|
|
318
|
+
**Returns**: `Promise<object | null>` - JSON Schema object or null if not set
|
|
319
|
+
|
|
320
|
+
**Example**:
|
|
321
|
+
```javascript
|
|
322
|
+
const schema = await hs.getSchema("temperature");
|
|
323
|
+
// => { type: "object", properties: { ... } }
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Errors**: None (returns null if not found)
|
|
327
|
+
|
|
328
|
+
**Contract Test**: Existing schema → object, non-existent → null
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
### `holosphere.clearSchema(lensName)`
|
|
333
|
+
Remove schema from a lens.
|
|
334
|
+
|
|
335
|
+
**Parameters**:
|
|
336
|
+
- `lensName: string`
|
|
337
|
+
|
|
338
|
+
**Returns**: `Promise<void>`
|
|
339
|
+
|
|
340
|
+
**Example**:
|
|
341
|
+
```javascript
|
|
342
|
+
await hs.clearSchema("temperature");
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**Errors**: None (idempotent)
|
|
346
|
+
|
|
347
|
+
**Contract Test**: After clear, getSchema returns null
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## 5. Federation Operations
|
|
352
|
+
|
|
353
|
+
### `holosphere.federate(sourceHolon, targetHolon, lensName, options?)`
|
|
354
|
+
Establish federation between two holons.
|
|
355
|
+
|
|
356
|
+
**Parameters**:
|
|
357
|
+
- `sourceHolon: string` - Source holon ID
|
|
358
|
+
- `targetHolon: string` - Target holon ID
|
|
359
|
+
- `lensName: string` - Lens to federate
|
|
360
|
+
- `options?: object`
|
|
361
|
+
|
|
362
|
+
**Options**:
|
|
363
|
+
```typescript
|
|
364
|
+
{
|
|
365
|
+
direction?: 'inbound' | 'outbound' | 'bidirectional'; // Default: 'outbound'
|
|
366
|
+
mode?: 'reference' | 'copy'; // Default: 'reference'
|
|
367
|
+
filter?: (data: object) => boolean; // Optional data filter
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**Returns**: `Promise<boolean>` - True if federation established
|
|
372
|
+
|
|
373
|
+
**Example**:
|
|
374
|
+
```javascript
|
|
375
|
+
// Geographic → Noospheric federation
|
|
376
|
+
await hs.federate(
|
|
377
|
+
"8928342e20fffff",
|
|
378
|
+
"nostr://topic/climate",
|
|
379
|
+
"temperature",
|
|
380
|
+
{ direction: 'bidirectional' }
|
|
381
|
+
);
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Errors**:
|
|
385
|
+
- Throws `Error` if source === target
|
|
386
|
+
- Returns `false` if federation setup fails
|
|
387
|
+
|
|
388
|
+
**Contract Test**: Valid holons → true, self-federation → error
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
### `holosphere.getFederatedData(holonId, lensName, options?)`
|
|
393
|
+
Query data including federated sources.
|
|
394
|
+
|
|
395
|
+
**Parameters**:
|
|
396
|
+
- `holonId: string`
|
|
397
|
+
- `lensName: string`
|
|
398
|
+
- `options?: object`
|
|
399
|
+
|
|
400
|
+
**Options**:
|
|
401
|
+
```typescript
|
|
402
|
+
{
|
|
403
|
+
resolveHolograms?: boolean; // Resolve references (default: true)
|
|
404
|
+
deduplicate?: boolean; // Remove duplicates (default: true)
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
**Returns**: `Promise<object[]>` - Combined local + federated data
|
|
409
|
+
|
|
410
|
+
**Example**:
|
|
411
|
+
```javascript
|
|
412
|
+
const allData = await hs.getFederatedData("8928342e20fffff", "temperature");
|
|
413
|
+
// => [{ id: "local-1", ... }, { id: "federated-1", _meta: { source: "..." } }]
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
**Errors**: None (returns empty array if no data)
|
|
417
|
+
|
|
418
|
+
**Contract Test**: Federated holon → includes remote data, non-federated → local only
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
### `holosphere.unfederate(sourceHolon, targetHolon, lensName)`
|
|
423
|
+
Remove federation relationship.
|
|
424
|
+
|
|
425
|
+
**Parameters**:
|
|
426
|
+
- `sourceHolon: string`
|
|
427
|
+
- `targetHolon: string`
|
|
428
|
+
- `lensName: string`
|
|
429
|
+
|
|
430
|
+
**Returns**: `Promise<boolean>`
|
|
431
|
+
|
|
432
|
+
**Example**:
|
|
433
|
+
```javascript
|
|
434
|
+
await hs.unfederate("8928342e20fffff", "nostr://topic/climate", "temperature");
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**Errors**: None (idempotent)
|
|
438
|
+
|
|
439
|
+
**Contract Test**: After unfederate, getFederatedData excludes remote data
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## 6. Hierarchical Operations
|
|
444
|
+
|
|
445
|
+
### `holosphere.upcast(holonId, lensName, dataId, options?)`
|
|
446
|
+
Propagate data to parent holons.
|
|
447
|
+
|
|
448
|
+
**Parameters**:
|
|
449
|
+
- `holonId: string` - Source holon
|
|
450
|
+
- `lensName: string`
|
|
451
|
+
- `dataId: string`
|
|
452
|
+
- `options?: object`
|
|
453
|
+
|
|
454
|
+
**Options**:
|
|
455
|
+
```typescript
|
|
456
|
+
{
|
|
457
|
+
maxLevel?: number; // Max parent levels (default: Infinity)
|
|
458
|
+
operation?: 'summarize' | 'aggregate' | 'concatenate'; // Default: 'concatenate'
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Returns**: `Promise<boolean>`
|
|
463
|
+
|
|
464
|
+
**Example**:
|
|
465
|
+
```javascript
|
|
466
|
+
await hs.upcast("8928342e20fffff", "events", "event-123", { maxLevel: 3 });
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**Errors**:
|
|
470
|
+
- Throws `Error` if holonId is not geographic (noospheric holons have no hierarchy)
|
|
471
|
+
|
|
472
|
+
**Contract Test**: Valid upcast → data in parents, noospheric → error
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## 7. Subscription Operations
|
|
477
|
+
|
|
478
|
+
### `holosphere.subscribe(holonId, lensName, callback, options?)`
|
|
479
|
+
Subscribe to real-time data changes.
|
|
480
|
+
|
|
481
|
+
**Parameters**:
|
|
482
|
+
- `holonId: string`
|
|
483
|
+
- `lensName: string`
|
|
484
|
+
- `callback: (data: object, key: string) => void`
|
|
485
|
+
- `options?: object`
|
|
486
|
+
|
|
487
|
+
**Options**:
|
|
488
|
+
```typescript
|
|
489
|
+
{
|
|
490
|
+
includeFederated?: boolean; // Include federated data (default: false)
|
|
491
|
+
throttle?: number; // Milliseconds between updates (default: 0)
|
|
492
|
+
filter?: (data: object) => boolean;
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Returns**: `{ unsubscribe: () => void }` - Subscription object
|
|
497
|
+
|
|
498
|
+
**Example**:
|
|
499
|
+
```javascript
|
|
500
|
+
const sub = hs.subscribe("8928342e20fffff", "temperature", (data, key) => {
|
|
501
|
+
console.log("Temperature updated:", data);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// Later...
|
|
505
|
+
sub.unsubscribe();
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
**Errors**:
|
|
509
|
+
- Throws `TypeError` if callback is not a function
|
|
510
|
+
|
|
511
|
+
**Contract Test**: Subscription receives updates, unsubscribe stops updates
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
## 8. Cryptographic Operations
|
|
516
|
+
|
|
517
|
+
### `holosphere.sign(content, privateKey)`
|
|
518
|
+
Sign content with secp256k1.
|
|
519
|
+
|
|
520
|
+
**Parameters**:
|
|
521
|
+
- `content: object | string` - Content to sign
|
|
522
|
+
- `privateKey: string` - Hex-encoded private key
|
|
523
|
+
|
|
524
|
+
**Returns**: `Promise<string>` - Hex-encoded signature
|
|
525
|
+
|
|
526
|
+
**Example**:
|
|
527
|
+
```javascript
|
|
528
|
+
const sig = await hs.sign({ message: "Hello" }, privateKey);
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
**Errors**:
|
|
532
|
+
- Throws `Error` if private key is invalid
|
|
533
|
+
- Lazy loads crypto module on first call
|
|
534
|
+
|
|
535
|
+
**Contract Test**: Valid content → signature, invalid key → error
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
### `holosphere.verify(content, signature, publicKey)`
|
|
540
|
+
Verify signature.
|
|
541
|
+
|
|
542
|
+
**Parameters**:
|
|
543
|
+
- `content: object | string`
|
|
544
|
+
- `signature: string` - Hex-encoded signature
|
|
545
|
+
- `publicKey: string` - Hex-encoded public key
|
|
546
|
+
|
|
547
|
+
**Returns**: `Promise<boolean>` - True if signature valid
|
|
548
|
+
|
|
549
|
+
**Example**:
|
|
550
|
+
```javascript
|
|
551
|
+
const isValid = await hs.verify({ message: "Hello" }, sig, pubkey);
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
**Errors**: None (returns false if invalid)
|
|
555
|
+
|
|
556
|
+
**Contract Test**: Valid signature → true, tampered content → false
|
|
557
|
+
|
|
558
|
+
---
|
|
559
|
+
|
|
560
|
+
### `holosphere.issueCapability(permissions, scope, recipient, options?)`
|
|
561
|
+
Issue capability token.
|
|
562
|
+
|
|
563
|
+
**Parameters**:
|
|
564
|
+
- `permissions: string[]` - e.g., ['read', 'write', 'delete']
|
|
565
|
+
- `scope: { holonId?: string, lensName?: string }`
|
|
566
|
+
- `recipient: string` - Public key
|
|
567
|
+
- `options?: object`
|
|
568
|
+
|
|
569
|
+
**Options**:
|
|
570
|
+
```typescript
|
|
571
|
+
{
|
|
572
|
+
expiresIn?: number; // Milliseconds (default: 3600000 = 1 hour)
|
|
573
|
+
issuerKey?: string; // Private key (default: use internal)
|
|
574
|
+
}
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**Returns**: `Promise<string>` - Capability token (signed JWT-like format)
|
|
578
|
+
|
|
579
|
+
**Example**:
|
|
580
|
+
```javascript
|
|
581
|
+
const token = await hs.issueCapability(
|
|
582
|
+
['read', 'write'],
|
|
583
|
+
{ holonId: "8928342e20fffff", lensName: "temperature" },
|
|
584
|
+
recipientPubkey,
|
|
585
|
+
{ expiresIn: 86400000 } // 24 hours
|
|
586
|
+
);
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
**Errors**:
|
|
590
|
+
- Throws `Error` if issuer key invalid
|
|
591
|
+
|
|
592
|
+
**Contract Test**: Valid params → token string, invalid key → error
|
|
593
|
+
|
|
594
|
+
---
|
|
595
|
+
|
|
596
|
+
### `holosphere.verifyCapability(token, requiredPermission, scope)`
|
|
597
|
+
Verify capability token.
|
|
598
|
+
|
|
599
|
+
**Parameters**:
|
|
600
|
+
- `token: string`
|
|
601
|
+
- `requiredPermission: string` - e.g., 'delete'
|
|
602
|
+
- `scope: { holonId: string, lensName: string }`
|
|
603
|
+
|
|
604
|
+
**Returns**: `Promise<boolean>` - True if token grants permission
|
|
605
|
+
|
|
606
|
+
**Example**:
|
|
607
|
+
```javascript
|
|
608
|
+
const canDelete = await hs.verifyCapability(
|
|
609
|
+
token,
|
|
610
|
+
'delete',
|
|
611
|
+
{ holonId: "8928342e20fffff", lensName: "temperature" }
|
|
612
|
+
);
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
**Errors**: None (returns false if invalid/expired)
|
|
616
|
+
|
|
617
|
+
**Contract Test**: Valid token → true, expired token → false
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
## 9. Social Protocol Operations
|
|
622
|
+
|
|
623
|
+
### `holosphere.publishNostr(event, holonId, lensName?)`
|
|
624
|
+
Publish Nostr event to holon.
|
|
625
|
+
|
|
626
|
+
**Parameters**:
|
|
627
|
+
- `event: object` - Nostr event (NIP-01 format)
|
|
628
|
+
- `holonId: string`
|
|
629
|
+
- `lensName?: string` - Default: 'social'
|
|
630
|
+
|
|
631
|
+
**Returns**: `Promise<boolean>`
|
|
632
|
+
|
|
633
|
+
**Example**:
|
|
634
|
+
```javascript
|
|
635
|
+
const nostrEvent = {
|
|
636
|
+
kind: 1,
|
|
637
|
+
content: "Hello from HoloSphere!",
|
|
638
|
+
tags: [["t", "holosphere"]],
|
|
639
|
+
// ... (will be signed automatically)
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
await hs.publishNostr(nostrEvent, "8928342e20fffff");
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
**Errors**:
|
|
646
|
+
- Throws `ValidationError` if event format invalid
|
|
647
|
+
- Throws `Error` if signature verification fails
|
|
648
|
+
|
|
649
|
+
**Contract Test**: Valid event → true, invalid format → ValidationError
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
### `holosphere.publishActivityPub(object, holonId, lensName?)`
|
|
654
|
+
Publish ActivityPub object to holon.
|
|
655
|
+
|
|
656
|
+
**Parameters**:
|
|
657
|
+
- `object: object` - ActivityPub object
|
|
658
|
+
- `holonId: string`
|
|
659
|
+
- `lensName?: string` - Default: 'social'
|
|
660
|
+
|
|
661
|
+
**Returns**: `Promise<boolean>`
|
|
662
|
+
|
|
663
|
+
**Example**:
|
|
664
|
+
```javascript
|
|
665
|
+
const apObject = {
|
|
666
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
667
|
+
type: "Note",
|
|
668
|
+
content: "Hello from HoloSphere!",
|
|
669
|
+
// ... (will be signed automatically)
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
await hs.publishActivityPub(apObject, "nostr://topic/general");
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
**Errors**: Same as publishNostr
|
|
676
|
+
|
|
677
|
+
**Contract Test**: Valid object → true, invalid format → ValidationError
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
### `holosphere.querySocial(holonId, options?)`
|
|
682
|
+
Query social content.
|
|
683
|
+
|
|
684
|
+
**Parameters**:
|
|
685
|
+
- `holonId: string`
|
|
686
|
+
- `options?: object`
|
|
687
|
+
|
|
688
|
+
**Options**:
|
|
689
|
+
```typescript
|
|
690
|
+
{
|
|
691
|
+
protocol?: 'nostr' | 'activitypub' | 'all'; // Default: 'all'
|
|
692
|
+
accessLevel?: 'public' | 'protected' | 'private' | 'all'; // Default: 'public'
|
|
693
|
+
lensName?: string; // Default: 'social'
|
|
694
|
+
}
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**Returns**: `Promise<object[]>` - Array of social content objects
|
|
698
|
+
|
|
699
|
+
**Example**:
|
|
700
|
+
```javascript
|
|
701
|
+
const posts = await hs.querySocial("8928342e20fffff", { protocol: 'nostr' });
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
**Errors**: None (returns empty array if no content)
|
|
705
|
+
|
|
706
|
+
**Contract Test**: With content → array, empty → [], filter works
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
## 10. Utility Operations
|
|
711
|
+
|
|
712
|
+
### `holosphere.isValidH3(holonId)`
|
|
713
|
+
Validate H3 format.
|
|
714
|
+
|
|
715
|
+
**Parameters**:
|
|
716
|
+
- `holonId: string`
|
|
717
|
+
|
|
718
|
+
**Returns**: `boolean`
|
|
719
|
+
|
|
720
|
+
**Example**:
|
|
721
|
+
```javascript
|
|
722
|
+
hs.isValidH3("8928342e20fffff"); // => true
|
|
723
|
+
hs.isValidH3("invalid"); // => false
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
**Errors**: None
|
|
727
|
+
|
|
728
|
+
**Contract Test**: Valid H3 → true, invalid → false
|
|
729
|
+
|
|
730
|
+
---
|
|
731
|
+
|
|
732
|
+
### `holosphere.metrics()`
|
|
733
|
+
Get usage metrics.
|
|
734
|
+
|
|
735
|
+
**Parameters**: None
|
|
736
|
+
|
|
737
|
+
**Returns**: `object` - Metrics snapshot
|
|
738
|
+
|
|
739
|
+
**Example**:
|
|
740
|
+
```javascript
|
|
741
|
+
const metrics = hs.metrics();
|
|
742
|
+
// => {
|
|
743
|
+
// writes: 1523,
|
|
744
|
+
// reads: 8942,
|
|
745
|
+
// subscriptions: 12,
|
|
746
|
+
// federations: 3,
|
|
747
|
+
// avgWriteTime: 15.3, // ms
|
|
748
|
+
// avgReadTime: 8.7 // ms
|
|
749
|
+
// }
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
**Errors**: None
|
|
753
|
+
|
|
754
|
+
**Contract Test**: Returns object with expected properties
|
|
755
|
+
|
|
756
|
+
---
|
|
757
|
+
|
|
758
|
+
## Error Types
|
|
759
|
+
|
|
760
|
+
```typescript
|
|
761
|
+
class ValidationError extends Error {
|
|
762
|
+
constructor(message: string, errors: object[]);
|
|
763
|
+
errors: object[]; // Ajv validation errors
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
class AuthorizationError extends Error {
|
|
767
|
+
constructor(message: string, requiredPermission?: string);
|
|
768
|
+
requiredPermission?: string;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
class HolosphereError extends Error {
|
|
772
|
+
constructor(message: string, code: string);
|
|
773
|
+
code: string; // Error code for programmatic handling
|
|
774
|
+
}
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
---
|
|
778
|
+
|
|
779
|
+
## Contract Test Summary
|
|
780
|
+
|
|
781
|
+
Each API method must have:
|
|
782
|
+
1. **Success case test**: Valid inputs → expected output
|
|
783
|
+
2. **Error case test**: Invalid inputs → expected error
|
|
784
|
+
3. **Edge case test**: Boundary conditions (empty data, null values, etc.)
|
|
785
|
+
4. **Integration test**: Cross-module interaction (e.g., write → read → update → delete)
|
|
786
|
+
|
|
787
|
+
**Coverage Target**: 100% of public API methods
|
|
788
|
+
|
|
789
|
+
---
|
|
790
|
+
|
|
791
|
+
## Next Steps
|
|
792
|
+
✅ API contract complete with all methods, parameters, and error handling
|
|
793
|
+
➡️ Generate quickstart.md with user story scenarios
|