    digraph G {
    size="7.5,10"
    labeljust="l"
    fontname=Helvetica;
    node [fontname=Helvetica]
    edge [fontname=Helvetica,fontsize=10]

        subgraph cluster0 {
        label="Datastore layer: Syncer goroutine";

            Syncer [shape=box]

            Syncer -> SyncerCallbacks [label="typed KVs"]

            SyncerCallbacks [label=<

Syncer callbacks API
Interface to frontend. OnStatusUpdated(status SyncStatus) OnUpdates(updates []KVPair)

    >, shape=none, margin=0];

            SyncerCallbacks -> SyncerCallbacksDecoupler [penwidth=5];

            SyncerCallbacksDecoupler [label=<

SyncerCallbacksDecoupler
Decouples SyncerCallbacks via channel.

    >, shape=none, margin=0];
        }

        subgraph cluster1 {
        label="Calculation layer: Validation goroutine";
            SyncerCallbacksDecoupler -> ValidationFilter [label="<
>" penwidth=5]; ValidationFilter [label=<

ValidationFilter struct
Runs validation on values.

    >, shape=none, margin=0]
            ValidationFilter -> AsyncCalcGraph [label="SyncerCallbacks", penwidth=5]
        }

        AsyncCalcGraph [label=<

AsyncCalcGraph struct
Puts updates on channel

>, shape=none, margin=0] AsyncCalcGraph -> AsyncCalcGraph2 [label="<
    >\nSyncStatus and []KVPair", penwidth=5]

        subgraph cluster2 {
        label="Calculation layer: graph processing goroutine";

            AsyncCalcGraph2 [label=<

AsyncCalcGraph struct
Converts channel back to method calls. Flushes event buffer after each dispatch.

    >, shape=none, margin=0]
            AsyncCalcGraph2 -> Dispatcher [penwidth=5]
            AsyncCalcGraph2 -> EventBuffer [label="Rate-limited\nFlush()"]

            subgraph {
                /* Put some padding either side of the Dispatcher. */
                rank=same; nodepadding1; Dispatcher; nodepadding2;
                nodepadding1 [style=invisible,fixedsize=true,width=4]
                nodepadding2 [style=invisible,fixedsize=true,width=4]
                nodepadding1 -> Dispatcher [style=invis];
                Dispatcher -> nodepadding2 [style=invis];
            }

            Dispatcher [label=<

Dispatcher
Fans out updates by type

    >, shape=none, margin=0]
            Dispatcher -> PolicyResolver [label="policy, tier\nKVs", penwidth=2]
            Dispatcher -> ActiveRulesCalc [label="policy, profile\nKVs", penwidth=2]

            Dispatcher -> LocalDispatcher [label="all\nendpoint KVs", penwidth=4];
            Dispatcher -> MemberCalc [label="all\nendpoint KVs", penwidth=4];
            Dispatcher -> ConfigBatcher [label="config KVs", penwidth=2];

            LocalDispatcher [label=<

Local endpoint dispatcher
Filters out non-local endpoints.

    >, shape=none, margin=0]
            LocalDispatcher -> ActiveRulesCalc [label="local\nendpoint KVs", penwidth=2];
            LocalDispatcher -> PolicyResolver [label="local\nendpoint KVs", penwidth=2]

            ConfigBatcher [label=<

Config batcher
Combines individual config keys into consistent snapshots.

    >, shape=none, margin=0]
            ConfigBatcher -> PipelineCallbacks [label="OnConfigUpdate()"]

            ActiveRulesCalc [label=<

Active rules calculator
Calculates which policies/profiles are active on this host.

    >, shape=none, margin=0];
            ActiveRulesCalc -> RuleScanner [label="active rule\nupdates"];
            ActiveRulesCalc -> PolicyResolver [label="policy match\nupdates"];

            "<
>" [shape=none] RuleScanner [label=<

Rule scanner
Scans rules, extracts selectors/named ports. Calculates IP set IDs.

>, shape=none, margin=0]; PolicyResolver [label=<

Policy resolver
Calculates the active policies for each active endpoint.

>, shape=none, margin=0]; MemberCalc [label=<

IP set member index
Combines profile labels with endpoint labels; indexes labels against selectors; calculates the IP/port memberships of IP sets; handles de-dupe of members.

    >, shape=none, margin=0];

            RuleScanner -> PipelineCallbacks [label="OnPolicy(In)Active()\nOnProfile(In)Active()"];

            RuleScannerShim [label="<
    >", shape=none]

            RuleScanner -> RuleScannerShim [label="Selector/Named port\n(in)active"];

            RuleScannerShim -> MemberCalc [label="Selector/Named port\nupdates"]
            RuleScannerShim -> PipelineCallbacks [label="OnIPSetAdded/Removed()"];

            MemberCalc -> "<
>" [label="OnIPSetMemberAdded/Removed()"]; "<
    >" -> PipelineCallbacks [label="OnIPSetMemberAdded/Removed()"];
            PolicyResolver -> PipelineCallbacks [label="Endpoints+policies\nOnEndpointTierUpdate()"];

            PipelineCallbacks [label=<

Pipeline callbacks API
Interface to frontend. OnIPSetAdded(setID string, ipSetType proto.IPSetUpdate_IPSetType) OnIPSetMemberAdded(setID string, ip labelindex.IPSetMember) OnIPSetMemberRemoved(setID string, ip labelindex.IPSetMember) OnIPSetRemoved(setID string) OnPolicyActive(model.PolicyKey, *proto.Rules) OnPolicyInactive(model.PolicyKey) OnProfileActive(model.ProfileRulesKey, *proto.Rules) OnProfileInactive(model.ProfileRulesKey) OnEndpointTierUpdate(endpointKey model.Key, OnConfigUpdate(globalConfig, selectorConfig, hostConfig map[string]string) endpoint interface{}, filteredTiers []endpoint.TierInfo)

>, shape=none, margin=0]; PipelineCallbacks -> EventBuffer [label="<>"] EventBuffer [label=<

Event sequencer
Batches/coalesces IP set updates; converts to protobuf structs; does dependency sequencing.

    >, shape=none, margin=0];
        }

        subgraph cluster3 {
        label="Dataplane layer: writer goroutine";
            DataplaneConnection [label=<

Dataplane connection
Marshals messages to/from Dataplane.

    >, shape=none, margin=0];
        }

        EventBuffer -> DataplaneConnection [label="<
    >\nproto.XYZUpdate protobuf structs", penwidth=2]
        DataplaneConnection -> Dataplane [label="protobuf"];
        Dataplane [shape=box, label="Dataplane driver"];
    }