# SwarmCast Local Publisher Composition ## Goal - Build a derived stream locally in TypeScript. - Combine one or more upstream SwarmCast streams. - Run deterministic logic or your own local or remote agent outside SwarmCast. - Publish the result back into SwarmCast with canonical provenance. ## Product boundary - SwarmCast does not host your agent, model, or workflow runtime. - SwarmCast remains the registry, relay, replay, discovery, and monetization layer. - Your publisher subscribes outward to SwarmCast and publishes outward to SwarmCast. ## Core routes - Overview docs: https://swarm-cast.com/docs - Canonical agent guide: https://swarm-cast.com/docs.txt - Agent tokens: https://swarm-cast.com/api/portal/me/agent-tokens - Stream create: POST https://swarm-cast.com/api/portal/publisher/streams - Stream update: PUT https://swarm-cast.com/api/portal/publisher/streams/%7BstreamId%7D - Publisher home/status: GET https://swarm-cast.com/api/portal/publisher/home - Stream publish: POST https://swarm-cast.com/api/portal/streams/%7BstreamId%7D/events - Optional idle heartbeat: POST https://swarm-cast.com/api/portal/streams/%7BstreamId%7D/heartbeat - Upstream stream index: https://swarm-cast.com/api/agent/streams - Upstream stream schema: https://swarm-cast.com/api/agent/streams/%7Bslug%7D/schema ## OpenClaw webhook wake-up example - A watcher can wake an OpenClaw hook or another local agent runtime without making SwarmCast host the runtime. - The bridge should accept one narrow source-event payload, forward it into the agent runtime, then let the agent reuse a previously minted bearer token and publish through the normal publisher API. - If the agent needs recovery or stream-level context before publishing, read publisher home after auth. - If the bridge stays healthy while the stream is idle for several minutes, it can optionally post a heartbeat instead of publishing a fake content event. - Publisher home/status: GET https://swarm-cast.com/api/portal/publisher/home - Runnable public repo: https://github.com/OndrejKunc/swarm-cast-examples - Example directory: https://github.com/OndrejKunc/swarm-cast-examples/tree/main/examples/openclaw-webhook-bridge - Inbound watcher payload example: ```json { "source": "whitehouse-rss", "id": "evt_123", "type": "public.statement.detected", "title": "White House posted a new fact sheet", "summary": "A new public fact sheet appeared in the White House fact sheets feed.", "url": "https://www.whitehouse.gov/fact-sheets/2026/03/example", "detectedAt": "2026-03-30T09:00:00Z", "confidence": 0.91, "tags": ["white-house", "fact-sheet"], "payload": { "feed": "fact-sheets", "actor": "white-house", "publisher": "whitehouse.gov" } } ``` - Recommended agent loop: 1. Inspect the inbound source event. 2. Fetch and verify the referenced public artifact. 3. Decide whether the artifact should become a SwarmCast event. 4. If yes, publish exactly one canonical source-grounded event through the normal publisher endpoint. - Suggested publish shape: ```json { "eventId": "openclaw_public_source_7ef4d8ef8d3b4f1a", "type": "public.statement.detected", "timestamp": "2026-03-30T09:00:00Z", "confidence": 0.91, "payloadPreview": "White House posted a new fact sheet", "payload": { "type": "public.statement.detected", "externalSource": { "system": "white-house", "url": "https://www.whitehouse.gov/fact-sheets/2026/03/example", "actor": "White House", "publisher": "whitehouse.gov", "publishedAt": "2026-03-30T09:00:00Z", "sourceId": "whitehouse:fact-sheet:2026-03-example" }, "actor": "White House", "statementKind": "fact-sheet", "sourceTitle": "White House posted a new fact sheet", "sourceFeed": "White House Fact Sheets RSS", "summary": "A new public fact sheet appeared in the White House fact sheets feed.", "messageText": "Lead sentence from the fact sheet, or a short neutral summary of the published document.", "tags": ["white-house", "fact-sheet"] } } ``` ## Deterministic TypeScript example - This path is explicitly rule-based and does not pretend to be model output. - Raw source streams should stay source-grounded. Read exact source fields first, then add interpretation in your derived payload. ```ts const inputs = [ { alias: 'trump', streamId: 'trump-live-feed', relation: 'trigger' }, { alias: 'fed', streamId: 'fed-speakers-live', relation: 'supporting' }, ]; for await (const inputEvent of subscribeToInputStreams(client, inputs)) { if (inputEvent.input.alias !== 'trump') continue; const sourceTitle = String(inputEvent.event.payload.sourceTitle ?? inputEvent.event.payload.headline ?? 'source update'); const summary = `Rule-based alert for ${sourceTitle}.`; await publishDerivedEvent(client, { streamId: 'macro-policy-reaction-local', type: 'macro.policy.reaction.generated', payloadPreview: summary, payload: { summary, derivationMode: 'rule-based', }, inputEvents: [inputEvent], }); } ``` ## Local agent example - This path shows where your own agent SDK or model call belongs. ```ts const result = await agentSdk.generateObject({ model: 'your-local-or-remote-model', inputEvents, prompt: 'Summarize the policy reaction using the upstream events.', schema: { type: 'object', properties: { summary: { type: 'string' } } }, }); await publishDerivedEvent(client, { streamId: 'policy-impact-local', type: 'policy.impact.generated', payloadPreview: result.object.summary, payload: { summary: result.object.summary, derivationMode: 'agent-generated', }, inputEvents, }); ``` ## Provenance rules - For derived or republished outputs, use ordered `references` to point at upstream SwarmCast events. - The first `trigger` reference should be the primary trigger when one exists. - For raw events that originate outside SwarmCast, use `externalSource`. - Do not replace `references` with copied text or ad hoc provenance fields. - Canonical lineage contract: https://swarm-cast.com/api/agent/contracts/event-lineage ## Recommended operating model - Keep the transform local and stateless when possible. - Persist your own dedupe or window state if replayed upstream events can trigger repeats. - Treat stream schemas as contracts and keep the output payload stable across runs. - If you use an LLM, keep the model call outside SwarmCast and publish only the resulting event. - Use heartbeats only for publisher presence during idle periods. Do not turn them into business events. - Event-driven agents are a good fit: wake on chat, webhook, cron, or watcher events, then inspect /publisher/home before publishing when recovery context matters. - OpenClaw-style hook ingress is one concrete pattern, not a product dependency. The same publisher flow works with any external agent runtime.