// aborist/mesh — epoch state machine.
//
// Every roster mutation bumps a counter. Old epochs persist forever
// (signatures stay verifiable). New gossip is encrypted under the
// post-rotation secret, so evicted members go opaque from epoch+1.
//
// Render: dot -Tpng docs/diagrams/mesh-epoch-lifecycle.dot -o /tmp/x.png

digraph mesh_epoch_lifecycle {
    rankdir=LR;
    bgcolor="white";
    node [shape=box, style="rounded,filled", fontname="Helvetica"];
    edge [fontname="Helvetica", fontsize=10];

    none    [label="no mesh state\n(meta.mesh.enabled = 0)", fillcolor="#eeeeee"];
    e0      [label="epoch 0\nfounder = sole admin\nreason: 'genesis'", fillcolor="#fff7d6"];
    e1      [label="epoch N\nroster + envelope\nsigned by actor", fillcolor="#d6ffd6"];
    eN      [label="epoch N+1\nfresh secret\nfresh envelope", fillcolor="#d6ffd6"];
    historic [label="every prior epoch\nstays on disk\n(roster + signatures verifiable)", fillcolor="#ffffff", shape=note];

    none -> e0  [label="aborist mesh init --group <name>"];
    e0   -> e1  [label="add | kick | rotate"];
    e1   -> eN  [label="add | kick | rotate"];
    eN   -> eN  [label="rotate (no roster change)"];

    e1 -> historic [style=dashed, arrowhead=none, color="#888888"];
    eN -> historic [style=dashed, arrowhead=none, color="#888888"];

    subgraph cluster_ops {
        label="Operator commands";
        style="rounded,dashed";
        color="#666666";
        node [fillcolor="#e8f0ff", shape=note];
        op_add    [label="aborist mesh add\n--member-id bob ...\nadmin only"];
        op_kick   [label="aborist mesh kick\n--member-id bob ...\nadmin only"];
        op_rotate [label="aborist mesh rotate\nany current member"];
    }
}
