> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shodai.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture overview

> Orient around agreement definitions, deployed agreement instances, onchain execution, SDKs, and supporting API layers.

export const SystemArchitectureDiagram = () => <div className="shodai-diagram-frame not-prose my-6 overflow-hidden rounded-lg border shadow-sm">
    <svg className="shodai-architecture-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 55 1280 790" role="img" aria-labelledby="system-architecture-diagram-title system-architecture-diagram-desc">
            <title id="system-architecture-diagram-title">Agreements Architecture</title>
            <desc id="system-architecture-diagram-desc">A light architecture diagram adapted from the source SVG showing the protocol core, execution steps, and supporting API layer services.</desc>
            <defs>
              <marker id="system-architecture-arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
                <polygon points="0 0, 10 3.5, 0 7" fill="var(--shodai-diagram-accent)" />
              </marker>
              <pattern id="system-architecture-grid" width="40" height="40" patternUnits="userSpaceOnUse">
                <path d="M 40 0 L 0 0 0 40" fill="none" stroke="var(--shodai-diagram-grid)" strokeWidth="0.5" />
              </pattern>
            </defs>
    
            <rect width="100%" height="100%" fill="var(--shodai-diagram-bg)" />
    
            <rect width="100%" height="100%" fill="url(#system-architecture-grid)" />
    
            {}
            <line x1="295" y1="315" x2="340" y2="315" stroke="var(--shodai-diagram-connector)" strokeWidth="1.8" markerEnd="url(#system-architecture-arrowhead)" />
            <line x1="565" y1="315" x2="610" y2="315" stroke="var(--shodai-diagram-connector)" strokeWidth="1.8" markerEnd="url(#system-architecture-arrowhead)" />
            <line x1="835" y1="315" x2="880" y2="315" stroke="var(--shodai-diagram-connector)" strokeWidth="1.8" markerEnd="url(#system-architecture-arrowhead)" />
            <path d="M 1055 410 C 1075 500, 1070 570, 1040 650" fill="none" stroke="var(--shodai-diagram-connector)" strokeWidth="1.8" strokeDasharray="8,6" markerEnd="url(#system-architecture-arrowhead)" />
    
            {}
            <rect x="45" y="70" width="1190" height="500" rx="18" fill="var(--shodai-diagram-tint-soft)" stroke="var(--shodai-diagram-line)" strokeWidth="1.2" strokeDasharray="8,4" />
            <text x="75" y="105" fill="var(--shodai-diagram-accent)" fontSize="20" fontWeight="700">Trust Layer</text>
            <text x="75" y="130" fill="var(--shodai-diagram-muted)" fontSize="14">Open, verifiable agreement definition and execution</text>
    
            {}
            <rect x="75" y="190" width="220" height="250" rx="10" fill="var(--shodai-diagram-bg)" stroke="var(--shodai-diagram-bg)" strokeWidth="4" />
            <rect x="75" y="190" width="220" height="250" rx="10" fill="var(--shodai-diagram-tint)" stroke="var(--shodai-diagram-accent)" strokeWidth="1.6" />
            <rect x="75" y="190" width="220" height="8" rx="4" fill="var(--shodai-diagram-accent)" />
            <rect x="97" y="220" width="74" height="28" rx="14" fill="var(--shodai-diagram-tint-stronger)" />
            <text x="134" y="239" fill="var(--shodai-diagram-accent)" fontSize="13" fontWeight="700" textAnchor="middle">DEFINE</text>
            <text x="97" y="284" fill="var(--shodai-diagram-ink)" fontSize="23" fontWeight="700">Agreement</text>
            <text x="97" y="312" fill="var(--shodai-diagram-ink)" fontSize="23" fontWeight="700">Data Standard</text>
            <text x="97" y="356" fill="var(--shodai-diagram-muted)" fontSize="17">Canonical JSON</text>
            <text x="97" y="381" fill="var(--shodai-diagram-muted)" fontSize="17">Parties + terms</text>
            <text x="97" y="406" fill="var(--shodai-diagram-muted)" fontSize="17">Inputs + states</text>
    
            <rect x="345" y="190" width="220" height="250" rx="10" fill="var(--shodai-diagram-bg)" stroke="var(--shodai-diagram-bg)" strokeWidth="4" />
            <rect x="345" y="190" width="220" height="250" rx="10" fill="var(--shodai-diagram-tint)" stroke="var(--shodai-diagram-accent)" strokeWidth="1.6" />
            <rect x="345" y="190" width="220" height="8" rx="4" fill="var(--shodai-diagram-accent)" />
            <rect x="367" y="220" width="82" height="28" rx="14" fill="var(--shodai-diagram-tint-stronger)" />
            <text x="408" y="239" fill="var(--shodai-diagram-accent)" fontSize="13" fontWeight="700" textAnchor="middle">COMPILE</text>
            <text x="367" y="284" fill="var(--shodai-diagram-ink)" fontSize="23" fontWeight="700">SDK / Client</text>
            <text x="367" y="312" fill="var(--shodai-diagram-ink)" fontSize="23" fontWeight="700">Libraries</text>
            <text x="367" y="356" fill="var(--shodai-diagram-muted)" fontSize="17">Deployment</text>
            <text x="367" y="381" fill="var(--shodai-diagram-muted)" fontSize="17">payloads</text>
            <text x="367" y="406" fill="var(--shodai-diagram-muted)" fontSize="17">Typed calls</text>
    
            <rect x="615" y="190" width="220" height="250" rx="10" fill="var(--shodai-diagram-bg)" stroke="var(--shodai-diagram-bg)" strokeWidth="4" />
            <rect x="615" y="190" width="220" height="250" rx="10" fill="var(--shodai-diagram-tint)" stroke="var(--shodai-diagram-accent)" strokeWidth="1.6" />
            <rect x="615" y="190" width="220" height="8" rx="4" fill="var(--shodai-diagram-accent)" />
            <rect x="637" y="220" width="82" height="28" rx="14" fill="var(--shodai-diagram-tint-stronger)" />
            <text x="678" y="239" fill="var(--shodai-diagram-accent)" fontSize="13" fontWeight="700" textAnchor="middle">EXECUTE</text>
            <text x="637" y="284" fill="var(--shodai-diagram-ink)" fontSize="23" fontWeight="700">Onchain</text>
            <text x="637" y="312" fill="var(--shodai-diagram-ink)" fontSize="23" fontWeight="700">Execution</text>
            <text x="637" y="340" fill="var(--shodai-diagram-ink)" fontSize="23" fontWeight="700">Engine</text>
            <text x="637" y="378" fill="var(--shodai-diagram-muted)" fontSize="17">Factory deploys</text>
            <text x="637" y="403" fill="var(--shodai-diagram-muted)" fontSize="17">Validates inputs</text>
    
            <rect x="885" y="190" width="220" height="250" rx="10" fill="var(--shodai-diagram-bg)" stroke="var(--shodai-diagram-bg)" strokeWidth="4" />
            <rect x="885" y="190" width="220" height="250" rx="10" fill="var(--shodai-diagram-tint)" stroke="var(--shodai-diagram-accent)" strokeWidth="1.6" />
            <rect x="885" y="190" width="220" height="8" rx="4" fill="var(--shodai-diagram-accent)" />
            <rect x="907" y="220" width="74" height="28" rx="14" fill="var(--shodai-diagram-tint-stronger)" />
            <text x="944" y="239" fill="var(--shodai-diagram-accent)" fontSize="13" fontWeight="700" textAnchor="middle">VERIFY</text>
            <text x="907" y="284" fill="var(--shodai-diagram-ink)" fontSize="23" fontWeight="700">Public</text>
            <text x="907" y="312" fill="var(--shodai-diagram-ink)" fontSize="23" fontWeight="700">Execution</text>
            <text x="907" y="340" fill="var(--shodai-diagram-ink)" fontSize="23" fontWeight="700">History</text>
            <text x="907" y="378" fill="var(--shodai-diagram-muted)" fontSize="17">Events + hashes</text>
            <text x="907" y="403" fill="var(--shodai-diagram-muted)" fontSize="17">Shared truth</text>
    
            <rect x="300" y="140" width="380" height="36" rx="18" fill="var(--shodai-diagram-bg)" stroke="var(--shodai-diagram-line)" strokeWidth="1" />
            <text x="490" y="163" fill="var(--shodai-diagram-ink)" fontSize="14" fontWeight="700" textAnchor="middle">SDK compilation boundary: model to execution profile</text>
    
            {}
            <rect x="185" y="475" width="910" height="58" rx="29" fill="var(--shodai-diagram-bg)" stroke="var(--shodai-diagram-line)" strokeWidth="1.2" />
            <circle cx="240" cy="504" r="15" fill="var(--shodai-diagram-tint-stronger)" stroke="var(--shodai-diagram-accent)" strokeWidth="1.5" />
            <text x="240" y="510" fill="var(--shodai-diagram-accent)" fontSize="15" fontWeight="700" textAnchor="middle">1</text>
            <text x="270" y="510" fill="var(--shodai-diagram-ink)" fontSize="15" fontWeight="700">Input</text>
            <line x1="320" y1="504" x2="360" y2="504" stroke="var(--shodai-diagram-connector)" strokeWidth="1.6" markerEnd="url(#system-architecture-arrowhead)" />
            <circle cx="405" cy="504" r="15" fill="var(--shodai-diagram-tint-stronger)" stroke="var(--shodai-diagram-accent)" strokeWidth="1.5" />
            <text x="405" y="510" fill="var(--shodai-diagram-accent)" fontSize="15" fontWeight="700" textAnchor="middle">2</text>
            <text x="435" y="510" fill="var(--shodai-diagram-ink)" fontSize="15" fontWeight="700">Validate</text>
            <line x1="500" y1="504" x2="540" y2="504" stroke="var(--shodai-diagram-connector)" strokeWidth="1.6" markerEnd="url(#system-architecture-arrowhead)" />
            <circle cx="585" cy="504" r="15" fill="var(--shodai-diagram-tint-stronger)" stroke="var(--shodai-diagram-accent)" strokeWidth="1.5" />
            <text x="585" y="510" fill="var(--shodai-diagram-accent)" fontSize="15" fontWeight="700" textAnchor="middle">3</text>
            <text x="615" y="510" fill="var(--shodai-diagram-ink)" fontSize="15" fontWeight="700">Verify</text>
            <line x1="665" y1="504" x2="705" y2="504" stroke="var(--shodai-diagram-connector)" strokeWidth="1.6" markerEnd="url(#system-architecture-arrowhead)" />
            <circle cx="750" cy="504" r="15" fill="var(--shodai-diagram-tint-stronger)" stroke="var(--shodai-diagram-accent)" strokeWidth="1.5" />
            <text x="750" y="510" fill="var(--shodai-diagram-accent)" fontSize="15" fontWeight="700" textAnchor="middle">4</text>
            <text x="780" y="510" fill="var(--shodai-diagram-ink)" fontSize="15" fontWeight="700">Transition</text>
            <line x1="865" y1="504" x2="905" y2="504" stroke="var(--shodai-diagram-connector)" strokeWidth="1.6" markerEnd="url(#system-architecture-arrowhead)" />
            <circle cx="950" cy="504" r="15" fill="var(--shodai-diagram-tint-stronger)" stroke="var(--shodai-diagram-accent)" strokeWidth="1.5" />
            <text x="950" y="510" fill="var(--shodai-diagram-accent)" fontSize="15" fontWeight="700" textAnchor="middle">5</text>
            <text x="980" y="510" fill="var(--shodai-diagram-ink)" fontSize="15" fontWeight="700">Action</text>
    
            {}
            <rect x="45" y="625" width="1190" height="200" rx="18" fill="var(--shodai-diagram-tint-soft)" stroke="var(--shodai-diagram-line)" strokeWidth="1.2" strokeDasharray="8,4" />
            <text x="75" y="660" fill="var(--shodai-diagram-accent)" fontSize="20" fontWeight="700">Supporting API Layer</text>
            <text x="75" y="685" fill="var(--shodai-diagram-muted)" fontSize="15">Optional services for</text>
            <text x="75" y="705" fill="var(--shodai-diagram-muted)" fontSize="15">queries, notifications,</text>
            <text x="75" y="725" fill="var(--shodai-diagram-muted)" fontSize="15">and workflow UX</text>
    
            <rect x="290" y="680" width="190" height="105" rx="10" fill="var(--shodai-diagram-bg)" stroke="var(--shodai-diagram-line)" strokeWidth="1.2" />
            <text x="310" y="713" fill="var(--shodai-diagram-ink)" fontSize="17" fontWeight="700">Index + Query</text>
            <circle cx="315" cy="740" r="4" fill="var(--shodai-diagram-accent)" />
            <text x="330" y="744" fill="var(--shodai-diagram-muted)" fontSize="13">agreement records</text>
            <circle cx="315" cy="766" r="4" fill="var(--shodai-diagram-accent)" />
            <text x="330" y="770" fill="var(--shodai-diagram-muted)" fontSize="13">participant history</text>
    
            <rect x="515" y="680" width="190" height="105" rx="10" fill="var(--shodai-diagram-bg)" stroke="var(--shodai-diagram-line)" strokeWidth="1.2" />
            <text x="535" y="713" fill="var(--shodai-diagram-ink)" fontSize="17" fontWeight="700">Webhooks</text>
            <circle cx="540" cy="740" r="4" fill="var(--shodai-diagram-accent)" />
            <text x="555" y="744" fill="var(--shodai-diagram-muted)" fontSize="13">state changes</text>
            <circle cx="540" cy="766" r="4" fill="var(--shodai-diagram-accent)" />
            <text x="555" y="770" fill="var(--shodai-diagram-muted)" fontSize="13">notification triggers</text>
    
            <rect x="740" y="680" width="190" height="105" rx="10" fill="var(--shodai-diagram-bg)" stroke="var(--shodai-diagram-line)" strokeWidth="1.2" />
            <text x="760" y="713" fill="var(--shodai-diagram-ink)" fontSize="17" fontWeight="700">Workflow Support</text>
            <circle cx="765" cy="740" r="4" fill="var(--shodai-diagram-accent)" />
            <text x="780" y="744" fill="var(--shodai-diagram-muted)" fontSize="13">drafts and setup</text>
            <circle cx="765" cy="766" r="4" fill="var(--shodai-diagram-accent)" />
            <text x="780" y="770" fill="var(--shodai-diagram-muted)" fontSize="13">private/off-chain data</text>
    
            <rect x="965" y="680" width="160" height="105" rx="10" fill="var(--shodai-diagram-bg)" stroke="var(--shodai-diagram-line)" strokeWidth="1.2" />
            <text x="985" y="713" fill="var(--shodai-diagram-ink)" fontSize="17" fontWeight="700">API Client</text>
            <circle cx="990" cy="740" r="4" fill="var(--shodai-diagram-accent)" />
            <text x="1005" y="744" fill="var(--shodai-diagram-muted)" fontSize="13">app integration</text>
            <circle cx="990" cy="766" r="4" fill="var(--shodai-diagram-accent)" />
            <text x="1005" y="770" fill="var(--shodai-diagram-muted)" fontSize="13">sample app</text>
    
            <rect x="1082" y="572" width="132" height="42" rx="8" fill="var(--shodai-diagram-bg)" opacity="1" />
            <text x="1148" y="590" fill="var(--shodai-diagram-muted)" fontSize="13" textAnchor="middle">events + state</text>
            <text x="1148" y="606" fill="var(--shodai-diagram-muted)" fontSize="13" textAnchor="middle">indexed by API</text>
          </svg>
  </div>;

