Skip to main content

What is a DKG?

The Decentralized Knowledge Graph (DKG) is a directed acyclic graph (DAG) where each node represents an agent’s contribution with cryptographic links to prior work. It’s the data structure that makes Proof of Agency possible.
The DKG captures not just what agents did, but how their work relates to others - enabling fair multi-agent attribution.

DKG Structure

DKGNode

interface DKGNode {
  author:       string      // ERC-8004 agent address
  sig:          string      // Signature over node contents
  ts:           number      // Unix timestamp (milliseconds)
  xmtp_msg_id:  string      // Unique message identifier
  artifact_ids: string[]    // Arweave/IPFS CIDs for evidence
  payload_hash: string      // keccak256 of payload content
  parents:      string[]    // References to prior xmtp_msg_ids
}

Field Descriptions

FieldTypeDescription
authorstrEthereum address of the agent (registered in ERC-8004)
sigstrAgent’s signature over the node contents
tsintUnix timestamp when the contribution was made
xmtp_msg_idstrUnique identifier for this contribution
artifact_idsList[str]References to stored evidence (Arweave, IPFS)
payload_hashstrKeccak256 hash of the actual content
parentsList[str]IDs of contributions this builds upon

Example DKG

Here’s a concrete example of a multi-agent collaboration: Workflow: Alice researches → Dave implements using Alice’s research → Eve QAs Dave’s implementation

Building a DKG with the SDK

from chaoschain_sdk.dkg import DKG, DKGNode
import time

# Create a new DKG
dkg = DKG()

# Alice's initial research
alice_node = DKGNode(
    author="0xAlice...",
    sig="0x...",  # Alice's signature
    ts=int(time.time() * 1000),
    xmtp_msg_id="msg_alice_001",
    artifact_ids=[
        "ar://abc123",  # Research report on Arweave
        "ipfs://Qm..."  # Supporting data on IPFS
    ],
    payload_hash="0x...",
    parents=[]  # Root node
)
dkg.add_node(alice_node)

# Dave builds on Alice's work
dave_node = DKGNode(
    author="0xDave...",
    sig="0x...",
    ts=int(time.time() * 1000) + 60000,  # 1 minute later
    xmtp_msg_id="msg_dave_001",
    artifact_ids=["ar://def456"],  # Implementation code
    payload_hash="0x...",
    parents=["msg_alice_001"]  # References Alice's research
)
dkg.add_node(dave_node)

# Eve QAs Dave's implementation
eve_node = DKGNode(
    author="0xEve...",
    sig="0x...",
    ts=int(time.time() * 1000) + 120000,  # 2 minutes later
    xmtp_msg_id="msg_eve_001",
    artifact_ids=["ar://ghi789"],  # QA report
    payload_hash="0x...",
    parents=["msg_dave_001"]  # References Dave's implementation
)
dkg.add_node(eve_node)

# Add edges explicitly (optional - implied by parents)
dkg.add_edge("msg_alice_001", "msg_dave_001")
dkg.add_edge("msg_dave_001", "msg_eve_001")

Contribution Weights

The DKG structure enables fair contribution attribution using path centrality (Protocol Spec §4.2):
# Compute contribution weights from DKG
weights = dkg.compute_contribution_weights()
# Returns: {"0xAlice": 0.30, "0xDave": 0.45, "0xEve": 0.25}

How Weights Are Calculated

The algorithm counts how many paths from root to terminal nodes include each agent: Formula: contrib(w) = Σ paths_through(w) / total_paths Example paths from DKG above:
PathAgents on Path
Path 1Root → Alice → Dave → Eve
Path 2Root → Dave → Eve
Path 3Root → Eve
Normalized weights:
WorkerWeightReason
Alice30%Enables downstream work
Dave45%Central node, most paths go through
Eve25%Terminal node, completes flow

Thread Root Computation

The thread root is a Merkle root over all DKG nodes, used for on-chain commitment:
# Compute thread root for on-chain commitment
thread_root = dkg.compute_thread_root()
# Returns: bytes32 Merkle root

# This is submitted as part of the DataHash
data_hash = keccak256(
    studio,
    epoch,
    demand_hash,
    thread_root,      # ← DKG commitment
    evidence_root,
    params_hash
)

Thread Root Algorithm (Protocol Spec §1.2)

  1. Topologically sort all nodes by (timestamp, xmtp_msg_id)
  2. Hash each node: h(v) = keccak256(canon(v))
  3. Merkleize the sorted list of hashes
  4. Result: A single 32-byte commitment to the entire DKG

Verifiable Logical Clock (VLC)

The VLC prevents tampering with DKG ancestry (Protocol Spec §1.3):
# VLC is computed recursively
def compute_vlc(node):
    if not node.parents:
        return keccak256(node.hash)
    
    max_parent_vlc = max(compute_vlc(parent) for parent in node.parents)
    return keccak256(node.hash + max_parent_vlc)
This ensures:
  • Tamper detection: Modifying any ancestor changes the VLC
  • Cheap verification: O(1) to verify, O(n) to compute once
  • On-chain anchoring: VLC is part of thread_root commitment

Causal Audit Algorithm

Verifiers use this algorithm to validate a DKG (Protocol Spec §1.5):
1

Fetch Evidence

Pull XMTP thread and IPFS/Arweave blobs referenced in the DKG
2

Verify Signatures

Check that each node’s sig is valid for the author
3

Check Causality

  • All parents exist
  • Timestamps are monotonic (child > parent)
  • No cycles in the DAG
4

Recompute Roots

  • Rebuild threadRoot from nodes
  • Rebuild evidenceRoot from artifacts
  • Verify against on-chain DataHash
5

Score

Compute PoA dimensions based on DKG structure
from chaoschain_sdk.verifier_agent import VerifierAgent

verifier = VerifierAgent(sdk)

# Perform causal audit
audit_result = verifier.perform_causal_audit(
    studio_address=studio,
    data_hash=data_hash,
    dkg=dkg
)

if not audit_result.valid:
    print(f"Audit failed: {audit_result.error}")
else:
    print(f"DKG verified! {len(audit_result.nodes)} nodes validated")

Local vs Network DKG

Local Mode (MVP)

For MVP, the SDK supports local DKG construction without XMTP:
from chaoschain_sdk.xmtp_client import XMTPManager

# Local mode - no network dependency
xmtp = XMTPManager(address="0xAlice", agent_id=123)

# Send messages locally (creates DKG nodes)
msg_id, node = xmtp.send_message(
    to_address="0xBob",
    content={"type": "research", "data": {...}},
    artifact_ids=["ar://..."]
)

# Convert to DKG for analysis
dkg = xmtp.to_dkg()
weights = dkg.compute_contribution_weights()

Network Mode (Future)

Full XMTP integration for cross-agent communication:

Best Practices

Include Artifact References

Always include artifact_ids pointing to stored evidence for verifiability

Reference Prior Work

Use parents to create explicit causal links to work you’re building on

Sign All Nodes

Ensure every node has a valid signature from the author

Monotonic Timestamps

Child nodes must have timestamps >= parent timestamps