conflux-state 0.8.0__tar.gz

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.
Files changed (47) hide show
  1. conflux_state-0.8.0/LICENSE +21 -0
  2. conflux_state-0.8.0/PKG-INFO +635 -0
  3. conflux_state-0.8.0/README.md +591 -0
  4. conflux_state-0.8.0/conflux/__init__.py +87 -0
  5. conflux_state-0.8.0/conflux/actions.py +87 -0
  6. conflux_state-0.8.0/conflux/app.py +274 -0
  7. conflux_state-0.8.0/conflux/client.py +207 -0
  8. conflux_state-0.8.0/conflux/crdts.py +266 -0
  9. conflux_state-0.8.0/conflux/framework.py +106 -0
  10. conflux_state-0.8.0/conflux/integrations/__init__.py +38 -0
  11. conflux_state-0.8.0/conflux/integrations/autogen.py +319 -0
  12. conflux_state-0.8.0/conflux/integrations/base.py +148 -0
  13. conflux_state-0.8.0/conflux/integrations/crewai.py +567 -0
  14. conflux_state-0.8.0/conflux/integrations/langgraph.py +726 -0
  15. conflux_state-0.8.0/conflux/net.py +89 -0
  16. conflux_state-0.8.0/conflux/protocol.py +55 -0
  17. conflux_state-0.8.0/conflux/server.py +694 -0
  18. conflux_state-0.8.0/conflux/state.py +104 -0
  19. conflux_state-0.8.0/conflux/storage.py +192 -0
  20. conflux_state-0.8.0/conflux/validate.py +315 -0
  21. conflux_state-0.8.0/conflux/websocket.py +311 -0
  22. conflux_state-0.8.0/conflux_state.egg-info/PKG-INFO +635 -0
  23. conflux_state-0.8.0/conflux_state.egg-info/SOURCES.txt +45 -0
  24. conflux_state-0.8.0/conflux_state.egg-info/dependency_links.txt +1 -0
  25. conflux_state-0.8.0/conflux_state.egg-info/entry_points.txt +2 -0
  26. conflux_state-0.8.0/conflux_state.egg-info/requires.txt +23 -0
  27. conflux_state-0.8.0/conflux_state.egg-info/top_level.txt +1 -0
  28. conflux_state-0.8.0/pyproject.toml +68 -0
  29. conflux_state-0.8.0/setup.cfg +4 -0
  30. conflux_state-0.8.0/tests/test_app.py +160 -0
  31. conflux_state-0.8.0/tests/test_convergence.py +108 -0
  32. conflux_state-0.8.0/tests/test_crdts.py +105 -0
  33. conflux_state-0.8.0/tests/test_demos.py +23 -0
  34. conflux_state-0.8.0/tests/test_framework.py +39 -0
  35. conflux_state-0.8.0/tests/test_integrations.py +332 -0
  36. conflux_state-0.8.0/tests/test_namespace.py +83 -0
  37. conflux_state-0.8.0/tests/test_ops.py +69 -0
  38. conflux_state-0.8.0/tests/test_phase4.py +192 -0
  39. conflux_state-0.8.0/tests/test_probabilistic.py +47 -0
  40. conflux_state-0.8.0/tests/test_prod_hardening.py +272 -0
  41. conflux_state-0.8.0/tests/test_prometheus.py +76 -0
  42. conflux_state-0.8.0/tests/test_replay.py +44 -0
  43. conflux_state-0.8.0/tests/test_security_review.py +212 -0
  44. conflux_state-0.8.0/tests/test_server.py +131 -0
  45. conflux_state-0.8.0/tests/test_spec_conformance.py +220 -0
  46. conflux_state-0.8.0/tests/test_validate.py +252 -0
  47. conflux_state-0.8.0/tests/test_websocket.py +204 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 lolllll212
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,635 @@
1
+ Metadata-Version: 2.4
2
+ Name: conflux-state
3
+ Version: 0.8.0
4
+ Summary: Deterministic, lock-free CRDT shared state protocol for concurrent AI agents, with drop-in adapters for LangGraph, CrewAI, and AutoGen.
5
+ Author: Conflux Maintainers
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/lolllll212/Conflux
8
+ Project-URL: Repository, https://github.com/lolllll212/Conflux
9
+ Project-URL: Documentation, https://github.com/lolllll212/Conflux#readme
10
+ Project-URL: Issues, https://github.com/lolllll212/Conflux/issues
11
+ Keywords: multi-agent,ai-agents,crdt,distributed-systems,shared-state,langgraph,crewai,autogen,lattice,state-management
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Topic :: System :: Distributed Computing
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: crypto
27
+ Requires-Dist: cryptography>=3.4; extra == "crypto"
28
+ Provides-Extra: langgraph
29
+ Requires-Dist: langgraph>=0.2.0; extra == "langgraph"
30
+ Provides-Extra: crewai
31
+ Requires-Dist: crewai>=0.80.0; extra == "crewai"
32
+ Provides-Extra: autogen
33
+ Requires-Dist: autogen-agentchat>=0.4.0; extra == "autogen"
34
+ Requires-Dist: autogen-core>=0.4.0; extra == "autogen"
35
+ Provides-Extra: integrations
36
+ Requires-Dist: langgraph>=0.2.0; extra == "integrations"
37
+ Requires-Dist: crewai>=0.80.0; extra == "integrations"
38
+ Requires-Dist: autogen-agentchat>=0.4.0; extra == "integrations"
39
+ Requires-Dist: autogen-core>=0.4.0; extra == "integrations"
40
+ Provides-Extra: test
41
+ Requires-Dist: pytest; extra == "test"
42
+ Requires-Dist: cryptography>=3.4; extra == "test"
43
+ Dynamic: license-file
44
+
45
+ # Conflux
46
+
47
+ [![PyPI](https://img.shields.io/pypi/v/conflux-state.svg)](https://pypi.org/project/conflux-state/)
48
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/)
49
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
50
+
51
+ A deterministic state protocol for autonomous multi-agent systems. State is not a
52
+ set of rows — it is the join of an evolving stream of signed, replayable actions.
53
+ Agents write concurrently to the same shared state with no table locks, no
54
+ two-phase commit, and no central coordinator; every node that has seen the same
55
+ actions converges to the identical state by construction.
56
+
57
+ ## Installation
58
+
59
+ Install the zero-dependency core package from PyPI (imported in Python as `conflux`):
60
+
61
+ ```bash
62
+ pip install conflux-state
63
+ ```
64
+
65
+ Optional extras for Ed25519 asymmetric signing and framework integrations:
66
+
67
+ ```bash
68
+ pip install "conflux-state[crypto]" # Ed25519 signing via cryptography
69
+ pip install "conflux-state[langgraph]" # LangGraph CheckpointSaver + Store
70
+ pip install "conflux-state[crewai]" # CrewAI Memory StorageBackend + Flow + Tools
71
+ pip install "conflux-state[autogen]" # AutoGen Memory + FunctionTools
72
+ pip install "conflux-state[integrations]" # All framework integrations
73
+ ```
74
+
75
+ ## Core use case: shared agent state
76
+
77
+ The headline problem conflux solves is **shared agent state**: a few dozen
78
+ agents of one application reading and mutating the same counters, registers,
79
+ and sets — fleet progress, job counters, leader/slot election, capability
80
+ flags, hand-off pointers — with signed writes, durable replay, and crash
81
+ recovery, on a mesh of commodity TCP/WebSocket nodes. Two adjacent use cases
82
+ ride on the same fabric: **workflow state** (task queues and step status
83
+ through `TaskStore` and deterministic `Workflow`) and a **replicated memory /
84
+ knowledge layer** (an agent's shared facts as register/set/string slots).
85
+ All three are one storage geometry; the product pitches the agent-state head.
86
+
87
+ This is not a blockchain and not a distributed SQL system. It is a replicated
88
+ state fabric built on mathematically convergent data structures (CRDTs / lattice
89
+ theory) — a coordination layer for distributed AI workflows.
90
+
91
+ ## Product definition and scope
92
+
93
+ Conflux is a **small-crew deterministic state fabric** for a few dozen agents
94
+ of a single application: signed, replayable, eventually-consistent shared
95
+ agent state with durable journaling and crash recovery. The protocol is
96
+ specified in [`docs/PROTOCOL.md`](docs/PROTOCOL.md); every claim in it maps to
97
+ a test (conformance index at the end of the spec). The signing, identity, and
98
+ storage subsystems are reviewed in [`docs/SECURITY.md`](docs/SECURITY.md),
99
+ which also states the trust boundary and the open findings. Conflux is
100
+ released under the [MIT License](LICENSE).
101
+
102
+ Scope — what it is:
103
+
104
+ - deterministic convergence: same action set ⇒ same `state_hash`, in any order;
105
+ - signed identity: HMAC (zero deps) or Ed25519, plus op/namespace/quota rules;
106
+ - durability: fsynced journal + cumulative snapshots that survive a hard crash;
107
+ - TCP and WebSocket transports, TLS on both, gossip between peer nodes.
108
+
109
+ Explicitly out of scope (backlog, not promised):
110
+
111
+ - linearizable reads, multi-region federation,
112
+ general-scale KV, blockchain-style global ordering.
113
+
114
+ ## Problem
115
+
116
+ Classic distributed databases serialize writes: a crew of agents updating the
117
+ same operation counters, queue pointers, capability flags, or hand-off state
118
+ either blocks itself (latency) or deadlocks. Locks scale poorly once agents
119
+ outnumber transactions — and agent crews are exactly that regime: many writers,
120
+ one small shared state.
121
+
122
+ ## Approach
123
+
124
+ Conflux treats shared state not as static rows but as the join of an evolving stream
125
+ of probabilistic actions. Every value living in the store is an element of a
126
+ join-semilattice, so any two replicas can merge their states with a single
127
+ commutative, associative, idempotent operation — the lattice join. Because a join is
128
+ order-independent, every replica that has seen the same set of actions converges to
129
+ the identical state, no matter the delivery order.
130
+
131
+ ```
132
+ state = join(contribution(action_1), ..., contribution(action_n))
133
+ ```
134
+
135
+ ## Core concepts
136
+
137
+ ### The lattice
138
+
139
+ `Lattice` is the abstract contract: every CRDT implements `merge(other)` (the join),
140
+ `read()`, and `canonical()` (a deterministic fingerprint used for hashing). The join
141
+ laws are verified directly in `tests/test_crdts.py`:
142
+
143
+ - idempotent: `a ⨆ a = a`
144
+ - commutative: `a ⨆ b = b ⨆ a`
145
+ - associative: `(a ⨆ b) ⨆ c = a ⨆ (b ⨆ c)`
146
+
147
+ ### CRDT types
148
+
149
+ | Type | Field | Meaning |
150
+ | --------------- | ------------- | --------------------------------------------------- |
151
+ | `GCounter` | count per key | grow-only counter, merge = elementwise max |
152
+ | `PNCounter` | (p, n) slots | counters with increments and decrements |
153
+ | `LWWRegister` | value, stamp | last-writer-wins register, stamp = (tick, agent) |
154
+ | `GSet` | set | grows only, merge = union |
155
+ | `ORSet` | tags, values | add/remove with tombstones, merge = union |
156
+ | `ORMap` | key -> CRDT | grows-only keys over any of the above |
157
+
158
+ Register values are ordinary JSON-ish values; equal stamps (or equal set tags, an
159
+ invalid protocol usage) are resolved deterministically by canonical order so every
160
+ replica still picks the same winner.
161
+
162
+ ### Actions and determinism
163
+
164
+ Agents do not mutate shared rows. They emit immutable `Action`s, each with a globally
165
+ unique `action_id`, an `agent`, a logical `tick`, an operation, and params:
166
+
167
+ ```python
168
+ Action(action_id="act-agent-1-42", agent="agent-1", tick=42,
169
+ op="counter_inc", key="orders", params={"by": 5})
170
+ ```
171
+
172
+ Probabilistic actions carry a `weight` param. Whether an action "fires" is decided by
173
+ `SHA-256(action_id, key)` projected onto a fixed-point 32-bit scale — a pure function
174
+ every replica can compute, so probabilistic inference produces identical state
175
+ everywhere without any coordination.
176
+
177
+ ### Convergence protocol
178
+
179
+ - Each agent owns a `Replica` — its local join of the actions it has seen. Its
180
+ journal is append-only and de-duplicated by `action_id`.
181
+ - `reconcile(a, b)`: the two replicas exchange journals and absorb what they have not
182
+ seen. Absorption is exactly the lattice join.
183
+ - `converge_all(replicas)`: full-mesh anti-entropy sweep (for tests and small crews).
184
+ - `converge_by_gossip(replicas, rounds)`: randomized pairwise anti-entropy (for
185
+ large, partial meshes).
186
+ - `Cluster` / `DistributedApp` / `InMemoryTransport`: a thin network abstraction to
187
+ demonstrate the protocol over message passing.
188
+
189
+ Any two replicas that have both absorbed the union of available actions are
190
+ provably identical: identical action sets + identical join = identical state. A
191
+ consensus fingerprint, `state_hash(state)`, is a SHA-256 over the canonical
192
+ serialization.
193
+
194
+ ## Quick start
195
+
196
+ ```python
197
+ from conflux import Cluster, InMemoryTransport
198
+
199
+ cluster = Cluster({"agent-a", "agent-b", "agent-c"}, transport=InMemoryTransport())
200
+ cluster.node("agent-a").counter_inc("orders", 5)
201
+ cluster.node("agent-b").counter_inc("orders", 3)
202
+ cluster.node("agent-c").register_set_weighted("risk.appetite", "low", weight=0.6)
203
+ cluster.gossip_round()
204
+
205
+ assert cluster.read("agent-a", "orders") == 8
206
+ assert cluster.read("agent-b", "orders") == 8
207
+ ```
208
+
209
+ Deterministic replay from a journal:
210
+
211
+ ```python
212
+ from conflux import state_hash, replay
213
+ from conflux.protocol import journal_from_json, journal_to_json
214
+
215
+ blob = journal_to_json(cluster.node("agent-a"))
216
+ actions = journal_from_json(blob)
217
+ # fold in any order, every time, always the same state:
218
+ assert state_hash(replay(actions).state) == state_hash(cluster.node("agent-b").state)
219
+ ```
220
+
221
+ ## Protocol hardening: versioning, signatures, authority
222
+
223
+ Actions travel as envelopes that any node can verify before they touch state.
224
+
225
+ ```json
226
+ {
227
+ "action": { "action_id": "act-a-1", "agent": "a", "tick": 1, "op": "counter_inc",
228
+ "key": "orders", "params": {"by": 3}, "version": 1 },
229
+ "signed_by": "a",
230
+ "signature": "<signature over canonical action bytes>",
231
+ "alg": "hmac-sha256"
232
+ }
233
+ ```
234
+
235
+ - **Versioning.** Every action carries `version`; the validator rejects unknown
236
+ versions, so protocol changes are explicit (current: `1`).
237
+ - **Signatures.** `sign_action(action, secret)` produces an HMAC-SHA256 over the
238
+ canonical (key-sorted) JSON of the action (zero dependencies). For asymmetric
239
+ identity, pass `alg="ed25519"` with a raw private key — `generate_ed25519_keypair()`
240
+ returns `(private, public)`, `verify_action(envelope, public, "ed25519")` checks it.
241
+ Ed25519 requires the optional `cryptography` package; HMAC needs nothing.
242
+ - **Identity + authority.** `AgentRegistry` maps agent ids to a secret (HMAC) *or* a
243
+ public key (Ed25519), plus allowed op sets and per-agent write quotas.
244
+ `validate_envelope` checks signer identity, signature, authorization, and quota
245
+ before a node ever merges, and refuses envelopes whose algorithm does not match
246
+ the agent's registered key.
247
+ - **Schema.** `validate_action` enforces op-specific payload rules (counter `by`,
248
+ JSON-safe register/set values, non-empty tags, `weight` in `[0, 1]`).
249
+
250
+ A server equipped with a registry refuses unverified, unauthorized, or
251
+ quota-exhausted submits (`{"type": "error", "reason": "..."}`); a server without
252
+ one still schema-validates but runs in trust-less dev mode. Note that the
253
+ registry guards the agent `submit` path only — the peer gossip path is trusted
254
+ at the network layer, and every node's listen port must therefore be treated
255
+ as a peer-trusted boundary. [`docs/SECURITY.md`](docs/SECURITY.md) walks
256
+ through what the signing, identity, and storage layers do and do not
257
+ guarantee, with each property mapped to the code and tests that enforce it.
258
+
259
+ ```python
260
+ from conflux import AgentRegistry, Client, Server
261
+
262
+ registry = AgentRegistry()
263
+ registry.register("driver", "s3cr3t", ops={"counter_inc", "lww_set"}, quota=1000)
264
+ server = Server("n1", ("127.0.0.1", 7001), registry=registry)
265
+ server.start()
266
+
267
+ client = Client("127.0.0.1", 7001, agent_id="driver", secret="s3cr3t")
268
+ client.counter_inc("orders", 5) # signed, authorized, quota-counted
269
+ ```
270
+
271
+ ## Running the tests
272
+
273
+ ```bash
274
+ python -m pytest
275
+ ```
276
+
277
+ The suite covers join laws, threaded lock-free convergence, merge-order
278
+ determinism, probabilistic determinism, journal round-tripping, the
279
+ network-framework gossip path, the networked backend (client round-trip,
280
+ two-node gossip convergence, restart persistence), signed-envelope authority,
281
+ namespaces, the WebSocket transport (RFC6455 framing, WS node convergence,
282
+ discovery, batching, backoff), the application layer (events, watchers,
283
+ task state, deterministic workflows), and the operational surface (TLS framing
284
+ over tcp + ws, signed/verified journals, membership + partition heal, and
285
+ cumulative snapshot compaction).
286
+
287
+ ## Application layer: workflows, tasks, events, watchers
288
+
289
+ Everything sits on the same replicated fabric, so application state converges
290
+ and replays exactly like raw CRDT state.
291
+
292
+ - **`EventBus`** — subscribe to the action stream by op, exact key, or key
293
+ prefix; handlers get `(action, origin)` where origin is `local` (this node's
294
+ own emit) or `absorb` (received from a peer). `Server.subscribe(...)` wires it
295
+ onto the live wire path.
296
+ - **`Watcher`** — value-level snapshots: `watch(key, handler)` fires when a
297
+ key's effective value changes, with the previous value.
298
+ - **`TaskStore`** — replicated task execution state under `tasks/<id>`
299
+ (created → running → step → done/failed), each transition a signed,
300
+ deterministic `lww_set`.
301
+ - **`Workflow` / `WorkflowRunner`** — ordered steps (`counter_inc`, `lww_set`,
302
+ `orset_add`, `orset_remove`, `noop`) against keys, with optional
303
+ `weight`-gated steps decided by the same deterministic hash every replica can
304
+ reproduce. Identical inputs always emit identical action logs.
305
+
306
+ ```python
307
+ from conflux import Server, Workflow, WorkflowRunner, WorkflowStep, TaskStore
308
+
309
+ server = Server("n1", ("127.0.0.1", 7001))
310
+ server.start()
311
+ server.subscribe(lambda action, origin: print("event:", origin, action.op),
312
+ op="counter_inc")
313
+ server.watch("wf/shipments", lambda value, key, old: print("watched:", value))
314
+
315
+ workflow = Workflow("fulfill", [
316
+ WorkflowStep("reserve", "counter_inc", key="wf/capacity", params={"by": 1}),
317
+ WorkflowStep("ship", "orset_add", key="wf/shipments", params={"value": "carrier-17"}),
318
+ ])
319
+ task = WorkflowRunner(server.replica, workflow).run("fulfill-1", "fulfill",
320
+ inputs={"sku": "A-1"})
321
+ ```
322
+
323
+ ## Framework integrations: LangGraph, CrewAI, AutoGen
324
+
325
+ `conflux.integrations` ships drop-in adapters that let popular orchestration
326
+ frameworks use Conflux as a **lock-free, convergent shared state backend**.
327
+ Every adapter accepts any Conflux runtime object (`Replica`, `Cluster` node,
328
+ `Server`, or networked/signed `Client`), works out of the box with zero
329
+ third-party dependencies, and automatically inherits each framework's native
330
+ base classes when installed.
331
+
332
+ ### LangGraph (`ConfluxCheckpointSaver` + `ConfluxStore`)
333
+
334
+ Persist, fork, and resume `StateGraph` checkpoints across separate Conflux
335
+ nodes, and share cross-thread items, CRDT counters, and sets without DB locks:
336
+
337
+ ```python
338
+ from langgraph.graph import StateGraph
339
+ from conflux import Client
340
+ from conflux.integrations.langgraph import ConfluxCheckpointSaver, ConfluxStore
341
+
342
+ client = Client("127.0.0.1", 7001, agent_id="planner", secret="s3cr3t")
343
+ checkpointer = ConfluxCheckpointSaver(client)
344
+ store = ConfluxStore(client)
345
+
346
+ graph = builder.compile(checkpointer=checkpointer, store=store)
347
+ graph.invoke({"topic": "CRDTs"}, config={"configurable": {"thread_id": "run-42"}})
348
+
349
+ # Concurrent CRDT counters & sets across parallel LangGraph workers:
350
+ store.counter_inc(("metrics", "research"), "queries_executed", by=1)
351
+ ```
352
+
353
+ Runnable example: [`examples/langgraph_shared_state.py`](examples/langgraph_shared_state.py)
354
+
355
+ ### CrewAI (`ConfluxCrewStorage`, `ConfluxFlowPersistence`, `create_crewai_tools`)
356
+
357
+ Plug Conflux into CrewAI's unified `Memory` (`StorageBackend` protocol),
358
+ checkpoint `@persist` Flows across nodes, and give agents direct CRDT tools:
359
+
360
+ ```python
361
+ from crewai import Agent, Crew
362
+ from crewai.memory import Memory
363
+ from conflux import Client
364
+ from conflux.integrations.crewai import (
365
+ ConfluxCrewStorage,
366
+ ConfluxFlowPersistence,
367
+ create_crewai_tools,
368
+ )
369
+
370
+ client = Client("127.0.0.1", 7001, agent_id="researcher", secret="s3cr3t")
371
+ storage = ConfluxCrewStorage(client)
372
+ tools = create_crewai_tools(client, namespace="crew.")
373
+
374
+ researcher = Agent(role="Researcher", goal="...", backstory="...", tools=tools)
375
+ crew = Crew(agents=[researcher], tasks=[...], memory=Memory(storage=storage))
376
+ ```
377
+
378
+ Runnable example: [`examples/crewai_shared_memory.py`](examples/crewai_shared_memory.py)
379
+
380
+ ### AutoGen (`ConfluxAutoGenMemory` + `create_autogen_tools`)
381
+
382
+ Equip AutoGen v0.4+/v0.7+ `AssistantAgent` teams with a replicated
383
+ `autogen_core.memory.Memory` store that injects converged facts + live CRDT
384
+ keys into `ChatCompletionContext`, alongside native `FunctionTool`s:
385
+
386
+ ```python
387
+ from autogen_agentchat.agents import AssistantAgent
388
+ from conflux import Client
389
+ from conflux.integrations.autogen import ConfluxAutoGenMemory, create_autogen_tools
390
+
391
+ client = Client("127.0.0.1", 7001, agent_id="coder", secret="s3cr3t")
392
+ shared_memory = ConfluxAutoGenMemory(client, state_watch_prefix="team.")
393
+ shared_tools = create_autogen_tools(client, namespace="team.")
394
+
395
+ agent = AssistantAgent(
396
+ name="coder",
397
+ model_client=model_client,
398
+ memory=[shared_memory],
399
+ tools=shared_tools,
400
+ )
401
+ ```
402
+
403
+ Runnable example: [`examples/autogen_shared_state.py`](examples/autogen_shared_state.py)
404
+
405
+ ## Publishing to PyPI (Trusted Publishing / OIDC)
406
+
407
+ Conflux is packaged as **`conflux-state`** on PyPI (imported as `conflux`) and
408
+ ships ready-to-use GitHub Actions workflows in
409
+ [`docs/github-workflows/publish.yml`](docs/github-workflows/publish.yml) and
410
+ [`docs/github-workflows/ci.yml`](docs/github-workflows/ci.yml) using PyPI
411
+ Trusted Publishing (OIDC — no long-lived API tokens required):
412
+
413
+ 1. Move or copy the workflows into `.github/workflows/`:
414
+ ```bash
415
+ mkdir -p .github/workflows && cp docs/github-workflows/*.yml .github/workflows/
416
+ ```
417
+ 2. On [pypi.org/manage/account/publishing/](https://pypi.org/manage/account/publishing/),
418
+ add a **Pending Publisher**:
419
+ - **PyPI Project Name**: `conflux-state`
420
+ - **Owner**: `lolllll212`
421
+ - **Repository name**: `Conflux`
422
+ - **Workflow name**: `publish.yml`
423
+ - **Environment name**: `pypi`
424
+ 3. Tag and push a release (or publish a GitHub Release):
425
+ ```bash
426
+ git tag v0.8.0 && git push origin v0.8.0
427
+ ```
428
+ GitHub Actions runs the full test suite, verifies the wheel + sdist via
429
+ `twine check --strict`, and publishes `conflux-state` to PyPI automatically.
430
+
431
+ ## Operations: metrics, admin stats, benchmarks
432
+
433
+ Every server exposes a live `metrics()` dict — absorbed/submitted actions,
434
+ journal length, links, outbound dials, discovered peers, known members, uptime,
435
+ current `state_hash` — and answers the wire query `{"type": "admin", "what": "stats"}`
436
+ with the same payload, so monitoring can poll nodes without a driver agent.
437
+
438
+ For Prometheus, the same data is rendered in text exposition format — no
439
+ dependencies — and served three ways: `Server.prometheus()`, the wire query
440
+ `{"type": "admin", "what": "prometheus"}` (returns the body over the framed
441
+ connection), and an embedded stdlib HTTP listener:
442
+
443
+ ```bash
444
+ python -m conflux.server --id alpha --listen 127.0.0.1:7001 --metrics-http
445
+ # -> metrics http on http://127.0.0.1:<ephemeral>/metrics
446
+ ```
447
+
448
+ ```python
449
+ server = Server("alpha", ("127.0.0.1", 7001)).start()
450
+ server.start_metrics_http("0.0.0.0", 9101) # scrape http://<node>:9101/metrics
451
+ ```
452
+
453
+ Counters (`conflux_actions_absorbed_total`, `..._submitted_total`) and gauges
454
+ (links, outbound, peers, discovered, members, journal_actions, uptime,
455
+ state-hash info label) are labeled with `node_id`:
456
+
457
+ ```bash
458
+ python benchmarks/bench.py # replay, cluster, tcp + ws round-trips
459
+ python benchmarks/bench.py --quick # small sizes for CI
460
+ ```
461
+
462
+ Example (previous run, this machine): ~168k actions/s journal replay,
463
+ ~38k actions/s full-mesh cluster convergence, ~0.13 ms single-agent
464
+ submit→ack round-trip over TCP and ~0.17 ms over WebSocket.
465
+
466
+ The real agent-state app, in one file: `examples/agent_crew_demo.py` — six
467
+ signed agents share state (`crew.work_done`, check-in set, leader slot) across
468
+ a 3-node mesh, then put the runtime through the failure model it was hardened
469
+ for: a hard crash, continued writes through the partition, restart from the
470
+ signed snapshot + journal tail, keyless reload refused, and an out-of-namespace
471
+ write refused with no state mutation. `examples/durable_crew_demo.py` shows the
472
+ same crash-recovery cycle on a single agent. Supporting demos:
473
+ `examples/workflow_demo.py` (tasks/events/watchers over a cluster) and
474
+ `examples/deploy_multi_node.py` (signed Ed25519 crews over TCP and WS meshes).
475
+
476
+ ## Backend: networked, persistent nodes
477
+
478
+ The reference implementation ships with a multi-node backend so agents in
479
+ different processes (or different machines) can converge. Two transports are
480
+ built in: the default framed TCP and `--transport ws`, a dependency-free
481
+ RFC6455 WebSocket transport (`conflux.websocket`). Set `transport="ws"` on the
482
+ `Server`/`Client` to use it.
483
+
484
+ ### Run a node
485
+
486
+ ```bash
487
+ python -m conflux.server --id alpha --listen 127.0.0.1:7001 --data ./data/alpha
488
+ python -m conflux.server --id beta --listen 127.0.0.1:7002 --peers 127.0.0.1:7001 --data ./data/beta
489
+ ```
490
+
491
+ Use `--peers host:port,host:port` to list neighbors. Gossip links are
492
+ bidirectional once established, and each node replays its persisted journal on
493
+ startup.
494
+
495
+ ### TLS framing (tls:// and wss://)
496
+
497
+ Pass a PEM certificate to encrypt every socket with the stdlib `ssl` module —
498
+ both the framed TCP transport and the RFC6455 WebSocket transport, for agent
499
+ clients and peer-to-peer gossip alike:
500
+
501
+ ```bash
502
+ python -m conflux.server --id alpha --listen 127.0.0.1:7001 \
503
+ --cert ./cert.pem --key ./key.pem --data ./data/alpha
504
+ ```
505
+
506
+ ```python
507
+ server = Server("alpha", ("127.0.0.1", 7001), tls=("cert.pem", "key.pem")).start()
508
+ client = Client("127.0.0.1", 7001, tls=("cert.pem", "key.pem", "cert.pem")) # ca = 3rd element
509
+ ```
510
+
511
+ Omitting the CA in the client's `tls=` tuple disables peer verification (self-signed
512
+ clusters); `tls=True` uses the system CAs, and `tls="ca.pem"` verifies against that CA.
513
+
514
+ ### Membership and partitions
515
+
516
+ Peers announce their node id, address, peer list, and member view on
517
+ handshake; every server maintains a `members` view (see `metrics()["members"]`
518
+ and `members_view()`). When a neighbor dies, the receive loop drops the link
519
+ and the gossip loop re-dials it with exponential backoff. Links that go silent
520
+ without closing (a hung or partitioned peer) are liveness-checked and dropped
521
+ after `--stale-after` seconds (default 3× `gossip_interval`), then re-dialed —
522
+ so a healed partition re-converges automatically once the peer is reachable
523
+ again (see `tests/test_phase4.py::test_partition_reconnect`).
524
+
525
+ ### Signed, compacted journals
526
+
527
+ ```bash
528
+ python -m conflux.server --id alpha --listen 127.0.0.1:7001 \
529
+ --cert ./cert.pem --key ./key.pem --journal-key "$(openssl rand -hex 16)" --data ./data/alpha
530
+ ```
531
+
532
+ Every journal line carries `"sig"` — an HMAC-SHA256 over the canonical action
533
+ bytes — and every snapshot carries the same over its `{seq, root}` payload.
534
+ Replay verifies both; a tampered log or snapshot raises instead of silently
535
+ restoring a corrupt state. Snapshots are **cumulative**: each compaction folds
536
+ the current root into the previous generation via the lattice join and emits a
537
+ single merged summary, pruning older generations, so a crash mid-history never
538
+ loses committed state.
539
+
540
+ ### Write and read from an agent
541
+
542
+ ```python
543
+ from conflux import Client
544
+
545
+ client = Client("127.0.0.1", 7001, agent_id="agent-42")
546
+ client.counter_inc("bookings.seats", 2)
547
+ client.register_set("flight.eta", "14:05")
548
+ client.set_add("tickets", "TK-100", tag="tk-100")
549
+ print(client.read("bookings.seats")) # 2
550
+ print(client.hash()) # 64-hex SHA-256 of canonical state
551
+
552
+ # Batch multiple mutations in a single atomic round-trip:
553
+ with client.batch() as b:
554
+ b.counter_inc("bookings.seats", 1)
555
+ b.register_set("flight.status", "boarding")
556
+
557
+ client.close()
558
+ ```
559
+
560
+ ### Durability
561
+
562
+ Each absorbed action is appended to `data/<node>/journal.jsonl` immediately.
563
+ When the journal passes the snapshot threshold, the current CRDT root is merged
564
+ (lattice join) into the previous generation's root and written atomically —
565
+ both as `snapshot-<seq>.json` (generation) and `snapshot.json` (latest alias) —
566
+ after which the log is truncated and older generations are pruned. A node never
567
+ replays a tail older than what its cumulative snapshot already captures, and a
568
+ crash mid-rotation degrades to the newest untampered generation. Optional
569
+ `--journal-key` HMAC-signs every line and snapshot, and replay refuses
570
+ tampered files. State values travel on the wire as JSON; register/set values
571
+ must be JSON-serializable.
572
+
573
+ ## Guarantees and limits
574
+
575
+ Conflux promises, and the test suite verifies:
576
+
577
+ - **Eventually consistent convergence**: every node that has absorbed the union of
578
+ available actions shows identical state (same `state_hash`), regardless of
579
+ delivery order.
580
+ - **No global lock, no blocking writes**: agents write to their own replicas
581
+ concurrently; merging is a join, so writers never contend.
582
+ - **Deterministic merges**: identical action sets always yield identical state,
583
+ including tie-breaks (equal stamps/tags) resolved by canonical order.
584
+ - **Replayability**: any subset of the journal rebuilds exactly the same state.
585
+ - **Durable ack**: a submit is acknowledged only after the accepting node has
586
+ absorbed the actions and fsynced them to its journal; an acked action
587
+ survives that node's process crash (torn-tail recovery).
588
+
589
+ The exact limits — schema bounds, frame size, snapshot/gossip/stale defaults,
590
+ backoff schedule, quota rules, and measured throughput (≈24k actions/s local
591
+ replay, ≈2.8k actions/s across an 8-agent TCP cluster) — are pinned in
592
+ [`docs/PROTOCOL.md` §8](docs/PROTOCOL.md); re-run `benchmarks/bench.py` for
593
+ host-local figures.
594
+
595
+ Honest limits:
596
+
597
+ - **Stale reads**: a read observes the join of everything that node has gossiped
598
+ so far. Convergence is eventual, not linearizable; workflows that need
599
+ strictly ordered reads need an external sequencing channel.
600
+ - **One type per key**: merging a key's existing CRDT with a different CRDT type
601
+ raises `TypeError` — a deterministic schema contract, not an ambiguity.
602
+ - **Unique IDs are load-bearing**: `action_id` and set tags are event identities;
603
+ reuse silently merges (so agent ids must be process-unique and ticks monotonic).
604
+ - **Fixed-point weights**: probabilistic decisions use a fixed 32-bit scale over
605
+ the same serialized `weight`, so all replicas agree as long as `weight` is a
606
+ JSON number.
607
+
608
+ ## Roadmap -> status
609
+
610
+ Phase 1 (protocol foundations), Phase 2 (cluster runtime), Phase 3 (application
611
+ layer), and the Phase 4 operational surface (metrics/admin probe, Prometheus
612
+ exporter, benchmarks, TLS, signed journals, membership/partition handling,
613
+ cumulative compaction) are complete.
614
+
615
+ The remaining ideas in each phase are **backlog, deliberately out of scope**
616
+ under the current product definition (§"Product definition and scope"): secure
617
+ framing beyond TLS, multi-cluster federation, chunked cross-peer snapshot
618
+ exchange, OpenTelemetry exporters, and workflow orchestration expansion. Each
619
+ is a candidate to revisit only after the core fabric — determinism, durability,
620
+ crash recovery — is proven by adopters.
621
+
622
+ ## Security
623
+
624
+ The cryptographic signing, identity verification, and storage subsystems are
625
+ documented in [`docs/SECURITY.md`](docs/SECURITY.md): threat model and trust
626
+ boundary, the properties the implementation enforces (constant-time
627
+ verification, algorithm binding, canonical serialization, signer binding,
628
+ replay idempotence, keyless-reload refusal), the findings that remain open,
629
+ and a deployment checklist. To report a vulnerability, open a GitHub issue or
630
+ contact the maintainer privately as described there.
631
+
632
+ ## License
633
+
634
+ Conflux is released under the [MIT License](LICENSE), as declared in
635
+ `pyproject.toml`.