export const AgreementStateMachineExample = ({agreement = {}, title = 'State Machine Map'}) => {
  const [zoom, setZoom] = useState(1);
  const [pan, setPan] = useState({
    x: 0,
    y: 0
  });
  const [isDragging, setIsDragging] = useState(false);
  const [nodeOffsets, setNodeOffsets] = useState({});
  const dragRef = useRef(null);
  const truncate = (value, max = 36) => {
    const text = String(value || '');
    if (text.length <= max) return text;
    return `${text.slice(0, Math.max(10, Math.floor(max / 2)))}...${text.slice(-8)}`;
  };
  const execution = agreement?.execution || ({});
  const rawStates = execution.states || ({});
  const states = Array.isArray(rawStates) ? Object.fromEntries(rawStates.map(state => {
    if (typeof state === 'string') return [state, {
      name: ''
    }];
    if (!state?.id) return null;
    return [String(state.id), state];
  }).filter(Boolean)) : rawStates;
  const transitions = Array.isArray(execution.transitions) ? execution.transitions : [];
  const stateIds = Object.keys(states);
  const initialState = execution?.initialize?.initialState || stateIds[0] || null;
  const inputs = execution.inputs || ({});
  const brand = 'var(--shodai-diagram-accent)';
  const transitionInputIds = transition => {
    const directInput = transition?.input ? [transition.input] : [];
    const conditionInputs = Array.isArray(transition?.conditions) ? transition.conditions.flatMap(condition => {
      if (condition?.input) return [condition.input];
      if (Array.isArray(condition?.inputs)) return condition.inputs;
      return [];
    }) : [];
    return Array.from(new Set([...directInput, ...conditionInputs].filter(Boolean).map(String)));
  };
  const inputDisplayName = inputId => {
    const input = inputs?.[inputId] || ({});
    return input.displayName || input.name || input.id || inputId;
  };
  const describeTransition = edge => {
    if (!edge) return '';
    const label = edge.inputIds.length > 0 ? ` using ${edge.inputIds.map(inputDisplayName).join(', ')}` : '';
    return `${edge.from} moves to ${edge.to}${label}.`;
  };
  const zoomIn = () => setZoom(value => Math.min(2.4, Number((value + 0.2).toFixed(2))));
  const zoomOut = () => setZoom(value => Math.max(0.7, Number((value - 0.2).toFixed(2))));
  const resetView = () => {
    setZoom(1);
    setPan({
      x: 0,
      y: 0
    });
    setNodeOffsets({});
  };
  const nodeWidth = 260;
  const nodeHeight = 66;
  const gapX = 128;
  const gapY = 96;
  const positions = new Map();
  const levels = new Map();
  const outgoing = new Map();
  const transitionOrder = new Map();
  transitions.forEach((transition, index) => {
    if (!transition?.from || !transition?.to) return;
    const from = String(transition.from);
    const to = String(transition.to);
    if (!outgoing.has(from)) outgoing.set(from, []);
    outgoing.get(from).push(to);
    if (!transitionOrder.has(from)) transitionOrder.set(from, index);
    if (!transitionOrder.has(to)) transitionOrder.set(to, index);
  });
  for (const [from, targets] of outgoing.entries()) {
    outgoing.set(from, Array.from(new Set(targets)));
  }
  const canReach = (start, target) => {
    if (!start || !target) return false;
    const seen = new Set();
    const queue = [start];
    while (queue.length > 0) {
      const current = queue.shift();
      if (!current || seen.has(current)) continue;
      if (current === target) return true;
      seen.add(current);
      (outgoing.get(current) || []).forEach(next => {
        if (!seen.has(next)) queue.push(next);
      });
    }
    return false;
  };
  if (initialState) levels.set(initialState, 0);
  for (let pass = 0; pass < Math.max(1, stateIds.length * 2); pass += 1) {
    let changed = false;
    for (const transition of transitions) {
      if (!transition?.from || !transition?.to) continue;
      const from = String(transition.from);
      const to = String(transition.to);
      const fromLevel = levels.get(from);
      if (fromLevel == null) continue;
      const nextLevel = fromLevel + 1;
      const currentLevel = levels.get(to);
      const closesCycle = currentLevel != null && currentLevel <= fromLevel && canReach(to, from);
      if (currentLevel == null || nextLevel > currentLevel && !closesCycle) {
        levels.set(to, nextLevel);
        changed = true;
      }
    }
    if (!changed) break;
  }
  const maxRankedLevel = Math.max(0, ...Array.from(levels.values()));
  stateIds.filter(id => levels.get(id) == null).sort((a, b) => a.localeCompare(b)).forEach((id, index) => levels.set(id, maxRankedLevel + 1 + index));
  const groupedByLevel = new Map();
  for (const id of stateIds) {
    const level = levels.get(id) || 0;
    if (!groupedByLevel.has(level)) groupedByLevel.set(level, []);
    groupedByLevel.get(level).push(id);
  }
  const levelNumbers = Array.from(groupedByLevel.keys()).sort((a, b) => a - b);
  const rowGap = nodeWidth + gapX;
  for (const level of levelNumbers) {
    const ids = groupedByLevel.get(level) || [];
    const idsWithIdeal = ids.map(id => {
      const incomingSources = transitions.filter(transition => String(transition?.to || '') === id && positions.has(String(transition?.from || '')) && (levels.get(String(transition?.from || '')) || 0) < level).map(transition => positions.get(String(transition.from)).x);
      const idealX = incomingSources.length > 0 ? incomingSources.reduce((sum, x) => sum + x, 0) / incomingSources.length : 0;
      return {
        id,
        idealX,
        order: transitionOrder.get(id) ?? stateIds.findIndex(stateId => stateId === id)
      };
    });
    idsWithIdeal.sort((a, b) => {
      if (a.idealX !== b.idealX) return a.idealX - b.idealX;
      return a.order - b.order;
    });
    const centerX = idsWithIdeal.length > 0 ? idsWithIdeal.reduce((sum, item) => sum + item.idealX, 0) / idsWithIdeal.length : 0;
    const startX = centerX - (idsWithIdeal.length - 1) * rowGap / 2;
    idsWithIdeal.forEach((item, index) => {
      positions.set(item.id, {
        x: startX + index * rowGap,
        y: level * (nodeHeight + gapY)
      });
    });
  }
  const baseRawNodes = stateIds.map(id => ({
    id,
    name: states[id]?.name || '',
    x: positions.get(id)?.x || 0,
    y: positions.get(id)?.y || 0,
    width: nodeWidth,
    height: nodeHeight
  }));
  const rawNodes = baseRawNodes.map(node => {
    const offset = nodeOffsets[node.id] || ({
      x: 0,
      y: 0
    });
    return {
      ...node,
      x: node.x + offset.x,
      y: node.y + offset.y
    };
  });
  const baseEdges = transitions.filter(transition => transition?.from && transition?.to).map((transition, index) => {
    const conditions = Array.isArray(transition?.conditions) ? transition.conditions : [];
    const condition = conditions.find(item => item?.input) || conditions[0];
    return {
      id: `${transition.from}-${transition.to}-${index}`,
      from: String(transition.from),
      to: String(transition.to),
      inputIds: transitionInputIds(transition),
      label: transitionInputIds(transition).join(', ') || (condition?.input ? String(condition.input) : '')
    };
  });
  const pairCounts = new Map();
  for (const edge of baseEdges) {
    const key = `${edge.from}->${edge.to}`;
    pairCounts.set(key, (pairCounts.get(key) || 0) + 1);
  }
  const pairSeen = new Map();
  const returnLaneBySource = new Map();
  const sideLaneByTarget = new Map();
  const rawEdges = baseEdges.map(edge => {
    const key = `${edge.from}->${edge.to}`;
    const pairIndex = pairSeen.get(key) || 0;
    pairSeen.set(key, pairIndex + 1);
    const fromLevel = levels.get(edge.from) || 0;
    const toLevel = levels.get(edge.to) || 0;
    const returnEdge = edge.from === edge.to || toLevel <= fromLevel;
    const sideEdge = !returnEdge && toLevel - fromLevel > 1;
    const returnLane = returnEdge ? returnLaneBySource.get(edge.from) || 0 : 0;
    const sideLane = sideEdge ? sideLaneByTarget.get(edge.to) || 0 : 0;
    if (returnEdge) returnLaneBySource.set(edge.from, returnLane + 1);
    if (sideEdge) sideLaneByTarget.set(edge.to, sideLane + 1);
    return {
      ...edge,
      pairCount: pairCounts.get(key) || 1,
      pairIndex,
      returnEdge,
      returnLane,
      sideEdge,
      sideLane
    };
  });
  const rawNodesById = new Map(rawNodes.map(node => [node.id, node]));
  const baseRawNodesById = new Map(baseRawNodes.map(node => [node.id, node]));
  const edgeLabelText = edge => String(edge.label || '');
  const edgeLabelWidth = edge => Math.max(76, edgeLabelText(edge).length * 8.5 + 20);
  const returnControlX = (edge, source, target) => Math.min(source.x, target.x) - 150 - edge.returnLane * 54;
  const sideControlX = (edge, source, target) => Math.max(source.x + source.width, target.x + target.width) + 230 + edge.sideLane * 66;
  const minReturnEdgeX = Math.min(0, ...rawEdges.filter(edge => edge.returnEdge).map(edge => {
    const source = baseRawNodesById.get(edge.from);
    const target = baseRawNodesById.get(edge.to);
    if (!source || !target) return 0;
    return returnControlX(edge, source, target) + 28 - edgeLabelWidth(edge) / 2 - 16;
  }));
  const maxSideEdgeX = Math.max(nodeWidth, ...rawEdges.filter(edge => edge.sideEdge).map(edge => {
    const source = baseRawNodesById.get(edge.from);
    const target = baseRawNodesById.get(edge.to);
    if (!source || !target) return nodeWidth;
    return sideControlX(edge, source, target) + 16 + edgeLabelWidth(edge) / 2 + 16;
  }));
  const nodeDragSlack = 80;
  const minX = Math.min(0, minReturnEdgeX, ...baseRawNodes.map(node => node.x)) - nodeDragSlack;
  const minY = Math.min(0, ...baseRawNodes.map(node => node.y)) - nodeDragSlack;
  const maxX = Math.max(nodeWidth, maxSideEdgeX, ...baseRawNodes.map(node => node.x + node.width)) + nodeDragSlack;
  const maxY = Math.max(nodeHeight, ...baseRawNodes.map(node => node.y + node.height)) + nodeDragSlack;
  const paddingX = 24;
  const paddingY = 72;
  const offsetX = paddingX - minX;
  const offsetY = paddingY - minY;
  const diagramWidth = Math.max(720, maxX - minX + paddingX * 2);
  const diagramHeight = Math.max(520, maxY - minY + paddingY * 2);
  const nodes = rawNodes.map(node => ({
    ...node,
    x: node.x + offsetX,
    y: node.y + offsetY
  }));
  const nodesById = new Map(nodes.map(node => [node.id, node]));
  const diagramTitle = agreement?.metadata?.name || 'Agreement state machine';
  const svgId = String(agreement?.metadata?.templateId || agreement?.metadata?.id || 'agreement').replace(/[^a-zA-Z0-9_-]/g, '-').slice(0, 48);
  const gridId = `${svgId}-grid`;
  const arrowId = `${svgId}-arrow`;
  const viewBoxWidth = diagramWidth / zoom;
  const viewBoxHeight = diagramHeight / zoom;
  const maxViewBoxX = Math.max(0, diagramWidth - viewBoxWidth);
  const maxViewBoxY = Math.max(0, diagramHeight - viewBoxHeight);
  const centerViewBoxX = Math.max(0, (diagramWidth - viewBoxWidth) / 2);
  const centerViewBoxY = Math.max(0, (diagramHeight - viewBoxHeight) / 4);
  const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
  const panSlack = 120 / zoom;
  const minPanX = -centerViewBoxX - panSlack;
  const maxPanX = maxViewBoxX - centerViewBoxX + panSlack;
  const minPanY = -centerViewBoxY - panSlack;
  const maxPanY = maxViewBoxY - centerViewBoxY + panSlack;
  const viewBoxX = centerViewBoxX + clamp(pan.x, minPanX, maxPanX);
  const viewBoxY = centerViewBoxY + clamp(pan.y, minPanY, maxPanY);
  const edgeOffset = (edge, source, target) => {
    const parallelOffset = edge.pairCount > 1 ? (edge.pairIndex - (edge.pairCount - 1) / 2) * 42 : 0;
    const returnOffset = target.y <= source.y ? source.x > target.x ? 120 : -120 : 0;
    return parallelOffset + returnOffset;
  };
  const edgePath = (edge, source, target) => {
    if (edge.returnEdge) {
      const startX = source.x;
      const startY = source.y + source.height / 2;
      const endX = target.x;
      const endY = target.y + target.height / 2;
      const controlX = returnControlX(edge, source, target);
      return `M ${startX} ${startY} C ${controlX} ${startY}, ${controlX} ${endY}, ${endX} ${endY}`;
    }
    if (edge.sideEdge) {
      const startX = source.x + source.width;
      const startY = source.y + source.height / 2;
      const endX = target.x + target.width;
      const endY = target.y + target.height / 2;
      const controlX = sideControlX(edge, source, target);
      return `M ${startX} ${startY} C ${controlX} ${startY}, ${controlX} ${endY}, ${endX} ${endY}`;
    }
    const offset = edgeOffset(edge, source, target);
    const startX = source.x + source.width / 2 + offset;
    const startY = source.y + source.height;
    const endX = target.x + target.width / 2 + offset;
    const endY = target.y;
    const midY = (startY + endY) / 2;
    return `M ${startX} ${startY} C ${startX} ${midY}, ${endX} ${midY}, ${endX} ${endY}`;
  };
  const edgeLabel = (edge, source, target) => {
    if (edge.returnEdge) {
      const sourceY = source.y + source.height / 2;
      const targetY = target.y + target.height / 2;
      return {
        x: returnControlX(edge, source, target) + 28,
        y: (sourceY + targetY) / 2 - 9 + edge.returnLane * 18
      };
    }
    if (edge.sideEdge) {
      const targetY = target.y + target.height / 2;
      return {
        x: sideControlX(edge, source, target) + 16,
        y: targetY - 42 - edge.sideLane * 18
      };
    }
    const offset = edgeOffset(edge, source, target);
    const sourceY = source.y + source.height;
    const targetY = target.y;
    return {
      x: (source.x + source.width / 2 + target.x + target.width / 2) / 2 + offset,
      y: (sourceY + targetY) / 2 - 9
    };
  };
  const startDrag = ({id, type = 'pan', nodeId, clientX, clientY, width, height}) => {
    dragRef.current = {
      id,
      startClientX: clientX,
      startClientY: clientY,
      startPan: pan,
      startNodeOffset: nodeId ? nodeOffsets[nodeId] || ({
        x: 0,
        y: 0
      }) : null,
      nodeId,
      type,
      width: width || 1,
      height: height || 1
    };
    setIsDragging(true);
  };
  const moveDrag = ({id, clientX, clientY}) => {
    const drag = dragRef.current;
    if (!drag || drag.id !== id) return;
    const deltaX = (clientX - drag.startClientX) / drag.width * viewBoxWidth;
    const deltaY = (clientY - drag.startClientY) / drag.height * viewBoxHeight;
    if (drag.type === 'node' && drag.nodeId) {
      const baseNode = baseRawNodesById.get(drag.nodeId);
      const nextOffset = {
        x: drag.startNodeOffset.x + deltaX,
        y: drag.startNodeOffset.y + deltaY
      };
      if (baseNode) {
        nextOffset.x = clamp(baseNode.x + nextOffset.x, minX, maxX - baseNode.width) - baseNode.x;
        nextOffset.y = clamp(baseNode.y + nextOffset.y, minY, maxY - baseNode.height) - baseNode.y;
      }
      setNodeOffsets(current => ({
        ...current,
        [drag.nodeId]: nextOffset
      }));
      return;
    }
    setPan({
      x: clamp(drag.startPan.x - deltaX, minPanX, maxPanX),
      y: clamp(drag.startPan.y - deltaY, minPanY, maxPanY)
    });
  };
  const stopDrag = id => {
    if (dragRef.current?.id === id) {
      dragRef.current = null;
      setIsDragging(false);
    }
  };
  const handlePointerDown = event => {
    event.currentTarget.setPointerCapture(event.pointerId);
    startDrag({
      id: `pointer-${event.pointerId}`,
      type: 'pan',
      clientX: event.clientX,
      clientY: event.clientY,
      width: event.currentTarget.clientWidth,
      height: event.currentTarget.clientHeight
    });
  };
  const handleNodePointerDown = (event, nodeId) => {
    if (event.button !== 0 || dragRef.current) return;
    const canvas = event.currentTarget.closest('[data-state-machine-canvas]');
    event.preventDefault();
    event.stopPropagation();
    event.currentTarget.setPointerCapture(event.pointerId);
    startDrag({
      id: `pointer-${event.pointerId}`,
      type: 'node',
      nodeId,
      clientX: event.clientX,
      clientY: event.clientY,
      width: canvas?.clientWidth,
      height: canvas?.clientHeight
    });
  };
  const handlePointerMove = event => {
    moveDrag({
      id: `pointer-${event.pointerId}`,
      clientX: event.clientX,
      clientY: event.clientY
    });
  };
  const handlePointerUp = event => {
    stopDrag(`pointer-${event.pointerId}`);
  };
  const handleMouseDown = event => {
    if (event.button !== 0 || dragRef.current) return;
    event.preventDefault();
    startDrag({
      id: 'mouse',
      type: 'pan',
      clientX: event.clientX,
      clientY: event.clientY,
      width: event.currentTarget.clientWidth,
      height: event.currentTarget.clientHeight
    });
  };
  const handleMouseMove = event => {
    const activeDragId = dragRef.current?.id || 'mouse';
    moveDrag({
      id: activeDragId,
      clientX: event.clientX,
      clientY: event.clientY
    });
  };
  const handleMouseUp = () => {
    if (dragRef.current) {
      stopDrag(dragRef.current.id);
    }
  };
  const zoomAtPoint = (event, nextZoom) => {
    const bounds = event.currentTarget.getBoundingClientRect();
    const pointerX = (event.clientX - bounds.left) / Math.max(1, bounds.width);
    const pointerY = (event.clientY - bounds.top) / Math.max(1, bounds.height);
    const currentWorldX = viewBoxX + pointerX * viewBoxWidth;
    const currentWorldY = viewBoxY + pointerY * viewBoxHeight;
    const nextViewBoxWidth = diagramWidth / nextZoom;
    const nextViewBoxHeight = diagramHeight / nextZoom;
    const nextCenterViewBoxX = Math.max(0, (diagramWidth - nextViewBoxWidth) / 2);
    const nextCenterViewBoxY = Math.max(0, (diagramHeight - nextViewBoxHeight) / 4);
    const nextMaxViewBoxX = Math.max(0, diagramWidth - nextViewBoxWidth);
    const nextMaxViewBoxY = Math.max(0, diagramHeight - nextViewBoxHeight);
    const nextPanSlack = 120 / nextZoom;
    const nextViewBoxX = currentWorldX - pointerX * nextViewBoxWidth;
    const nextViewBoxY = currentWorldY - pointerY * nextViewBoxHeight;
    setZoom(nextZoom);
    setPan({
      x: clamp(nextViewBoxX - nextCenterViewBoxX, -nextCenterViewBoxX - nextPanSlack, nextMaxViewBoxX - nextCenterViewBoxX + nextPanSlack),
      y: clamp(nextViewBoxY - nextCenterViewBoxY, -nextCenterViewBoxY - nextPanSlack, nextMaxViewBoxY - nextCenterViewBoxY + nextPanSlack)
    });
  };
  const handleWheel = event => {
    if (!event.ctrlKey && !event.metaKey) return;
    event.preventDefault();
    event.stopPropagation();
    const scale = event.deltaY < 0 ? 1.12 : 1 / 1.12;
    const nextZoom = clamp(Number((zoom * scale).toFixed(3)), 0.7, 2.4);
    zoomAtPoint(event, nextZoom);
  };
  return <div className="shodai-state-machine-frame not-prose my-8 overflow-hidden rounded-lg border shadow-sm">
      <section className="shodai-state-machine-shell flex min-h-[460px] flex-col lg:min-h-[600px]">
        <div className="shodai-state-machine-header flex items-center justify-between border-b px-4 py-3">
          <div>
            <div className="shodai-state-machine-title text-base font-semibold">
              {title}
            </div>
            <div className="shodai-state-machine-meta text-sm">
              Derived from execution.states and execution.transitions
            </div>
          </div>
          <span className="shodai-state-machine-count rounded border px-2 py-1 font-mono text-sm">
            {stateIds.length} states
          </span>
        </div>

        <div className="shodai-state-machine-canvas-bg relative h-full min-h-[420px] overflow-hidden lg:min-h-[560px]" onWheelCapture={handleWheel}>
          <div className="absolute inset-0" data-state-machine-canvas onPointerDown={handlePointerDown} onPointerMove={handlePointerMove} onPointerUp={handlePointerUp} onPointerCancel={handlePointerUp} onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} onMouseLeave={handleMouseUp}>
            <svg role="img" aria-label={`${diagramTitle} state machine diagram`} viewBox={`${viewBoxX} ${viewBoxY} ${viewBoxWidth} ${viewBoxHeight}`} preserveAspectRatio="xMidYMin meet" className={`h-full min-h-[420px] w-full touch-none select-none lg:min-h-[520px] ${isDragging ? 'cursor-grabbing' : 'cursor-grab'}`} draggable={false}>
              <defs>
                <pattern id={gridId} width="18" height="18" patternUnits="userSpaceOnUse">
                  <circle cx="1" cy="1" r="1" className="shodai-state-machine-grid-dot" />
                </pattern>
                <marker id={arrowId} markerWidth="10" markerHeight="10" refX="8" refY="5" orient="auto" markerUnits="strokeWidth" className="shodai-state-machine-connector">
                  <path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor" />
                </marker>
              </defs>

              <rect width={diagramWidth} height={diagramHeight} fill={`url(#${gridId})`} className="shodai-state-machine-connector" />

              {rawEdges.map(edge => {
    const source = nodesById.get(edge.from);
    const target = nodesById.get(edge.to);
    if (!source || !target) return null;
    const label = edgeLabel(edge, source, target);
    return <g key={edge.id} aria-label={`Transition from ${edge.from} to ${edge.to}`}>
                    <title>{describeTransition(edge)}</title>
                    <path d={edgePath(edge, source, target)} fill="none" stroke="currentColor" strokeWidth="1.5" markerEnd={`url(#${arrowId})`} className="shodai-state-machine-connector" />
                    {edge.label ? <g>
                        {(() => {
      const visibleLabel = edgeLabelText(edge);
      const labelWidth = edgeLabelWidth(edge);
      return <>
                              <rect x={label.x - labelWidth / 2} y={label.y - 13} width={labelWidth} height="22" rx="11" className="shodai-state-machine-label-bg" />
                              <text x={label.x} y={label.y + 4} textAnchor="middle" className="shodai-state-machine-label-text font-mono text-[14px] font-semibold">
                                {visibleLabel}
                              </text>
                            </>;
    })()}
                      </g> : null}
                  </g>;
  })}

              {nodes.map(node => {
    const start = initialState === node.id;
    return <g key={node.id} aria-label={`State ${node.id}`} className="cursor-move" onPointerDown={event => handleNodePointerDown(event, node.id)} onMouseDown={event => event.stopPropagation()}>
                    <title>
                      {states[node.id]?.description || states[node.id]?.name || node.id}
                    </title>
                    {start ? <rect x={node.x} y={node.y} width={node.width} height={node.height} rx="10" stroke={brand} strokeWidth="2" filter="drop-shadow(0 1px 2px rgba(0,0,0,0.06))" className="shodai-state-machine-start-node" /> : <rect x={node.x} y={node.y} width={node.width} height={node.height} rx="10" stroke="currentColor" strokeWidth="1" filter="drop-shadow(0 1px 2px rgba(0,0,0,0.06))" className="shodai-state-machine-node" />}
                    <text x={node.x + 16} y={node.y + 26} className="shodai-state-machine-node-id font-mono text-[16px] font-semibold">
                      {truncate(node.id, 30)}
                    </text>
                    <text x={node.x + 16} y={node.y + 50} className="shodai-state-machine-node-name text-[14px]">
                      {truncate(node.name || (start ? 'Initial state' : ''), 34)}
                    </text>
                  </g>;
  })}
            </svg>
          </div>

          <div className="shodai-state-machine-controls absolute bottom-5 left-5 flex flex-col overflow-hidden rounded border shadow-sm">
            <button type="button" aria-label="Zoom in on state machine" title="Zoom in" className="shodai-state-machine-control flex h-9 w-9 items-center justify-center border-b text-xl leading-none transition disabled:cursor-not-allowed disabled:opacity-40" disabled={zoom >= 2.4} onClick={zoomIn}>
              +
            </button>
            <button type="button" aria-label="Zoom out of state machine" title="Zoom out" className="shodai-state-machine-control flex h-9 w-9 items-center justify-center border-b text-xl leading-none transition disabled:cursor-not-allowed disabled:opacity-40" disabled={zoom <= 0.7} onClick={zoomOut}>
              -
            </button>
            <button type="button" aria-label="Reset state machine view" title="Reset view" className="shodai-state-machine-control flex h-9 w-9 items-center justify-center text-[10px] font-semibold uppercase tracking-normal transition" onClick={resetView}>
              fit
            </button>
          </div>
        </div>
      </section>
    </div>;
};

