holosphere 2.0.0-alpha1 → 2.0.0-alpha10
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/CHANGELOG.md +473 -0
- package/FEATURES.md +431 -0
- package/LICENSE +29 -166
- package/LICENSE-AGPL.md +180 -0
- package/README.md +97 -16
- package/dist/2019-D2OG2idw.js +6680 -0
- package/dist/2019-D2OG2idw.js.map +1 -0
- package/dist/2019-EION3wKo.cjs +8 -0
- package/dist/2019-EION3wKo.cjs.map +1 -0
- package/dist/_commonjsHelpers-C37NGDzP.cjs +2 -0
- package/dist/_commonjsHelpers-C37NGDzP.cjs.map +1 -0
- package/dist/_commonjsHelpers-CUmg6egw.js +7 -0
- package/dist/_commonjsHelpers-CUmg6egw.js.map +1 -0
- package/dist/browser-BSniCNqO.js +3058 -0
- package/dist/browser-BSniCNqO.js.map +1 -0
- package/dist/browser-Cq59Ij19.cjs +2 -0
- package/dist/browser-Cq59Ij19.cjs.map +1 -0
- package/dist/cdn/holosphere.min.js +55 -0
- package/dist/cdn/holosphere.min.js.map +1 -0
- package/dist/cjs/holosphere.cjs +2 -0
- package/dist/cjs/holosphere.cjs.map +1 -0
- package/dist/esm/holosphere.js +53 -0
- package/dist/esm/holosphere.js.map +1 -0
- package/dist/index-DDGt_V9o.cjs +12 -0
- package/dist/index-DDGt_V9o.cjs.map +1 -0
- package/dist/index-DJXftyvB.js +39841 -0
- package/dist/index-DJXftyvB.js.map +1 -0
- package/dist/index-DMbdcMtK.cjs +18 -0
- package/dist/index-DMbdcMtK.cjs.map +1 -0
- package/dist/index-DeZ1xz_s.js +15104 -0
- package/dist/index-DeZ1xz_s.js.map +1 -0
- package/dist/indexeddb-storage-BFt6hMeF.js +176 -0
- package/dist/indexeddb-storage-BFt6hMeF.js.map +1 -0
- package/dist/indexeddb-storage-BK5tv4Sh.cjs +2 -0
- package/dist/indexeddb-storage-BK5tv4Sh.cjs.map +1 -0
- package/dist/memory-storage-C9HuoL2E.js +91 -0
- package/dist/memory-storage-C9HuoL2E.js.map +1 -0
- package/dist/memory-storage-Dao7jfYG.cjs +2 -0
- package/dist/memory-storage-Dao7jfYG.cjs.map +1 -0
- package/dist/secp256k1-BbKzbLtD.cjs +12 -0
- package/dist/secp256k1-BbKzbLtD.cjs.map +1 -0
- package/dist/secp256k1-CreY7Pcl.js +1890 -0
- package/dist/secp256k1-CreY7Pcl.js.map +1 -0
- package/docs/CONTRACTS.md +797 -0
- package/docs/FOSDEM_PROPOSAL.md +388 -0
- package/docs/LOCALFIRST.md +266 -0
- package/docs/api/ai_aggregation.js.html +333 -0
- package/docs/api/ai_breakdown.js.html +524 -0
- package/docs/api/ai_classifier.js.html +231 -0
- package/docs/api/ai_council.js.html +246 -0
- package/docs/api/ai_embeddings.js.html +304 -0
- package/docs/api/ai_federation-ai.js.html +338 -0
- package/docs/api/ai_h3-ai.js.html +970 -0
- package/docs/api/ai_index.js.html +124 -0
- package/docs/api/ai_json-ops.js.html +241 -0
- package/docs/api/ai_llm-service.js.html +239 -0
- package/docs/api/ai_nl-query.js.html +236 -0
- package/docs/api/ai_relationships.js.html +367 -0
- package/docs/api/ai_schema-extractor.js.html +235 -0
- package/docs/api/ai_spatial.js.html +307 -0
- package/docs/api/ai_tts.js.html +214 -0
- package/docs/api/content_social-protocols.js.html +180 -0
- package/docs/api/core_holosphere.js.html +757 -0
- package/docs/api/crypto_nostr-utils.js.html +306 -0
- package/docs/api/crypto_secp256k1.js.html +267 -0
- package/docs/api/data/search.json +1 -0
- package/docs/api/federation_discovery.js.html +337 -0
- package/docs/api/federation_handshake.js.html +478 -0
- package/docs/api/federation_hologram.js.html +1053 -0
- package/docs/api/federation_registry.js.html +389 -0
- package/docs/api/fonts/Inconsolata-Regular.ttf +0 -0
- package/docs/api/fonts/OpenSans-Regular.ttf +0 -0
- package/docs/api/fonts/WorkSans-Bold.ttf +0 -0
- package/docs/api/global.html +3 -0
- package/docs/api/hierarchical_upcast.js.html +128 -0
- package/docs/api/index.html +265 -0
- package/docs/api/index.js.html +1868 -0
- package/docs/api/lib_ai-methods.js.html +660 -0
- package/docs/api/lib_contract-methods.js.html +445 -0
- package/docs/api/lib_errors.js.html +56 -0
- package/docs/api/lib_federation-methods.js.html +348 -0
- package/docs/api/lib_index.js.html +33 -0
- package/docs/api/module-ai.html +5 -0
- package/docs/api/module-ai_aggregation-SmartAggregation.html +6 -0
- package/docs/api/module-ai_aggregation.SmartAggregation.html +3 -0
- package/docs/api/module-ai_aggregation.html +3 -0
- package/docs/api/module-ai_breakdown-TaskBreakdown.html +5 -0
- package/docs/api/module-ai_breakdown.TaskBreakdown.html +3 -0
- package/docs/api/module-ai_breakdown.html +3 -0
- package/docs/api/module-ai_classifier-Classifier.html +6 -0
- package/docs/api/module-ai_classifier.Classifier.html +3 -0
- package/docs/api/module-ai_classifier.html +3 -0
- package/docs/api/module-ai_council-Council.html +6 -0
- package/docs/api/module-ai_council.Council.html +3 -0
- package/docs/api/module-ai_council.html +3 -0
- package/docs/api/module-ai_embeddings-Embeddings.html +5 -0
- package/docs/api/module-ai_embeddings.Embeddings.html +3 -0
- package/docs/api/module-ai_embeddings.html +3 -0
- package/docs/api/module-ai_federation-ai-FederationAdvisor.html +6 -0
- package/docs/api/module-ai_federation-ai.FederationAdvisor.html +3 -0
- package/docs/api/module-ai_federation-ai.html +3 -0
- package/docs/api/module-ai_h3-ai-H3AI.html +6 -0
- package/docs/api/module-ai_h3-ai.H3AI.html +3 -0
- package/docs/api/module-ai_h3-ai.html +3 -0
- package/docs/api/module-ai_json-ops-JSONOps.html +5 -0
- package/docs/api/module-ai_json-ops.JSONOps.html +3 -0
- package/docs/api/module-ai_json-ops.html +3 -0
- package/docs/api/module-ai_llm-service-LLMService.html +5 -0
- package/docs/api/module-ai_llm-service.LLMService.html +3 -0
- package/docs/api/module-ai_llm-service.html +3 -0
- package/docs/api/module-ai_nl-query-NLQuery.html +5 -0
- package/docs/api/module-ai_nl-query.NLQuery.html +3 -0
- package/docs/api/module-ai_nl-query.html +3 -0
- package/docs/api/module-ai_relationships-RelationshipDiscovery.html +6 -0
- package/docs/api/module-ai_relationships.RelationshipDiscovery.html +3 -0
- package/docs/api/module-ai_relationships.html +3 -0
- package/docs/api/module-ai_schema-extractor-SchemaExtractor.html +5 -0
- package/docs/api/module-ai_schema-extractor.SchemaExtractor.html +3 -0
- package/docs/api/module-ai_schema-extractor.html +3 -0
- package/docs/api/module-ai_spatial-SpatialAnalysis.html +6 -0
- package/docs/api/module-ai_spatial.SpatialAnalysis.html +3 -0
- package/docs/api/module-ai_spatial.html +3 -0
- package/docs/api/module-ai_tts-TTS.html +5 -0
- package/docs/api/module-ai_tts.TTS.html +3 -0
- package/docs/api/module-ai_tts.html +3 -0
- package/docs/api/module-content_social-protocols.html +3 -0
- package/docs/api/module-core_holosphere-HoloSphere.html +6 -0
- package/docs/api/module-core_holosphere.HoloSphere.html +3 -0
- package/docs/api/module-core_holosphere.html +3 -0
- package/docs/api/module-crypto_nostr-utils.html +3 -0
- package/docs/api/module-crypto_secp256k1.html +3 -0
- package/docs/api/module-federation_hologram.html +3 -0
- package/docs/api/module-hierarchical_upcast.html +3 -0
- package/docs/api/module-holosphere-HoloSphereBase.html +3 -0
- package/docs/api/module-holosphere.html +3 -0
- package/docs/api/module-lib_ai-methods.html +3 -0
- package/docs/api/module-lib_contract-methods.html +3 -0
- package/docs/api/module-lib_errors-AuthorizationError.html +3 -0
- package/docs/api/module-lib_errors-ValidationError.html +3 -0
- package/docs/api/module-lib_errors.AuthorizationError.html +3 -0
- package/docs/api/module-lib_errors.ValidationError.html +3 -0
- package/docs/api/module-lib_errors.html +3 -0
- package/docs/api/module-lib_federation-methods.html +3 -0
- package/docs/api/module-lib_index.html +3 -0
- package/docs/api/module-schema_validator-ValidationError.html +3 -0
- package/docs/api/module-schema_validator.ValidationError.html +3 -0
- package/docs/api/module-schema_validator.html +3 -0
- package/docs/api/module-spatial_h3-operations.html +4 -0
- package/docs/api/module-storage_backend-factory.BackendFactory.html +3 -0
- package/docs/api/module-storage_backend-factory.html +3 -0
- package/docs/api/module-storage_backend-interface-StorageBackend.html +3 -0
- package/docs/api/module-storage_backend-interface.StorageBackend.html +3 -0
- package/docs/api/module-storage_backend-interface.html +3 -0
- package/docs/api/module-storage_backends_activitypub-backend-ActivityPubBackend.html +7 -0
- package/docs/api/module-storage_backends_activitypub-backend.ActivityPubBackend.html +3 -0
- package/docs/api/module-storage_backends_activitypub-backend.html +3 -0
- package/docs/api/module-storage_backends_activitypub_server-ActivityPubServer.html +8 -0
- package/docs/api/module-storage_backends_activitypub_server.ActivityPubServer.html +3 -0
- package/docs/api/module-storage_backends_activitypub_server.html +3 -0
- package/docs/api/module-storage_backends_gundb-backend-GunDBBackend.html +7 -0
- package/docs/api/module-storage_backends_gundb-backend.GunDBBackend.html +3 -0
- package/docs/api/module-storage_backends_gundb-backend.html +3 -0
- package/docs/api/module-storage_backends_nostr-backend-NostrBackend.html +8 -0
- package/docs/api/module-storage_backends_nostr-backend.NostrBackend.html +3 -0
- package/docs/api/module-storage_backends_nostr-backend.html +3 -0
- package/docs/api/module-storage_filesystem-storage-FileSystemStorage.html +5 -0
- package/docs/api/module-storage_filesystem-storage-browser-FileSystemStorage.html +3 -0
- package/docs/api/module-storage_filesystem-storage-browser.FileSystemStorage.html +3 -0
- package/docs/api/module-storage_filesystem-storage-browser.html +3 -0
- package/docs/api/module-storage_filesystem-storage.FileSystemStorage.html +3 -0
- package/docs/api/module-storage_filesystem-storage.html +3 -0
- package/docs/api/module-storage_global-tables.html +3 -0
- package/docs/api/module-storage_gun-async.html +3 -0
- package/docs/api/module-storage_gun-auth-GunAuth.html +5 -0
- package/docs/api/module-storage_gun-auth.GunAuth.html +3 -0
- package/docs/api/module-storage_gun-auth.html +3 -0
- package/docs/api/module-storage_gun-federation.html +3 -0
- package/docs/api/module-storage_gun-references-GunReferenceHandler.html +5 -0
- package/docs/api/module-storage_gun-references.GunReferenceHandler.html +3 -0
- package/docs/api/module-storage_gun-references.html +3 -0
- package/docs/api/module-storage_gun-schema-GunSchemaValidator.html +5 -0
- package/docs/api/module-storage_gun-schema.GunSchemaValidator.html +3 -0
- package/docs/api/module-storage_gun-schema.html +3 -0
- package/docs/api/module-storage_gun-wrapper.html +11 -0
- package/docs/api/module-storage_indexeddb-storage-IndexedDBStorage.html +5 -0
- package/docs/api/module-storage_indexeddb-storage.IndexedDBStorage.html +3 -0
- package/docs/api/module-storage_indexeddb-storage.html +3 -0
- package/docs/api/module-storage_key-storage-simple.html +3 -0
- package/docs/api/module-storage_key-storage.html +4 -0
- package/docs/api/module-storage_memory-storage-MemoryStorage.html +5 -0
- package/docs/api/module-storage_memory-storage.MemoryStorage.html +3 -0
- package/docs/api/module-storage_memory-storage.html +3 -0
- package/docs/api/module-storage_migration-MigrationTool.html +6 -0
- package/docs/api/module-storage_migration.MigrationTool.html +3 -0
- package/docs/api/module-storage_migration.html +3 -0
- package/docs/api/module-storage_nostr-async.html +18 -0
- package/docs/api/module-storage_nostr-client-LRUCache.html +3 -0
- package/docs/api/module-storage_nostr-client-NostrClient.html +7 -0
- package/docs/api/module-storage_nostr-client.NostrClient.html +15 -0
- package/docs/api/module-storage_nostr-client.html +6 -0
- package/docs/api/module-storage_nostr-wrapper.html +3 -0
- package/docs/api/module-storage_outbox-queue-OutboxQueue.html +4 -0
- package/docs/api/module-storage_outbox-queue.OutboxQueue.html +3 -0
- package/docs/api/module-storage_outbox-queue.html +3 -0
- package/docs/api/module-storage_persistent-storage-PersistentStorage.html +3 -0
- package/docs/api/module-storage_persistent-storage.html +4 -0
- package/docs/api/module-storage_sync-service-SyncService.html +5 -0
- package/docs/api/module-storage_sync-service.SyncService.html +3 -0
- package/docs/api/module-storage_sync-service.html +3 -0
- package/docs/api/module-storage_unified-storage.html +3 -0
- package/docs/api/module-subscriptions_manager.SubscriptionRegistry.html +3 -0
- package/docs/api/module-subscriptions_manager.html +3 -0
- package/docs/api/schema_validator.js.html +113 -0
- package/docs/api/scripts/core.js +726 -0
- package/docs/api/scripts/core.min.js +23 -0
- package/docs/api/scripts/resize.js +90 -0
- package/docs/api/scripts/search.js +265 -0
- package/docs/api/scripts/search.min.js +6 -0
- package/docs/api/scripts/third-party/Apache-License-2.0.txt +202 -0
- package/docs/api/scripts/third-party/fuse.js +9 -0
- package/docs/api/scripts/third-party/hljs-line-num-original.js +369 -0
- package/docs/api/scripts/third-party/hljs-line-num.js +1 -0
- package/docs/api/scripts/third-party/hljs-original.js +5171 -0
- package/docs/api/scripts/third-party/hljs.js +1 -0
- package/docs/api/scripts/third-party/popper.js +5 -0
- package/docs/api/scripts/third-party/tippy.js +1 -0
- package/docs/api/scripts/third-party/tocbot.js +672 -0
- package/docs/api/scripts/third-party/tocbot.min.js +1 -0
- package/docs/api/spatial_h3-operations.js.html +129 -0
- package/docs/api/storage_backend-factory.js.html +133 -0
- package/docs/api/storage_backend-interface.js.html +164 -0
- package/docs/api/storage_backends_activitypub-backend.js.html +298 -0
- package/docs/api/storage_backends_activitypub_server.js.html +678 -0
- package/docs/api/storage_backends_gundb-backend.js.html +878 -0
- package/docs/api/storage_backends_nostr-backend.js.html +254 -0
- package/docs/api/storage_filesystem-storage-browser.js.html +83 -0
- package/docs/api/storage_filesystem-storage.js.html +207 -0
- package/docs/api/storage_global-tables.js.html +116 -0
- package/docs/api/storage_gun-async.js.html +344 -0
- package/docs/api/storage_gun-auth.js.html +376 -0
- package/docs/api/storage_gun-federation.js.html +788 -0
- package/docs/api/storage_gun-references.js.html +212 -0
- package/docs/api/storage_gun-schema.js.html +309 -0
- package/docs/api/storage_gun-wrapper.js.html +645 -0
- package/docs/api/storage_indexeddb-storage.js.html +224 -0
- package/docs/api/storage_key-storage-simple.js.html +102 -0
- package/docs/api/storage_key-storage.js.html +171 -0
- package/docs/api/storage_memory-storage.js.html +128 -0
- package/docs/api/storage_migration.js.html +354 -0
- package/docs/api/storage_nostr-async.js.html +1076 -0
- package/docs/api/storage_nostr-client.js.html +1598 -0
- package/docs/api/storage_nostr-wrapper.js.html +218 -0
- package/docs/api/storage_outbox-queue.js.html +248 -0
- package/docs/api/storage_persistent-storage.js.html +160 -0
- package/docs/api/storage_sync-service.js.html +201 -0
- package/docs/api/storage_unified-storage.js.html +157 -0
- package/docs/api/styles/clean-jsdoc-theme-base.css +1159 -0
- package/docs/api/styles/clean-jsdoc-theme-dark.css +412 -0
- package/docs/api/styles/clean-jsdoc-theme-light.css +482 -0
- package/docs/api/styles/clean-jsdoc-theme-scrollbar.css +30 -0
- package/docs/api/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
- package/docs/api/styles/clean-jsdoc-theme.min.css +1 -0
- package/docs/api/subscriptions_manager.js.html +162 -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/examples/demo.html +47 -0
- package/examples/holosphere-widget.js +1242 -0
- package/examples/widget-demo.html +274 -0
- package/examples/widget.html +703 -0
- package/jsdoc.json +26 -0
- package/package.json +25 -7
- package/src/ai/aggregation.js +13 -2
- package/src/ai/breakdown.js +12 -2
- package/src/ai/classifier.js +14 -3
- package/src/ai/council.js +22 -7
- package/src/ai/embeddings.js +37 -15
- package/src/ai/federation-ai.js +13 -2
- package/src/ai/h3-ai.js +14 -2
- package/src/ai/index.js +16 -7
- package/src/ai/json-ops.js +18 -5
- package/src/ai/llm-service.js +62 -31
- package/src/ai/nl-query.js +12 -2
- package/src/ai/relationships.js +13 -2
- package/src/ai/schema-extractor.js +24 -10
- package/src/ai/spatial.js +13 -2
- package/src/ai/tts.js +25 -8
- package/src/cdn-entry.js +22 -0
- package/src/content/social-protocols.js +34 -25
- package/src/contracts/abis/Appreciative.json +1280 -0
- package/src/contracts/abis/AppreciativeFactory.json +101 -0
- package/src/contracts/abis/Bundle.json +1438 -0
- package/src/contracts/abis/BundleFactory.json +106 -0
- package/src/contracts/abis/Holon.json +881 -0
- package/src/contracts/abis/Holons.json +330 -0
- package/src/contracts/abis/Managed.json +1262 -0
- package/src/contracts/abis/ManagedFactory.json +149 -0
- package/src/contracts/abis/Membrane.json +261 -0
- package/src/contracts/abis/Splitter.json +1624 -0
- package/src/contracts/abis/SplitterFactory.json +220 -0
- package/src/contracts/abis/TestToken.json +321 -0
- package/src/contracts/abis/Zoned.json +1461 -0
- package/src/contracts/abis/ZonedFactory.json +154 -0
- package/src/contracts/chain-manager.js +403 -0
- package/src/contracts/deployer.js +500 -0
- package/src/contracts/event-listener.js +539 -0
- package/src/contracts/holon-contracts.js +359 -0
- package/src/contracts/index.js +82 -0
- package/src/contracts/networks.js +229 -0
- package/src/contracts/operations.js +687 -0
- package/src/contracts/queries.js +638 -0
- package/src/core/holosphere.js +487 -6
- package/src/crypto/nostr-utils.js +303 -0
- package/src/crypto/secp256k1.js +7 -2
- package/src/federation/handshake.js +475 -0
- package/src/federation/hologram.js +117 -3
- package/src/hierarchical/upcast.js +40 -25
- package/src/index.js +1501 -1909
- package/src/lib/ai-methods.js +657 -0
- package/src/lib/contract-methods.js +442 -0
- package/src/lib/errors.js +53 -0
- package/src/lib/federation-methods.js +345 -0
- package/src/lib/index.js +30 -0
- package/src/schema/validator.js +22 -3
- package/src/spatial/h3-operations.js +19 -3
- package/src/storage/backend-factory.js +7 -2
- package/src/storage/backend-interface.js +21 -2
- package/src/storage/backends/activitypub/server.js +25 -3
- package/src/storage/backends/activitypub-backend.js +25 -2
- package/src/storage/backends/gundb-backend.js +692 -50
- package/src/storage/backends/nostr-backend.js +116 -1
- package/src/storage/filesystem-storage-browser.js +42 -2
- package/src/storage/filesystem-storage.js +72 -5
- package/src/storage/global-tables.js +35 -3
- package/src/storage/gun-async.js +75 -15
- package/src/storage/gun-auth.js +373 -0
- package/src/storage/gun-federation.js +785 -0
- package/src/storage/gun-references.js +209 -0
- package/src/storage/gun-schema.js +306 -0
- package/src/storage/gun-wrapper.js +475 -54
- package/src/storage/indexeddb-storage.js +112 -13
- package/src/storage/key-storage-simple.js +32 -9
- package/src/storage/key-storage.js +45 -13
- package/src/storage/memory-storage.js +68 -2
- package/src/storage/migration.js +20 -7
- package/src/storage/nostr-async.js +412 -122
- package/src/storage/nostr-client.js +749 -76
- package/src/storage/nostr-wrapper.js +6 -2
- package/src/storage/outbox-queue.js +55 -18
- package/src/storage/persistent-storage.js +62 -14
- package/src/storage/sync-service.js +51 -17
- package/src/storage/unified-storage.js +154 -0
- package/src/subscriptions/manager.js +34 -17
- package/types/index.d.ts +133 -0
- package/vite.config.cdn.js +60 -0
- 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
- /package/{cleanup-test-data.js → scripts/cleanup-test-data.js} +0 -0
- /package/{test-ai-real-api.js → scripts/test-ai-real-api.js} +0 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
# FOSDEM 2025 - Local First Devroom
|
|
2
|
+
## Talk Proposal
|
|
3
|
+
|
|
4
|
+
**Title:** "HoloSphere: Federated Local-First Spatial Data with Nostr and Capability Tokens"
|
|
5
|
+
|
|
6
|
+
**Category:** FOSS Frameworks that enable Local First app development
|
|
7
|
+
|
|
8
|
+
**Speaker:** [Your name/organization]
|
|
9
|
+
|
|
10
|
+
**Duration:** 20-25 minutes + Q&A
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Abstract
|
|
15
|
+
|
|
16
|
+
What if your local-first app knew *where* it was - and could federate data across geographic boundaries without a central server?
|
|
17
|
+
|
|
18
|
+
**HoloSphere** is an open-source JavaScript library that combines three powerful ideas: Uber's H3 hexagonal spatial indexing, the Nostr relay protocol for P2P synchronization, and a novel "hologram" reference system for zero-duplication federation.
|
|
19
|
+
|
|
20
|
+
Every write is signed with your secp256k1 key, cached in memory, persisted to IndexedDB/filesystem, then published to relays in the background. Reads return instantly from local cache. The result: offline writes, instant reads, eventual sync - with no backend infrastructure.
|
|
21
|
+
|
|
22
|
+
The key innovation is **holograms** - lightweight references (~150 bytes) that point to data in other locations without copying it. When biodiversity data in a local hexagon federates to regional and national views, only references propagate - the source remains authoritative. Cross-project federation uses **capability tokens** with scoped permissions, enabling controlled data sharing between independent HoloSphere instances.
|
|
23
|
+
|
|
24
|
+
This talk demonstrates a citizen science use case: field researchers collecting biodiversity observations offline, with data federating up through spatial hierarchies and syncing across the Nostr network when connectivity returns.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## How Nostr Enables Local-First
|
|
29
|
+
|
|
30
|
+
### The Storage Model
|
|
31
|
+
|
|
32
|
+
HoloSphere uses Nostr's **parameterized replaceable events (kind 30000)** as its storage primitive. Every data item becomes a signed event with a d-tag encoding the path:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
d-tag: "appname/holonId/lensName/dataId"
|
|
36
|
+
"biomap/8928308280fffff/biodiversity/obs-001"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### The Write Path (Local-First)
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
write(holon, lens, data)
|
|
43
|
+
|
|
|
44
|
+
Sign with secp256k1 private key
|
|
45
|
+
|
|
|
46
|
+
Cache in memory (~1ms)
|
|
47
|
+
|
|
|
48
|
+
Persist to IndexedDB/filesystem (~10ms)
|
|
49
|
+
|
|
|
50
|
+
Publish to relays (background, fire-and-forget)
|
|
51
|
+
|
|
|
52
|
+
Return success (before relay confirmation)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Key insight:** The write returns after local persistence, not after relay confirmation. This makes writes instant and offline-capable.
|
|
56
|
+
|
|
57
|
+
### The Read Path (Cache-First)
|
|
58
|
+
|
|
59
|
+
1. Check memory cache (5-second TTL) - instant return
|
|
60
|
+
2. Check persistent storage - still fast
|
|
61
|
+
3. Query relays only on cache miss
|
|
62
|
+
4. Background refresh for stale data
|
|
63
|
+
|
|
64
|
+
**Result:** Hot reads are ~1ms. Cold reads hit local storage before network. Network is truly optional for reads.
|
|
65
|
+
|
|
66
|
+
### What This Gives You
|
|
67
|
+
|
|
68
|
+
- **No accounts:** Your keypair *is* your identity
|
|
69
|
+
- **No servers:** Connect to any public relay, or run your own
|
|
70
|
+
- **No lock-in:** Standard Nostr protocol, portable keys
|
|
71
|
+
- **Data sovereignty:** You sign everything, you control access
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Federation Without Duplication: Holograms
|
|
76
|
+
|
|
77
|
+
### The Problem
|
|
78
|
+
|
|
79
|
+
A biodiversity observation recorded in hexagon A should appear in:
|
|
80
|
+
- The local forest view (resolution 9)
|
|
81
|
+
- The regional view (resolution 6)
|
|
82
|
+
- The national aggregation (resolution 3)
|
|
83
|
+
|
|
84
|
+
Copying data to each level = storage explosion + stale data + no single source of truth.
|
|
85
|
+
|
|
86
|
+
### The Solution: Holograms
|
|
87
|
+
|
|
88
|
+
A **hologram** is a lightweight reference (~150 bytes) that points to the original:
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
// Original observation in local hexagon
|
|
92
|
+
{
|
|
93
|
+
id: "obs-001",
|
|
94
|
+
species: "Parus major",
|
|
95
|
+
timestamp: 1701234567000,
|
|
96
|
+
confidence: 0.95
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Hologram in regional hexagon (~150 bytes)
|
|
100
|
+
{
|
|
101
|
+
id: "obs-001",
|
|
102
|
+
hologram: true,
|
|
103
|
+
soul: "biomap/8928308280fffff/biodiversity/obs-001",
|
|
104
|
+
target: { holonId, lensName, dataId },
|
|
105
|
+
// Local overrides (optional):
|
|
106
|
+
aggregationWeight: 1.0,
|
|
107
|
+
verifiedBy: "regional_coordinator"
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Automatic Resolution
|
|
112
|
+
|
|
113
|
+
When you read from the regional view, holograms resolve automatically:
|
|
114
|
+
1. Detect `hologram: true`
|
|
115
|
+
2. Follow `soul` to source
|
|
116
|
+
3. Verify capability (if cross-project)
|
|
117
|
+
4. Merge source data with local overrides
|
|
118
|
+
5. Return complete object with `_hologram` metadata
|
|
119
|
+
|
|
120
|
+
**Circular detection:** Tracks visited souls, max depth 10.
|
|
121
|
+
|
|
122
|
+
**Active hologram tracking:** Sources know all their holograms via `_meta.activeHolograms` - enables cascade updates and cleanup.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Cross-Project Federation: Capabilities
|
|
127
|
+
|
|
128
|
+
### The Challenge
|
|
129
|
+
|
|
130
|
+
Different research projects want to share data:
|
|
131
|
+
- Project A: Local birdsong observations
|
|
132
|
+
- Project B: Regional biodiversity aggregation
|
|
133
|
+
- Project C: National conservation database
|
|
134
|
+
|
|
135
|
+
How do you control who reads what without a central auth server?
|
|
136
|
+
|
|
137
|
+
### Capability Tokens
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
{
|
|
141
|
+
type: "capability",
|
|
142
|
+
permissions: ["read"],
|
|
143
|
+
scope: {
|
|
144
|
+
holonId: "*", // Any hexagon
|
|
145
|
+
lensName: "biodiversity" // Only biodiversity lens
|
|
146
|
+
},
|
|
147
|
+
recipient: "project_b_pubkey",
|
|
148
|
+
expires: 1735689600000, // 30 days
|
|
149
|
+
// Signed by issuer's private key
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Federation Handshake (Over Nostr)
|
|
154
|
+
|
|
155
|
+
1. Project A sends federation request (kind 30078) with offered capability
|
|
156
|
+
2. Project B accepts (kind 30079) with reciprocal capability
|
|
157
|
+
3. Both store received capabilities in their federation registry
|
|
158
|
+
4. Holograms can now resolve across project boundaries
|
|
159
|
+
|
|
160
|
+
### Registry Tracks
|
|
161
|
+
|
|
162
|
+
- **Federated partners:** Public keys of trusted projects
|
|
163
|
+
- **Inbound capabilities:** What we can read from them
|
|
164
|
+
- **Outbound capabilities:** What they can read from us
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Use Case: Citizen Science Biodiversity Monitoring
|
|
169
|
+
|
|
170
|
+
### The Scenario
|
|
171
|
+
|
|
172
|
+
Field researchers and citizen scientists mapping biodiversity in remote areas:
|
|
173
|
+
|
|
174
|
+
1. **Select Region:** Draw polygon on map (forest, watershed, reserve)
|
|
175
|
+
2. **Convert to Hexagons:** Polygon becomes H3 hexagons at chosen resolution
|
|
176
|
+
3. **Collect Offline:** Record observations in the field
|
|
177
|
+
- Species identified
|
|
178
|
+
- GPS coordinates (auto-assigned to hexagon)
|
|
179
|
+
- Audio recordings
|
|
180
|
+
- Environmental conditions
|
|
181
|
+
4. **Local Storage:** Data persists to device, immediately queryable
|
|
182
|
+
5. **Background Sync:** When connectivity returns, publishes to relays
|
|
183
|
+
6. **Federation:** Regional coordinators see aggregated data via holograms
|
|
184
|
+
7. **Cross-Project:** National database receives capability-gated access
|
|
185
|
+
|
|
186
|
+
### Why This Architecture Works
|
|
187
|
+
|
|
188
|
+
- **Poor connectivity:** Field sites often have no network - offline-first is essential
|
|
189
|
+
- **Spatial data:** Observations are inherently geographic - hexagons are natural
|
|
190
|
+
- **Multi-scale:** Local → regional → national aggregation via hologram hierarchy
|
|
191
|
+
- **Data sovereignty:** Researchers keep keys, control their data
|
|
192
|
+
- **No infrastructure:** Public Nostr relays, no servers to maintain
|
|
193
|
+
|
|
194
|
+
### Technical Example
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
// Initialize HoloSphere with keypair
|
|
198
|
+
const hs = new HoloSphere({
|
|
199
|
+
appName: 'biomap',
|
|
200
|
+
privateKey: researcherPrivateKey,
|
|
201
|
+
relays: ['wss://relay.damus.io', 'wss://relay.nostr.band']
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Convert GPS to hexagon
|
|
205
|
+
const hexagon = await hs.toHolon(45.5231, -122.6765, 9);
|
|
206
|
+
|
|
207
|
+
// Record observation (works offline)
|
|
208
|
+
await hs.write(hexagon, 'biodiversity', {
|
|
209
|
+
species: 'Parus major',
|
|
210
|
+
timestamp: Date.now(),
|
|
211
|
+
audio: recordingBlob,
|
|
212
|
+
confidence: 0.95,
|
|
213
|
+
observer: hs.publicKey
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Federate to regional hexagon (creates hologram)
|
|
217
|
+
const regionalHex = (await hs.getParents(hexagon))[2]; // Resolution 6
|
|
218
|
+
await hs.federate(hexagon, regionalHex, 'biodiversity', {
|
|
219
|
+
direction: 'outbound',
|
|
220
|
+
mode: 'reference' // Hologram, not copy
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Regional coordinator queries aggregated data
|
|
224
|
+
const regionalData = await hs.getAll(regionalHex, 'biodiversity');
|
|
225
|
+
// Returns: original observations + resolved holograms from child hexagons
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Current Capabilities vs Limitations
|
|
231
|
+
|
|
232
|
+
### What Works Today
|
|
233
|
+
|
|
234
|
+
| Feature | Status | Notes |
|
|
235
|
+
|---------|--------|-------|
|
|
236
|
+
| Local persistence | Working | IndexedDB (browser), filesystem (Node) |
|
|
237
|
+
| Nostr relay sync | Working | Fire-and-forget publish, background sync |
|
|
238
|
+
| H3 spatial indexing | Working | 16 resolution levels, parent/child navigation |
|
|
239
|
+
| Hologram references | Working | Same-project federation |
|
|
240
|
+
| Hologram resolution | Working | Automatic, circular detection |
|
|
241
|
+
| Local overrides | Working | Position, metadata on holograms |
|
|
242
|
+
| Active hologram tracking | Working | Source tracks all references |
|
|
243
|
+
| Real-time subscriptions | Working | Nostr-based, with throttling |
|
|
244
|
+
| Capability tokens | Working | Scope + permissions + expiration |
|
|
245
|
+
| Cross-project holograms | Working | With capability verification |
|
|
246
|
+
| Federation discovery | Working | Nostr events for request/accept |
|
|
247
|
+
|
|
248
|
+
### Known Limitations (Honest Assessment)
|
|
249
|
+
|
|
250
|
+
| Feature | Status | Notes |
|
|
251
|
+
|---------|--------|-------|
|
|
252
|
+
| Capability signature verification | Stubbed | Tokens generated but verification TODO |
|
|
253
|
+
| CRDT/automatic merge | Not implemented | Last-write-wins only |
|
|
254
|
+
| Offline write queue | Partial | Writes persist locally, no retry queue |
|
|
255
|
+
| Conflict resolution | Not implemented | No vector clocks, no causal ordering |
|
|
256
|
+
| Guaranteed delivery | Not implemented | Relay publish is best-effort |
|
|
257
|
+
| Bidirectional sync | Not implemented | Holograms are read-only references |
|
|
258
|
+
|
|
259
|
+
### Architecture Classification
|
|
260
|
+
|
|
261
|
+
HoloSphere is currently **"local-caching with remote sync"** rather than pure **"local-first with remote replication"**:
|
|
262
|
+
|
|
263
|
+
- Writes: Local-first (persist before relay)
|
|
264
|
+
- Reads: Cache-first (local before network)
|
|
265
|
+
- Sync: Eventually consistent via relays
|
|
266
|
+
- Conflicts: Last-write-wins (no merge)
|
|
267
|
+
|
|
268
|
+
This is sufficient for many use cases but not full local-first as defined by Ink & Switch.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Demo Plan
|
|
273
|
+
|
|
274
|
+
Live demonstration (5-7 minutes):
|
|
275
|
+
|
|
276
|
+
1. **Draw polygon** on interactive map (Leaflet.draw)
|
|
277
|
+
2. **Convert to hexagons** at multiple resolutions
|
|
278
|
+
3. **Record biodiversity observations** in hexagons
|
|
279
|
+
4. **Show local persistence** (IndexedDB inspector)
|
|
280
|
+
5. **Disconnect network** - continue adding data
|
|
281
|
+
6. **Query locally** - data remains available
|
|
282
|
+
7. **Federate to regional hexagon** - create holograms
|
|
283
|
+
8. **Show hologram resolution** - ~150 bytes vs full data
|
|
284
|
+
9. **Reconnect** - watch background sync to relays
|
|
285
|
+
10. **Cross-project federation** - exchange capabilities, resolve foreign holograms
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Why This Matters for Local-First
|
|
290
|
+
|
|
291
|
+
- **Spatial local-first:** First framework combining H3 geospatial with offline-capable P2P
|
|
292
|
+
- **Nostr beyond social:** Demonstrates Nostr as general-purpose sync layer
|
|
293
|
+
- **Federation without servers:** Capability tokens enable controlled sharing
|
|
294
|
+
- **Real use case:** Biodiversity monitoring has genuine connectivity constraints
|
|
295
|
+
- **Honest about gaps:** We show what works and what's still TODO
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Technical Stack
|
|
300
|
+
|
|
301
|
+
- **h3-js** - Uber's hexagonal geospatial indexing
|
|
302
|
+
- **nostr-tools** - Nostr protocol implementation
|
|
303
|
+
- **@noble/curves** - secp256k1 cryptography
|
|
304
|
+
- **IndexedDB/Filesystem** - Persistent local storage
|
|
305
|
+
- **OpenAI** (optional) - AI-assisted spatial queries
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Repository & Materials
|
|
310
|
+
|
|
311
|
+
- **Code:** https://github.com/liminalvillage/holosphere2
|
|
312
|
+
- **Documentation:** LOCALFIRST.md (architecture), VIBE.md (vision)
|
|
313
|
+
- **License:** MIT
|
|
314
|
+
- **Live Demo:** [URL TBD]
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Target Audience
|
|
319
|
+
|
|
320
|
+
- Developers building local-first applications
|
|
321
|
+
- Environmental tech / citizen science builders
|
|
322
|
+
- Decentralized app developers (especially Nostr ecosystem)
|
|
323
|
+
- Anyone interested in geospatial + P2P
|
|
324
|
+
|
|
325
|
+
## Takeaway
|
|
326
|
+
|
|
327
|
+
Attendees will understand how to:
|
|
328
|
+
1. Use Nostr as a local-first sync layer (not just social media)
|
|
329
|
+
2. Organize data spatially with H3 hexagonal indexing
|
|
330
|
+
3. Federate across boundaries using holograms (zero duplication)
|
|
331
|
+
4. Control access with capability tokens (no central auth)
|
|
332
|
+
|
|
333
|
+
And honestly assess which local-first properties the current implementation achieves vs. what remains to be built.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Appendix: Data Structures
|
|
338
|
+
|
|
339
|
+
### Nostr Event (Data Write)
|
|
340
|
+
|
|
341
|
+
```javascript
|
|
342
|
+
{
|
|
343
|
+
kind: 30000,
|
|
344
|
+
pubkey: "author_hex_pubkey",
|
|
345
|
+
created_at: 1701234567,
|
|
346
|
+
tags: [["d", "biomap/8928308280fffff/biodiversity/obs-001"]],
|
|
347
|
+
content: "{\"species\":\"Parus major\",\"confidence\":0.95,...}",
|
|
348
|
+
sig: "hex_signature"
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Hologram Object
|
|
353
|
+
|
|
354
|
+
```javascript
|
|
355
|
+
{
|
|
356
|
+
id: "obs-001",
|
|
357
|
+
hologram: true,
|
|
358
|
+
soul: "biomap/8928308280fffff/biodiversity/obs-001",
|
|
359
|
+
target: {
|
|
360
|
+
appname: "biomap",
|
|
361
|
+
holonId: "8928308280fffff",
|
|
362
|
+
lensName: "biodiversity",
|
|
363
|
+
dataId: "obs-001",
|
|
364
|
+
authorPubKey: "hex_pubkey" // For cross-project
|
|
365
|
+
},
|
|
366
|
+
capability: "base64_token", // Optional
|
|
367
|
+
crossHolosphere: true, // If cross-project
|
|
368
|
+
// Local overrides:
|
|
369
|
+
verifiedBy: "coordinator_key",
|
|
370
|
+
aggregationWeight: 1.0
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Capability Token
|
|
375
|
+
|
|
376
|
+
```javascript
|
|
377
|
+
{
|
|
378
|
+
type: "capability",
|
|
379
|
+
permissions: ["read"],
|
|
380
|
+
scope: { holonId: "*", lensName: "biodiversity" },
|
|
381
|
+
recipient: "partner_pubkey",
|
|
382
|
+
issuer: "holosphere",
|
|
383
|
+
nonce: "unique_random",
|
|
384
|
+
issued: 1701234567000,
|
|
385
|
+
expires: 1703826567000
|
|
386
|
+
}
|
|
387
|
+
// Encoded: base64(JSON).signature
|
|
388
|
+
```
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Local-First Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
HoloSphere implements **true local-first architecture** where persistent storage is the source of truth, and Nostr relays serve as the sync/replication layer. This document describes the architecture and how it achieves local-first principles.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
### Storage Layer Stack
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
+----------------------------------------------------------+
|
|
15
|
+
| HoloSphere API |
|
|
16
|
+
+----------------------------------------------------------+
|
|
17
|
+
| nostr-wrapper.js |
|
|
18
|
+
| (CRUD abstraction: write, read, update) |
|
|
19
|
+
+----------------------------------------------------------+
|
|
20
|
+
| nostr-async.js |
|
|
21
|
+
| (LOCAL-FIRST: persistentGet → background relay sync) |
|
|
22
|
+
+----------------------------------------------------------+
|
|
23
|
+
| nostr-client.js |
|
|
24
|
+
| (NostrClient: OutboxQueue, SyncService, relay pool) |
|
|
25
|
+
+----------------------------------------------------------+
|
|
26
|
+
| |
|
|
27
|
+
| +------------------+ +-------------------------+ |
|
|
28
|
+
| | Persistent Store | | OutboxQueue | |
|
|
29
|
+
| | (PRIMARY) | | (guaranteed delivery) | |
|
|
30
|
+
| | filesystem/IDB | | exponential backoff | |
|
|
31
|
+
| +------------------+ +-------------------------+ |
|
|
32
|
+
| | | |
|
|
33
|
+
| v v |
|
|
34
|
+
| +------------------+ +-------------------------+ |
|
|
35
|
+
| | Memory Cache | | SyncService | |
|
|
36
|
+
| | (optimization) | | (background retry) | |
|
|
37
|
+
| +------------------+ +-------------------------+ |
|
|
38
|
+
| |
|
|
39
|
+
| +--------------------------------------------------+ |
|
|
40
|
+
| | Nostr Relays (Replication) | |
|
|
41
|
+
| | wss://relay.damus.io, wss://relay.nostr.band | |
|
|
42
|
+
| +--------------------------------------------------+ |
|
|
43
|
+
+----------------------------------------------------------+
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Data Flow
|
|
47
|
+
|
|
48
|
+
#### Write Path (Guaranteed Delivery)
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
write(holon, lens, data)
|
|
52
|
+
│
|
|
53
|
+
▼
|
|
54
|
+
Sign with secp256k1 private key
|
|
55
|
+
│
|
|
56
|
+
▼
|
|
57
|
+
Cache in memory (~1ms)
|
|
58
|
+
│
|
|
59
|
+
▼
|
|
60
|
+
Persist to storage (~10ms) ──► Return success immediately
|
|
61
|
+
│
|
|
62
|
+
▼
|
|
63
|
+
Enqueue in OutboxQueue (persistent)
|
|
64
|
+
│
|
|
65
|
+
▼
|
|
66
|
+
Attempt immediate delivery to relays
|
|
67
|
+
│
|
|
68
|
+
├─► Success: Remove from queue
|
|
69
|
+
│
|
|
70
|
+
└─► Failure: SyncService retries with exponential backoff
|
|
71
|
+
(1s → 2s → 4s → 8s → 16s, max 60s)
|
|
72
|
+
Failed events purged after 24 hours
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Key property:** Writes return after local persistence, not after relay confirmation.
|
|
76
|
+
|
|
77
|
+
#### Read Path (Persistent-First)
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
read(holon, lens, dataId)
|
|
81
|
+
│
|
|
82
|
+
▼
|
|
83
|
+
Check persistent storage FIRST
|
|
84
|
+
│
|
|
85
|
+
├─► Found: Return immediately
|
|
86
|
+
│ │
|
|
87
|
+
│ └─► Trigger background refresh from relays
|
|
88
|
+
│
|
|
89
|
+
└─► Not found: Query relays (fallback)
|
|
90
|
+
│
|
|
91
|
+
└─► Cache result locally
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Key property:** Reads never block on network if data exists locally.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Local-First Principles Assessment
|
|
99
|
+
|
|
100
|
+
Based on [Ink & Switch's Local-First Software](https://www.inkandswitch.com/local-first/) principles:
|
|
101
|
+
|
|
102
|
+
| Principle | Score | Implementation |
|
|
103
|
+
|-----------|-------|----------------|
|
|
104
|
+
| **No spinners** | ✅ Good | Reads return from persistent storage instantly; writes return after local persist |
|
|
105
|
+
| **Works offline** | ✅ Good | Full read/write functionality; OutboxQueue ensures writes sync when online |
|
|
106
|
+
| **Data ownership** | ✅ Good | Keypair-based identity; user signs all data with secp256k1 |
|
|
107
|
+
| **Longevity** | ✅ Good | Data persists locally (IndexedDB/filesystem); relays are backup |
|
|
108
|
+
| **Privacy** | ✅ Good | User controls keys; data can be encrypted |
|
|
109
|
+
| **Collaboration** | ⚠️ Partial | Last-write-wins; no CRDTs (future enhancement) |
|
|
110
|
+
| **No lock-in** | ✅ Good | Standard Nostr protocol; portable keypairs |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Implementation Details
|
|
115
|
+
|
|
116
|
+
### OutboxQueue (`src/storage/outbox-queue.js`)
|
|
117
|
+
|
|
118
|
+
Persistent queue for guaranteed relay delivery:
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
// Events are enqueued with full retry metadata
|
|
122
|
+
{
|
|
123
|
+
id: 'event-id',
|
|
124
|
+
event: { /* signed Nostr event */ },
|
|
125
|
+
relays: ['wss://relay1', 'wss://relay2'],
|
|
126
|
+
status: 'pending', // 'pending' | 'failed'
|
|
127
|
+
retries: 0,
|
|
128
|
+
createdAt: 1701234567000,
|
|
129
|
+
nextRetryAt: 1701234567000,
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Features:**
|
|
134
|
+
- Exponential backoff: 1s → 2s → 4s → 8s → 16s (max 60s)
|
|
135
|
+
- Max 5 retry attempts before marking as 'failed'
|
|
136
|
+
- Failed events auto-purge after 24 hours
|
|
137
|
+
- Manual retry available via `retryFailed(eventId)`
|
|
138
|
+
|
|
139
|
+
### SyncService (`src/storage/sync-service.js`)
|
|
140
|
+
|
|
141
|
+
Background service for reliable sync:
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
const syncService = new SyncService(client, {
|
|
145
|
+
interval: 10000, // Process queue every 10 seconds
|
|
146
|
+
autoStart: true,
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Operations:**
|
|
151
|
+
- Processes pending events from OutboxQueue
|
|
152
|
+
- Retries failed deliveries with backoff
|
|
153
|
+
- Purges old failed events (24h TTL)
|
|
154
|
+
- Graceful shutdown on `client.close()`
|
|
155
|
+
|
|
156
|
+
### Persistent-First Reads (`src/storage/nostr-async.js`)
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
// nostrGet() - single item
|
|
160
|
+
export async function nostrGet(client, path, kind, options) {
|
|
161
|
+
// LOCAL-FIRST: Check persistent storage FIRST
|
|
162
|
+
const persistedEvent = await client.persistentGet(path);
|
|
163
|
+
if (persistedEvent) {
|
|
164
|
+
// Trigger background refresh (non-blocking)
|
|
165
|
+
client.refreshPathInBackground(path, kind, options);
|
|
166
|
+
return JSON.parse(persistedEvent.content);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Fallback to relay query only if not in local storage
|
|
170
|
+
return queryRelays(client, path, kind, options);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// nostrGetAll() - collection
|
|
174
|
+
export async function nostrGetAll(client, pathPrefix, kind, options) {
|
|
175
|
+
// LOCAL-FIRST: Check persistent storage FIRST
|
|
176
|
+
const persistedEvents = await client.persistentGetAll(pathPrefix);
|
|
177
|
+
if (persistedEvents.length > 0) {
|
|
178
|
+
// Trigger background refresh (non-blocking)
|
|
179
|
+
client.refreshPrefixInBackground(pathPrefix, kind, options);
|
|
180
|
+
return parseEvents(persistedEvents);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Fallback to relay query
|
|
184
|
+
return queryRelays(client, pathPrefix, kind, options);
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Configuration Options
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
const hs = new HoloSphere({
|
|
194
|
+
appName: 'myapp',
|
|
195
|
+
relays: ['wss://relay.damus.io'],
|
|
196
|
+
privateKey: 'hex...',
|
|
197
|
+
|
|
198
|
+
// Local-first options
|
|
199
|
+
persistence: true, // Enable persistent storage (default: true)
|
|
200
|
+
backgroundSync: true, // Enable SyncService (default: true)
|
|
201
|
+
syncInterval: 10000, // Sync interval in ms (default: 10000)
|
|
202
|
+
maxRetries: 5, // Max retry attempts (default: 5)
|
|
203
|
+
retryBaseDelay: 1000, // Base delay for backoff (default: 1000ms)
|
|
204
|
+
retryMaxDelay: 60000, // Max delay cap (default: 60000ms)
|
|
205
|
+
failedTTL: 86400000, // TTL for failed events (default: 24 hours)
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Comparison: Before vs After
|
|
212
|
+
|
|
213
|
+
### Before (Relay-First with Local Caching)
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
Read: Memory cache → NETWORK (blocking up to 30s)
|
|
217
|
+
Write: Cache → Fire-and-forget to relays (no retry)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Problems:**
|
|
221
|
+
- Cold reads block on network
|
|
222
|
+
- Writes can be lost if relay fails
|
|
223
|
+
- No guaranteed delivery
|
|
224
|
+
|
|
225
|
+
### After (Local-First with Relay Sync)
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
Read: Persistent storage → Return immediately → Background refresh
|
|
229
|
+
Write: Cache → Persist → OutboxQueue → Retry until delivered
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Benefits:**
|
|
233
|
+
- Reads never block on network
|
|
234
|
+
- Writes guaranteed to eventually deliver
|
|
235
|
+
- Works fully offline
|
|
236
|
+
- Data survives app restarts
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Remaining Limitations
|
|
241
|
+
|
|
242
|
+
| Feature | Status | Notes |
|
|
243
|
+
|---------|--------|-------|
|
|
244
|
+
| Conflict resolution | Last-write-wins | No CRDTs; timestamps determine winner |
|
|
245
|
+
| Multi-device sync | Via relays | No direct peer-to-peer sync |
|
|
246
|
+
| Real-time collaboration | Limited | No operational transforms |
|
|
247
|
+
|
|
248
|
+
### Future Enhancements
|
|
249
|
+
|
|
250
|
+
1. **CRDTs** - Automerge/Yjs for collaborative editing
|
|
251
|
+
2. **Direct sync** - mDNS/local network device discovery
|
|
252
|
+
3. **Conflict UI** - Surface conflicts for user resolution
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## References
|
|
257
|
+
|
|
258
|
+
- [Local-First Software (Ink & Switch)](https://www.inkandswitch.com/local-first/)
|
|
259
|
+
- [Nostr Protocol Specification](https://github.com/nostr-protocol/nips)
|
|
260
|
+
- [CRDTs and Local-First](https://crdt.tech/)
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
*Document Version: 2.0*
|
|
265
|
+
*Last Updated: 2025-12*
|
|
266
|
+
*Status: Implemented*
|