ConcordFlint

Architecture & Data Flow

Concord is a decentralized coordination engine. State is driven by an append-only event log, projected into local views, reconciled against container runtimes, and synchronized across nodes over an encrypted mesh.


Data Flow Diagrams

1. Workload Submission Flow

CLI / Go SDK
     │
     │ (1) POST /workload/submit
     │     (JSON Workload Spec)
     ▼
Unix IPC Server
(~/.config/concord/concord.sock)
     │
     │ (2) Record "workload.spec"
     │     Event
     ▼
Append-Only Journal
(journal.jsonl)
     │
     │ (3) Deterministic
     │     Projection
     ▼
bbolt KV Views
(Workloads, EventsByID, ByNode)

2. Local Workload Reconciliation & Execution Flow

bbolt KV Views (Desired State)
     │
     │ (4) Active Workload Specs
     ▼
Reconciler Loop
     │
     ├──► (5) Fetch Image
     │         │
     │         ▼
     │    Embedded OCI Registry
     │    (Zot localhost:8444)
     │
     └──► (6) Lifecycle Control
               │
               ▼
          Container Runtime
          (internal/cr)
               │
               ├──► cgroups (CPU/Mem)
               │
               ├──► runc (Namespaces)
               │
               ├──► Bridge & veth (concord0)
               │
               └──► Health Check (/health)

3. Peer Discovery & WireGuard Mesh Flow

Node Discovery
     │
     ├──► mDNS (LAN Multicast)
     │         │
     ├──► SWIM Gossip (UDP :17946)
     │         │
     └──► DNS Server (SRV/A :15353)
               │
               ▼
     Peer Memberlist
               │
               │ (7) Exchange WG Keys & IPs
               ▼
     WireGuard Mesh (internal/cn)
     (Flat Encrypted P2P Overlay)

4. Cross-Node State & Image Replication Flow

[ Remote Node B ]
Transport Server & Registry
       │
       │ (8) Noise Pull Events
       │ (10) P2P Image/Blob Sync
       │ (Over WireGuard Mesh)
       ▼
[ Local Node A ]
Peer Sync Loop (internal/peersync)
       │
       ├──► (9) Missing Events
       │         │
       │         ▼
       │    Local Journal (journal.jsonl)
       │         │
       │         ▼
       │    Local bbolt Views
       │         │
       │         ▼
       │    Reconciler ──► runc
       │
       └──► (10) Missing Blobs
                 │
                 ▼
            Embedded OCI Registry
            (localhost:8444)

Underlay vs overlay

Concord uses two disjoint IPv4 spaces.

Underlay is the host NIC. Memberlist, mDNS, and the Noise transport use it. Join targets are underlay addresses. Peers dial whatever ResolveAdvertise published, never cn0.

Overlay is 10.0.0.0/16. Each node has cn0 at 10.0.{index}.1/24 and allocates containers in that /24. WireGuard carries peer /24s between nodes. Memberlist must not advertise cn0, wg-*, or any 10.0.0.0/16 address. Joining an overlay IP on a node that also has cn0 is a local TCP connect, not a peer.

Resonance simulator attaches a tun as the underlay NIC. That tun must not use 10.0.0.0/16. Use a disjoint prefix such as 192.168.100.0/24, one address per node. Overlay stays 10.0.0.0/16 inside each netns. If the tun is 10.0.0.1 / 10.0.0.2, Concord cannot tell underlay from cn0, Join hits the local bridge, and membership stays at one node.


Scheduling

In each connected segment, the node with the lowest UUID string is the leader. It assigns unassigned workloads to the peer with the fewest active workloads. When segments reunite, journals sync and state converges.


Conflict model

Concord sidesteps most conflicts by construction: every workload run mints a fresh unique ID, so concurrent submissions never disagree about the same key. Merge of distinct IDs is a union.

That union is a CvRDT (convergent replicated data type): the journal is a grow-only set of immutable events, merge is set union, and union is associative, commutative, and idempotent. Sync applies unknown IDs and skips known ones, so every segment converges to the same event set regardless of partition history or arrival order. Views are deterministic projections of that set: tombstone dominance, byte-larger live specs, and highest-generation pins all resolve without clocks.

The sidestep leaks in one place. Nodes re-record the spec (scheduler claiming writes its own workload.spec copy per ID), so one workload ID can end up with several distinct spec events from different authors. These are conflicting same-ID writes, and the view must resolve them deterministically.

Landed rules in internal/journalview/workloads.go (putEvent):

  • Tombstone dominance. A stored tombstone (Removed=true) is never replaced by a live spec copy, regardless of arrival order. A stop wins over any spec copy.
  • Live-live tiebreak. Two live specs for one ID resolve by deterministic comparison: the byte-larger serialization wins, regardless of arrival order.

Both rules are order-independent: every node converges to the same stored copy no matter the sync arrival sequence. No wall-clock or logical timestamp participates in these paths.