For the complete documentation index, see [llms.txt](https://docs.shodai.network/llms.txt).

## Architecture at a glance

<SystemArchitectureDiagram />

Shodai turns agreements into living, verifiable infrastructure: programmable commitments with explicit state, signatures, transitions, and history.

The agreement definition is the center of the system. It is produced from the data standard, rendered as human-readable agreement content, and deployed to the onchain engine to create a deployed agreement instance. Outside the protocol core, SDKs, APIs, and applications use definitions and instances to create user experiences around agreement creation, submission, inspection, and monitoring.

| Layer                              | Role                                                                                                                                      |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Data standard                      | Defines the common agreement language and schema semantics.                                                                               |
| Agreement definition               | Describes prose, variables, participant-backed address variables, inputs, states, transitions, actions, and references.                   |
| Deployed agreement instance        | Records live initialization values, participant address mappings, current state, accepted inputs, emitted events, and transition history. |
| Onchain execution engine           | Deploys agreement instances and enforces valid inputs, issuers, states, transitions, and recorded history.                                |
| SDK and client libraries           | Help applications validate, prepare, sign, deploy, submit inputs, and read agreement state.                                               |
| Supporting API / application layer | Creates, deploys, indexes, monitors, notifies, and integrates agreements into products.                                                   |
| Pluggable modules                  | Add specialized capabilities such as payment integrations, roles, attestations, dispute flows, and domain-specific execution.             |

## Native vs composed behavior

Shodai's core engine focuses on agreement structure, authorization, valid inputs, state progression, and verifiable history.

Domain-specific behavior such as escrow, payment execution, dispute resolution, compliance checks, notifications, or private-data verification can be composed through modular actions, external modules, or application-layer integrations.

This keeps the core agreement model generic while allowing richer behavior to be added around it.

## Data standard in practice

The data standard gives every agreement the same shape: metadata, typed variables, human-readable content, expected inputs, lifecycle states, and transitions. In a Memorandum of Understanding template, the diagram below is derived from the `execution` section of the agreement definition.

The following visual example shows an MOU agreement definition and a derived state machine. The execution path is: PENDING\_PARTY\_A\_SIGNATURE --partyAData--> PENDING\_PARTY\_B\_SIGNATURE --partyBData--> PENDING\_ACCEPTANCE; PENDING\_ACCEPTANCE --accepted--> ACCEPTED; PENDING\_ACCEPTANCE --rejected--> REJECTED.

<AgreementStateMachineExample
  title="MOU execution path"
  agreement={{
"metadata": {
  "id": "did:example:mou-v1",
  "templateId": "did:template:mou-v1",
  "version": "1.0.0",
  "createdAt": "2024-03-20T12:00:00Z",
  "name": "Memorandum of Understanding",
  "author": "Agreements Protocol",
  "description": "Template for non-binding memorandum of understanding between two parties"
},
"variables": {
  "partyAEthAddress": {
    "type": "address",
    "subtype": "participant",
    "name": "Party A Address",
    "validation": {
      "required": true
    }
  },
  "partyAName": {
    "type": "string",
    "name": "Party A Name",
    "validation": {
      "required": true,
      "minLength": 1
    }
  },
  "partyASignature": {
    "type": "string",
    "subtype": "signature",
    "name": "Party A Signature",
    "validation": {
      "required": true
    }
  },
  "partyBEthAddress": {
    "type": "address",
    "subtype": "participant",
    "name": "Party B Address",
    "validation": {
      "required": true
    }
  },
  "partyBName": {
    "type": "string",
    "name": "Party B Name",
    "validation": {
      "required": true,
      "minLength": 1
    }
  },
  "partyBSignature": {
    "type": "string",
    "subtype": "signature",
    "name": "Party B Signature",
    "validation": {
      "required": true
    }
  },
  "effectiveDate": {
    "type": "dateTime",
    "name": "Effective Date",
    "validation": {
      "required": true
    }
  },
  "scope": {
    "type": "string",
    "name": "Scope of Cooperation",
    "validation": {
      "required": true
    }
  },
  "termDuration": {
    "type": "string",
    "name": "Term Duration",
    "validation": {
      "required": true
    }
  }
},
"content": {
  "type": "md",
  "data": "# MEMORANDUM OF UNDERSTANDING\n\n**BETWEEN PARTY A:** ${variables.partyAName}\n\n**AND PARTY B:** ${variables.partyBName}\n\n**EFFECTIVE DATE:** ${variables.effectiveDate}\n\n## Scope of cooperation\n\n${variables.scope}\n\n## Term\n\nThis MOU remains in effect for ${variables.termDuration} unless terminated earlier by the parties.\n\n## Signatures\n\n${variables.partyASignature}\n${variables.partyBSignature}"
},
"execution": {
  "states": {
    "PENDING_PARTY_A_SIGNATURE": {
      "name": "Pending Signature From A",
      "description": "Party A supplies the proposed terms and signature data."
    },
    "PENDING_PARTY_B_SIGNATURE": {
      "name": "Pending Signature From B",
      "description": "Party B accepts the proposed MOU terms and supplies signature data."
    },
    "PENDING_ACCEPTANCE": {
      "name": "Pending Final Acceptance",
      "description": "Party A accepts or rejects Party B's data."
    },
    "ACCEPTED": {
      "name": "Agreement Accepted",
      "description": "The agreement has been accepted by both parties."
    },
    "REJECTED": {
      "name": "Agreement Rejected",
      "description": "The agreement has been rejected and will not proceed."
    }
  },
  "initialize": {
    "name": "Initialize",
    "initialState": "PENDING_PARTY_A_SIGNATURE",
    "data": {
      "partyAEthAddress": "${variables.partyAEthAddress}",
      "partyBEthAddress": "${variables.partyBEthAddress}"
    }
  },
  "inputs": {
    "partyAData": {
      "type": "VerifiedCredentialEIP712",
      "schema": "verified-credential-eip712.schema.json",
      "displayName": "Party A Signature",
      "data": {
        "partyAName": "${variables.partyAName}",
        "scope": "${variables.scope}",
        "termDuration": "${variables.termDuration}",
        "effectiveDate": "${variables.effectiveDate}"
      },
      "issuer": "${variables.partyAEthAddress.value}"
    },
    "partyBData": {
      "type": "VerifiedCredentialEIP712",
      "schema": "verified-credential-eip712.schema.json",
      "displayName": "Party B Signature",
      "data": {
        "partyBName": "${variables.partyBName}",
        "partyBSignature": "${variables.partyBSignature}"
      },
      "issuer": "${variables.partyBEthAddress.value}"
    },
    "accepted": {
      "type": "VerifiedCredentialEIP712",
      "schema": "verified-credential-eip712.schema.json",
      "displayName": "Party A Accepted Party B's Data",
      "data": {
        "partyASignature": "${variables.partyASignature}"
      },
      "issuer": "${variables.partyAEthAddress.value}"
    },
    "rejected": {
      "type": "VerifiedCredentialEIP712",
      "schema": "verified-credential-eip712.schema.json",
      "displayName": "Party A Rejected Party B's Data",
      "data": {
        "partyARejectionSignature": {
          "type": "string",
          "subtype": "signature",
          "name": "Party A Rejection Signature",
          "validation": {
            "required": true
          }
        }
      },
      "issuer": "${variables.partyAEthAddress.value}"
    }
  },
  "transitions": [
    {
      "from": "PENDING_PARTY_A_SIGNATURE",
      "to": "PENDING_PARTY_B_SIGNATURE",
      "conditions": [
        {
          "type": "isValid",
          "input": "partyAData"
        }
      ]
    },
    {
      "from": "PENDING_PARTY_B_SIGNATURE",
      "to": "PENDING_ACCEPTANCE",
      "conditions": [
        {
          "type": "isValid",
          "input": "partyBData"
        }
      ]
    },
    {
      "from": "PENDING_ACCEPTANCE",
      "to": "ACCEPTED",
      "conditions": [
        {
          "type": "isValid",
          "input": "accepted"
        }
      ]
    },
    {
      "from": "PENDING_ACCEPTANCE",
      "to": "REJECTED",
      "conditions": [
        {
          "type": "isValid",
          "input": "rejected"
        }
      ]
    }
  ]
}
}}
/>

## Definition vs instance

An agreement definition describes the agreement's terms and valid execution path. It is authored from the agreement data standard and can be rendered, validated, inspected, and prepared for deployment.

A deployed agreement instance is the runtime form of that definition. It has live initialization values, participant address mappings, current state, accepted inputs, emitted events, and execution history.

## How the parts fit together

1. Author an agreement definition from the [agreement data standard](/system-architecture/data-standard).
2. Render the same definition for human review and application workflows.
3. Use SDKs or the Agreements API to validate, prepare, sign, and deploy the agreement.
4. Deploy the definition to the [onchain execution engine](/system-architecture/on-chain), creating a deployed agreement instance.
5. Participants submit defined inputs to the deployed instance as the agreement progresses.
6. The engine validates issuer constraints, schemas, states, and transitions, then records state and history.
7. Applications can read deployed instance state directly or use the [Agreements API](/system-architecture/putting-it-together) for indexing, monitoring, notifications, and product integrations.

## API-assisted and direct onchain operation

The documented happy path uses the Shodai API and SDK to simplify validation, signing, deployment, indexing, and application integration.

The onchain execution layer is the verifiable substrate. Developers who need stronger trust-minimization or resilience can inspect the relevant contracts, typed data, chain configuration, addresses, and events directly in [`CNSLabs/agreements-protocol-evm`](https://github.com/CNSLabs/agreements-protocol-evm).

The API-assisted path and the direct onchain path are complementary:

* the API helps applications deploy and operate agreements more easily
* the onchain engine provides the verifiable agreement runtime
* EIP-712 typed data connects user authorization to onchain execution

## Protocol boundary

The protocol is defined by the agreement data standard and agreement definitions. The EVM engine is the first concrete execution engine for those semantics.

The API does not define what an agreement means. It helps applications work with agreement definitions and deployed agreement instances by creating, deploying, indexing, monitoring, notifying, and integrating agreements into products.

| Boundary               | Includes                                                                                                  |
| ---------------------- | --------------------------------------------------------------------------------------------------------- |
| Protocol semantics     | Agreement data standard and agreement definition.                                                         |
| Runtime implementation | Onchain execution engine that interprets agreement definitions and enforces deployed agreement instances. |
| Developer tooling      | SDKs and client libraries for validation, signing, deployment, input submission, and reads.               |
| Application layer      | API services, indexing, monitoring, notifications, integrations, and user experiences.                    |

## What becomes verifiable

Agreements Protocol makes agreement progression constrained, inspectable, and verifiable. Once an agreement definition is deployed, participants and agents can inspect the current state, accepted inputs, and transition history without relying only on an application, API, or private database.

| Verifiable property       | What it means                                                                      |
| ------------------------- | ---------------------------------------------------------------------------------- |
| Constrained inputs        | Only defined inputs can move the agreement.                                        |
| Authorized issuers        | Inputs can be bound to specific parties or addresses.                              |
| Deterministic progression | Valid inputs produce defined state transitions.                                    |
| Auditable sequence        | Accepted inputs and state changes are preserved.                                   |
| Inspectable state         | Current state and history can be read by applications, parties, and agents.        |
| Reduced ambiguity         | The agreement's operational path is explicit, not hidden in prose or private code. |

## What this enables

As a developer, you get a reusable way to model agreements, deterministic multi-party execution, verifiable outcomes across participants, and a consistent interface across different agreement types.

That matters because the agreement definition becomes the shared object that humans read, applications render, agents inspect, and execution engines enforce.

## Choose your path

<CardGroup cols={2}>
  <Card title="Agreement data standard" icon="file-code" href="/system-architecture/data-standard">
    Understand the JSON structure that describes agreement prose, variables, inputs, states, transitions, and valid execution paths.
  </Card>

  <Card title="Onchain execution engine" icon="workflow" href="/system-architecture/on-chain">
    See how agreement definitions become verifiable onchain agreement instances.
  </Card>

  <Card title="Agreements API" icon="code" href="/system-architecture/putting-it-together">
    Understand why the API is the product integration layer around agreement creation, deployment, monitoring, and participant workflows.
  </Card>

  <Card title="Repositories" icon="folder" href="/system-architecture/repositories">
    Learn which repositories own the data standard, EVM execution engine, and TypeScript API client.
  </Card>
</CardGroup>

## Summary

The data standard defines the common language. The agreement definition describes the prose, variables, inputs, states, transitions, and valid execution path. A deployed agreement instance records current state, accepted inputs, events, and transition history. SDKs, APIs, and application workflows make it easier to create, deploy, monitor, and integrate agreements into real products.

The result is a protocol for agreement progression: not just what parties accepted, but how the agreement can move, who can move it, and how that movement can be inspected.
