Pollen
Amalgame's peer-to-peer data bus + declarative workflow orchestrator. No central broker, no ZooKeeper cluster, no SPOF — every node talks directly to the others, and they all read the same workflow.json on a shared network mount to know what to do at each step. Sister project to Mosaic: where Mosaic handles your inbound HTTP traffic, Pollen distributes data between your services and coordinates their processing.
Why "Pollen"
Pollen travels without a central coordinator: carried from flower to flower by autonomous carriers, it transports the most valuable thing to propagate — genetic information. Each carrier knows where to go, the pollen knows what to become. That is exactly the execution model we're building: autonomous nodes that emit and receive directly, on a network where coordination lives in the shared data (the workflow.json), not in a conductor.
Done: the Amalgame port has shipped
Pollen was a Node.js prototype (TARMeule). It is now an Amalgame package, amalgame-pollen v0.3.0 — reusable, installable via amc package add pollen, validated by ~200 test assertions. Three repos make up the product:
amalgame-pollen(v0.3.0) — the official AM library: transport, capability registry, and the v3 workflow engine. Embeddable in a Mosaic app or any AM program.pollen— a reference CLI binary (TCP transport), ported from the TARMeule prototype.pollen-manager— theworkflow.jsonweb editor + executions dashboard (Mosaic app, in progress).
Two layers: a peer-to-peer bus and a workflow orchestrator
Pollen doesn't look like RabbitMQ, Kafka or NATS — which are all central brokers. Pollen is closer to a mix of ZeroMQ (for transport) and Argo Workflows (for orchestration), but fully decentralized:
- Transport — the
amalgame-pollenpackage uses long-lived TCP connections, newline-delimited JSON (one message = one line), with an application-level ACK. Direct node to node, no broker in between. - Coordination — a
workflow.jsonfile (schemapollen/v3) on a shared network mount. It describes the actions (abstract topics) and the entries (who reacts to what, and how). Each server reads the file, identifies its role and runs its part.
The workflow.json is the orchestrator. There is no "orchestrator" process to run. The file is declarative, the nodes are autonomous — each one knows what to do at every step.
When to use Pollen
- Local network / LAN — IoT, SCADA, industrial sensors, factory supervision
- Distributed processing pipelines — a transformation chain (acquisition → filtering → aggregation → archiving) across several machines
- Symmetric topologies — no client/server relationship, every node emits and receives
- Zero infra to provision — no dedicated VM, no Docker compose for a broker, no workflow server
- Orchestration in the data — changing the topology = editing one JSON file, no redeploy
- Resilient to network breaks — a node that comes back re-reads
workflow.jsonand resumes its place
workflow.json v3 — the declarative orchestrator
The core idea: no process decides who does what. The decision lives in a JSON file dropped on a shared network mount (NFS, SMB, cluster share). The pollen/v3 schema brings a real control-flow engine — no longer just a static DAG.
// workflow.json — example: a temperature processing chain { "schema": "pollen/v3", "name": "telemetry-pipeline", "version": 1, // actions = abstract topics, resolved via the capability registry "actions": { "filter": { "topic": "temperature.filtered" }, "archive": { "topic": "temperature.archive" }, "alert": { "topic": "ops.alert" } }, // entries = handlers triggered by an incoming topic "entries": [ { "name": "main", "on": "temperature.raw", "do": [ { "type": "call", "action": "filter" }, { "type": "if", "cases": [ { "when": "msg.data.value > 80", "do": { "type": "call", "action": "alert", "mode": "all" } }, { "else": true, "do": { "type": "call", "action": "archive" } } ] } ] } ] }
The v3 engine is a tree-walker that runs a sequence of typed steps:
- ✓
call— invoke an action;mode: "one" | "all"(one provider or all),on_error: "fail" | "log" | "drop" - ✓
if— first-match branching (caseswithwhen+else: true) - ✓
for/while— iterate a list, loop bounded bymaxIter - ✓
set— mutate execution state via a CEL-lite expression - ✓
goto+anchor— jump between entries (withparams/returns, call stack up to 64 frames) and a named resumption point for retry patterns
Changing the topology = editing one JSON file. The loader validates the schema (undefined entries, goto cycles, anchors, expression syntax) before execution.
Amalgame API — a node that follows the workflow
The server-side app is minimal: declare your role and the shared dir, load the v3 workflow, advertise your capabilities, then block in the listen loop. Pollen handles the rest (v3 dispatch, routing to providers, load balancing).
namespace App import Amalgame.Pollen public class Program { public static void Main(string[] args) { Pollen.WorkflowSetSharedDir("./shared") Pollen.WorkflowSetSelf("filter-1", "127.0.0.1", 5000) Pollen.WorkflowLoadV3("./shared/workflow.json") // Advertise this node's capabilities + read the others' registry Pollen.StartCapabilityWriter("filter-1", "127.0.0.1", 5000) Pollen.StartCapabilityReader() Pollen.SetLoadBalance(true) // registry-resolved forwarding Pollen.StartListener(5000) // TCP loop: receive, v3 dispatch, re-emit } }
Inject a message into the workflow from the outside (a sensor, a Mosaic app, a script):
// fire-and-forget Pollen.Publish("proc-01.lan", 5000, "temperature.raw", 1, dataJson) // wait for the application-level ACK (timeout in ms) Pollen.PublishSync("proc-01.lan", 5000, "temperature.raw", 1, dataJson, 2000)
Shipped bricks (from the TARMeule prototype)
The Node.js prototype (github.com/BastienMOUGET/TARMeule) served as the functional reference. Here's what v0.3.0 actually implements:
TCP transport + ACK
TCP connections, JSON envelopes one per line (messageId, type, topic, data, timestamp), application-level ACK. TCP guarantees ordering and no in-stream duplicates.
v3 workflow engine
JSON → AST loader, validator (8 rules), tree-walking dispatcher. Steps call / goto / anchor / if / for / while / set.
CEL-lite expressions
Lexer + Pratt parser + evaluator. Arithmetic, comparisons, booleans, in, ternary; builtins len, int, float, string, contains, startsWith… Paths state.X, params.Y, msg.data.Z.
Capability registry + load balancing
Each node advertises its actions in capabilities/. The workflow's actions are resolved at runtime to a concrete provider, with a power-of-two-choices load balancer.
State + execution tracing
Per-execution state persisted atomically in sharedDir/state/. Every hop is recorded in sharedDir/executions/ — the basis of the manager's live dashboard.
AES-256 encryption
Optional packet encryption (shared key), planned for v0.4.0 once amalgame-crypto stabilizes. Today: cleartext JSON payload (trusted network).
Pollen Manager — the visual editor
pollen-manager is a Mosaic application (served on :3000) that reads/writes workflow.json and surfaces the live executions each node writes into sharedDir/executions/. It is not a control plane: Pollen stays choreography-based (every node knows its part) — the manager edits the score and visualizes runs.
- ✓REST API
GET/PUT /api/workflow(read/write + validation),/api/executions,/api/capabilities,POST /api/inject - ✓SVG DAG render + v3 workflow flowchart
- ◐Drag & drop editor, live execution overlay on the graph, SYNC broadcast on save — in progress
Status: v0.1.0-dev, phase 4.0 scaffold. The build is temporarily blocked by an Amalgame packager limitation (classes in sibling sources files aren't linked yet) — tracked upstream in the compiler.
Why it was ported to Amalgame
The Node.js prototype was functional but heavy to deploy on the target devices (sensors, industrial gateways, PLCs): Node.js and its ~50 MB runtime, process keep-alive, npm install… The AM port gives:
- ✓A single native binary — a few MB, starts in milliseconds
- ✓No runtime to install — deployable on Raspberry Pi, Linux PLCs, Windows embedded
- ✓Bounded RAM, no Node.js GC pauses — predictable real-time behavior
- ✓Reusable — a package, not a frozen binary: embeddable in a Mosaic app (HTTP → Pollen bridge)
- ◐
amalgame-serviceintegration — systemd daemon or native Windows service (upcoming) - ◐Encryption via
amalgame-crypto— will share the AES code of the rest of the ecosystem (v0.4.0)
Roadmap
The core (shipped)
TCP transport + ACK, v3 workflow engine (tree-walker + 8 validation rules), CEL-lite expressions, capability registry + power-of-two load balancing, per-execution state + tracing.
Security & observability
AES-256 encryption (shared key) via amalgame-crypto. Prometheus metrics. Richer schema validation on emit/receive.
QoS & Mosaic bridge
Configurable QoS modes (fire-and-forget / ack-required / at-least-once) per action. Re-landing the Mosaic bridge (expose a Pollen topic as a WebSocket, and vice versa) to wire a web dashboard to sensors.
Identity & mature manager
Public-key authentication (Ed25519) + per-topic ACLs. Manager: drag & drop nodes, live execution overlay, manual intervention (force-ACK, replay).
What Pollen is not
- Not a durable broker — no persistent queue, no Kafka-style replay. A missed message is lost (unless retried within the ACK window).
- Not a full Temporal / Argo Workflows — the v3 engine does control flow (if/for/while/goto + state), but no durable execution history, no transactional saga with rollback.
- Not multi-datacenter — designed for LAN or VPN-LAN. Not meant to cross the open Internet.
- Not a Mosaic replacement — to expose a web API to clients outside the LAN, that's Mosaic.
- Not a transactional MOM — no cross-topic atomic transactions, no dead-letter queue, no global ordering guarantees.
For those cases, the ecosystem offers the right third-party tools: Kafka for durable replay, RabbitMQ for transactional workflows, MQTT for broker-centralized IoT. Pollen plays in the "real-time LAN pipeline, no broker, no orchestration server" slot.
Try Pollen
Pollen is ported to Amalgame (package v0.3.0) and usable. The visual manager and AES encryption follow.