Moss/Weave Integration
This document describes how Requests & Offers integrates with the Weave/Moss ecosystem as a Moss Tool. It covers architecture, design decisions, and development setup.
Overview
R&O runs in two modes:
- Standalone -- Direct connection via
AppWebsocket - Moss Tool -- Integrated via
WeaveClientwithin a Moss group
The app detects its context once at startup and adapts connection, profile handling, and admin detection accordingly. Weave-specific concerns are isolated in a dedicated WeaveService + weaveStore layer, keeping HolochainClientService focused on core Holochain connectivity.
1. Context Detection
Context detection uses a two-stage approach coordinated between +layout.svelte and the Weave layer.
Stage 1 -- Hot-reload setup and detection (+layout.svelte onMount):
// +layout.svelte โ onMount
onMount(async () => {
// Sets up window.__WEAVE_API__ which isWeaveContext() checks.
// MUST run before Weave context detection.
try {
await initializeHotReload();
} catch {
// Expected to fail in non-Weave environments and production webhapps
}
// Detect Weave context AFTER hot-reload has set up the environment
weaveStore.detectWeaveContext();
// ...
});
Stage 2 -- Connection delegation (HolochainClientService.connectClient()):
// HolochainClientService.svelte.ts โ connectClient()
if (weaveStore.isWeaveContext) {
console.log('๐งถ Detected Weave context, connecting via WeaveClient...');
const result = await weaveStore.connect();
client = result.appClient;
} else {
console.log('๐ก Standalone mode, connecting via AppWebsocket...');
client = await AppWebsocket.connect();
}
The WeaveService lazily detects the Weave context by calling isWeaveContext() from @theweave/api. This lazy detection is necessary because in weave dev mode, initializeHotReload() must set up window.__WEAVE_API__ before isWeaveContext() can detect the environment.
Cross-group views (renderInfo.type !== 'applet-view') are not yet supported and result in a WeaveError.
Key files:
ui/src/lib/stores/weave.store.svelte.tsui/src/lib/services/weave.service.tsui/src/lib/services/HolochainClientService.svelte.ts
2. Connection Management
Both modes share the same retry logic in HolochainClientService:
- Max retries: 3 attempts
- Backoff: Exponential -- 2s, 4s, 8s delays (
Math.pow(2, retryCount) * 1000) - State tracking: Reactive
isConnected/isConnectingflags - Concurrent guard: If a connection attempt is already in progress, callers wait via polling
In Weave context, HolochainClientService.connectClient() delegates to weaveStore.connect(), which internally runs WeaveService.connect() via Effect. This returns a WeaveConnectionResult containing the appClient, weaveClient, and profilesClient. In standalone mode, AppWebsocket.connect() is called directly. The retry/backoff/state-tracking logic remains in HolochainClientService for both paths.
Components can call waitForConnection() to block until the client is ready, or use verifyConnection() to test connectivity (attempts client.appInfo() as a health check).
On WebSocket or connection errors during callZome(), the service marks itself as disconnected and nulls the client, forcing reconnection on the next call.
3. Hybrid Profile System
In Moss, users already have a profile (nickname + avatar) managed by the group's ProfilesClient. R&O stores additional professional data (bio, email, skills, location). The weaveStore manages profile fetching, avatar conversion, and enrichment of R&O users with Moss identity data.
WeaveStore Profile Management
The weaveStore provides methods and reactive state for Moss profile handling:
Methods:
| Method | Purpose |
|---|---|
initialize(agentPubKey) | Fetch Moss profile via profilesClient.getAgentProfile(), populate reactive state |
refreshMossProfile(agentPubKey) | Re-fetch Moss profile (e.g., after profile update) |
enrichWithMossProfile(raoUser, agentPubKey) | Merge Moss identity onto an R&O UIUser (Effect-based) |
Reactive state:
| Property | Type | Description |
|---|---|---|
mossProfile | MossProfile | null | Current Moss profile (nickname + avatar) |
mossAvatarBlob | Blob | null | Decoded avatar as a Blob for direct display |
hasMossNickname | boolean | Derived: in Weave context and profile has nickname |
hasMossAvatar | boolean | Derived: in Weave context and avatar blob exists |
mossNickname | string | null | Derived: the Moss nickname or null |
After connection, the layout initializes the Weave store profile state:
// +layout.svelte โ initialization orchestrator
if (weaveStore.isWeaveContext) {
const appInfo = await hc.getAppInfo();
if (appInfo?.agent_pub_key) {
await weaveStore.initialize(appInfo.agent_pub_key as AgentPubKey);
}
}
Enrichment behavior:
- Moss context:
enrichWithMossProfilereturns aUIUserwithnicknameandpicturefrom Moss, all other fields from R&O, andidentitySource: 'moss' - Standalone: Returns the R&O user as-is with
identitySource: 'standalone' - If a Moss profile exists but no R&O user record yet (new user in Moss), a minimal
UIUseris returned with Moss identity fields and empty R&O fields
Component Integration
Components import weaveStore directly to access Moss profile data. For example, UserForm.svelte uses the store to pre-fill the nickname and avatar when creating a new user in Moss context:
weaveStore.mossNickname-- Pre-fills the nickname fieldweaveStore.hasMossNickname-- Controls whether nickname is editableweaveStore.hasMossAvatar-- Controls avatar display/editingweaveStore.mossAvatarBlob-- Provides the avatar for preview
Error Handling
WeaveError is a tagged error (Data.TaggedError('WeaveError')) with fields for message, cause, context, agentPubKey, and operation. Error contexts are defined in WEAVE_CONTEXTS:
DETECT_CONTEXT-- Detecting Weave environmentCONNECT-- Connecting to WeaveGET_MOSS_PROFILE-- Fetching from MossprofilesClientENRICH_WITH_MOSS_PROFILE-- Merging Moss data onto R&O profileCHECK_PROGENITOR-- Checking tool installer statusAVATAR_CONVERSION-- Converting base64 avatar to binary
Key files:
ui/src/lib/services/weave.service.tsui/src/lib/stores/weave.store.svelte.tsui/src/lib/errors/weave.errors.tsui/src/lib/errors/error-contexts.tsui/src/lib/types/ui.ts(identitySourcefield onUIUser)
4. Progenitor Detection (Admin)
In Moss, the group "progenitor" (the agent who installed the tool) gets admin rights automatically.
Working approach: toolInstaller() + pubkey comparison
The weaveStore.checkProgenitor(myPubKey) method handles progenitor detection:
// weave.store.svelte.ts โ checkProgenitor()
async function checkProgenitor(myPubKey: AgentPubKey): Promise<boolean> {
if (!isWeaveContext || !weaveClient) {
isProgenitor = false;
return false;
}
const renderInfo = weaveClient.renderInfo;
if (renderInfo.type !== 'applet-view') {
isProgenitor = false;
return false;
}
const appletHash = renderInfo.appletHash;
const installerPubKey = await weaveClient.toolInstaller(appletHash);
if (!installerPubKey) {
isProgenitor = false;
return false;
}
const installerB64 = encodeHashToBase64(installerPubKey);
const myB64 = encodeHashToBase64(myPubKey);
isProgenitor = installerB64 === myB64;
return isProgenitor;
}
toolInstaller(appletHash) returns the AgentPubKey of whoever installed the R&O tool in this Moss group. Comparing it with the current agent's pubkey gives cryptographic proof of installer identity. The result is stored as reactive isProgenitor state.
Failed approach: myAccountabilitiesPerGroup()
An earlier attempt used weaveClient.myAccountabilitiesPerGroup() to check for a "Progenitor" role. This doesn't work because the API returns group role definitions to all agents, not filtered by who holds them. Both the progenitor and regular members receive identical data showing "Progenitor role exists."
Standalone fallback
When not in Moss context, checkProgenitor() sets isProgenitor = false and returns immediately. The existing R&O admin system (explicit registerNetworkAdministrator / addNetworkAdministrator) handles admin rights.
Key file: ui/src/lib/stores/weave.store.svelte.ts
5. Auto-Admin Registration
The +layout.svelte initialization orchestrator runs autoRegisterProgenitorAdminStep after connection, hREA init, network verification, Weave store initialization, and user data loading:
- Check
weaveStore.isWeaveContext-- skip if standalone - Get agent pubkey from
hc.getAppInfo() - Call
weaveStore.checkProgenitor(agentPubKey)-- skip if not progenitor - Call
administrationStore.hasAnyAdministrators()-- skip if admins already exist - Check
usersStore.currentUserexists with anoriginal_action_hash - Call
administrationStore.registerNetworkAdministrator(userHash, [pubKey])
This is wrapped in E.catchAll so failures are non-critical -- the app continues normally.
In standalone mode, first-admin registration is triggered via Ctrl+Shift+A keyboard shortcut, which shows a confirmation modal. This shortcut is blocked in Weave context since progenitor auto-admin handles it.
Key files:
ui/src/routes/+layout.svelteui/src/lib/stores/weave.store.svelte.ts
6. Network Peer Discovery
getNetworkPeers() queries the conductor for connected peers:
- Checks
client instanceof AppWebsocket(method not available on the genericAppClientinterface used in Moss) - Calls
client.agentInfo({ dna_hashes: null }) - Handles multiple response formats (object with
agent_pub_key, JSON strings, nestedagentInfo.agent) - Returns a deduplicated array of agent public key strings
Similarly, getPeerMetaInfo() collects metadata from peers by calling client.peerMetaInfo() for each discovered agent URL.
Limitation: In Weave context, the client is an AppClient (not AppWebsocket), so agentInfo / peerMetaInfo are not available. These methods return empty results. A future upstream PR to @holochain/client could add these to the AppClient interface.
Key file: ui/src/lib/services/HolochainClientService.svelte.ts
7. Data Flow
User opens R&O
โ
โผ
+layout.svelte (onMount)
โ
โโโโบ initializeHotReload()
โ
โผ
weaveStore.detectWeaveContext()
โ
โผ
connectToHolochain()
โ
โโโ Weave? โ weaveStore.connect() โ WeaveService.connect() โ WeaveClient
โ โ
โโโ Standalone? โ AppWebsocket.connect() โ
โ โ
โผ โผ
Store client reference โโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
Initialize hREA, verify network, load user data
โ
โผ
weaveStore.initialize(agentPubKey) (Weave only โ fetch Moss profile)
โ
โผ
autoRegisterProgenitorAdminStep
โ โโโ weaveStore.checkProgenitor(pubKey)
โ
โผ
App renders
โ
โโโโบ Profile display โ weaveStore โ Moss or R&O profiles
โ
โโโโบ Admin checks โ weaveStore.isProgenitor โ Moss group data or R&O admin system
โ
โโโโบ Zome calls โ HolochainClientService โ Holochain conductor
8. Development Setup
Directory structure
weave/
โโโ applet-dev.sh # Multi-agent launch script
โโโ curations-0.15.json # Tool curation metadata for Moss registry
โโโ tool-list-0.15.json # Tool registry (versions, hashes, download URLs)
โโโ weave.dev.config.json # Dev sandbox config (groups, agents, applets)
Running in Weave dev mode
bun run applet-dev # Launches with 2 agents by default
AGENTS=3 bun run applet-dev # Custom number of agents (1-10)
The applet-dev.sh script:
- Starts the UI dev server on port 8888
- Launches agent 1 with
weave --agent-idx 1 --dev-config ./weave/weave.dev.config.json - Launches additional agents with staggered delays (5s between each) and
--sync-time 20000 - Uses
concurrentlyto run all processes
Dev sandbox config (weave.dev.config.json)
Defines a test group ("R&O Dev Group") with:
networkSeed: "ro-dev-test-2026"for isolated test networks- Agent 1 as the creating agent (becomes progenitor)
- The
requests_and_offersapplet sourced fromlocalhost(hApp from./workdir/requests_and_offers.happ, UI from port 8888)
Tool registry (tool-list-0.15.json)
Contains the developer collective metadata and tool versions for the Moss tool directory:
- Developer: hAppenings Community CIC
- Tool ID:
requests-and-offers - Version branch:
0.3.x - Includes hApp, WebHapp, and UI SHA256 hashes for integrity verification
Curations (curations-0.15.json)
Points Moss to the tool list URL for discovery in the Moss tool directory.
9. Weave API Reference
Key methods used from @theweave/api:
| Method | Purpose | Returns |
|---|---|---|
isWeaveContext() | Detect if running inside Moss | boolean |
WeaveClient.connect() | Establish connection in Moss context | WeaveClient |
initializeHotReload() | Enable dev hot-reload in Moss | void |
weaveClient.renderInfo | Get applet view info, client, profiles client | RenderInfo |
weaveClient.toolInstaller(appletHash) | Get pubkey of tool installer | AgentPubKey | undefined |
weaveClient.renderInfo.appletClient | Holochain AppClient for zome calls | AppClient |
weaveClient.renderInfo.profilesClient | Profiles service for Moss identities | ProfilesClient |
weaveClient.renderInfo.appletHash | Hash identifying this tool installation | AppletHash |
10. Version Compatibility
| Package | Version |
|---|---|
@theweave/api | 0.6.3 |
@theweave/cli | 0.15.10 |
| Moss | 0.15.x |
| Holochain | 0.6.x |
| R&O | 0.3.0 |
11. Files Reference
| Category | File | Purpose |
|---|---|---|
| Weave Service | ui/src/lib/services/weave.service.ts | Effect-TS service for context detection + WeaveClient connection |
| Weave Store | ui/src/lib/stores/weave.store.svelte.ts | Reactive store: profile state, avatar, progenitor check, enrichment |
| Connection | ui/src/lib/services/HolochainClientService.svelte.ts | Delegates to weaveStore for Weave connection, peer discovery |
| Connection Utils | ui/src/lib/utils/holochain-client.utils.ts | Simple connection/status helpers used by layout |
| Errors | ui/src/lib/errors/weave.errors.ts | WeaveError tagged error |
| Error Contexts | ui/src/lib/errors/error-contexts.ts | WEAVE_CONTEXTS |
| Types | ui/src/lib/types/ui.ts | identitySource field on UIUser |
| Admin Store | ui/src/lib/stores/administration.store.svelte.ts | Admin detection, progenitor auto-registration |
| Layout | ui/src/routes/+layout.svelte | Initialization orchestrator, auto-admin step |
| User Form | ui/src/lib/components/users/UserForm.svelte | Moss profile pre-fill via weaveStore |
| Weave Config | weave/weave.dev.config.json | Dev sandbox configuration |
| Tool Registry | weave/tool-list-0.15.json | Moss tool directory metadata |
| Curations | weave/curations-0.15.json | Moss tool curation entry |
| Dev Script | weave/applet-dev.sh | Multi-agent Weave dev launcher |
12. Testing Checklist
- App loads in Moss group without errors
- Profile displays correctly (nickname + avatar from Moss)
- User creation form pre-fills with Moss profile data
- Group progenitor is auto-registered as admin
- Non-progenitor agents do not get auto-admin
- Admin features available to progenitor
- Network peers visible in standalone mode
- Standalone mode still works (regression test)
- DHT propagation between multiple agents in Weave dev mode
-
Ctrl+Shift+Ablocked in Weave context
13. Future Considerations
-
Deeper Moss permissions -- Currently only progenitor status is checked. Moss may offer richer permission models that could map to R&O roles.
-
Profile sync -- Profiles are read-only from Moss. Bidirectional sync could be explored if R&O has fields that should flow back to Moss.
-
Cross-group views -- Currently unsupported (
renderInfo.type !== 'applet-view'returns aWeaveError). Could enable multi-group visibility in future. -
AppClientinterface -- Upstream PR to@holochain/clientto addagentInfo/peerMetaInfotoAppClientwould allow peer discovery in Moss context.