Core Concepts
Understanding these five concepts makes every other part of cloud-arch click into place.
1. Topology vs Animation
Every cloud-arch script has two distinct phases that must not be mixed up.
Topology describes what exists: groups, nodes, and the physical connections between them. This is the infrastructure layer — servers, databases, message brokers, their groupings, and the TCP sockets that exist between them at rest.
Animation describes what happens: a sequence of packets traveling through the topology along specific paths, with explanatory messages at each hop. This is the runtime layer — a single request, a failure cascade, a replication event.
Script execution
│
├── Phase 1: TopologyBuilder
│ ├── createGroup(...)
│ ├── addProcess(...)
│ ├── connect(...)
│ └── apply() ← commits to store, canvas renders
│
└── Phase 2: FlowBuilder
├── scenario(...)
├── from(...).to(...)
├── showMessage(...)
└── return flow ← returned to runner, player appearsawait topology.apply() must complete before you create a FlowBuilder. The FlowBuilder reads the node and edge store to resolve animation paths.
2. Edges represent TCP connections
An edge created by topology.connect(A, B) represents a TCP connection (or equivalent transport-layer socket) where A opens the connection to B.
The arrow direction answers the question: who dials whom?
It does not mean data only flows from A to B. Once a TCP connection is established, data flows in both directions. Responses travel back along the same edge in reverse — and the FlowBuilder animation reflects this automatically.
connect('api', 'pg')
Means: API server dials Postgres (TCP handshake initiated by API)
Arrow: api ──────────────────► pg
Animation forward: api → pg (query)
Animation reverse: pg → api (result rows) — same edge, opposite directionNever add a second edge for responses. connect('pg', 'api') in addition to connect('api', 'pg') creates two separate edges — a duplicate that will make the diagram incorrect and the animation confusing.
The practical consequence: when you build a proxy, load balancer, or API gateway, all client traffic must pass through the proxy node. You should never have a direct edge from a client to a backend that bypasses the proxy, even for responses.
3. Layer system
Layers allow a single script to express multiple views of the same system without duplicating code. Each node, edge, and animation scenario can be tagged with one or more layer names.
// This node only appears when the 'v2' layer is active
group.addProcess({
id: 'cache',
label: 'Redis Cache',
shape: 'cylinder',
layer: 'v2',
state: 'running',
});
// This connection only appears in v2 and v3
topology.connect('api', 'cache', {
protocol: 'redis',
label: 'cache lookup',
layer: 'v2',
});Layer pills appear in the top-right corner of the canvas. Each unique layer name in the script becomes a toggle button. Clicking a pill shows or hides all elements tagged with that layer.
Common naming conventions:
| Pattern | Use case |
|---|---|
v1, v2, v3 | Architectural evolution over time |
mvp, highload | Scalability tiers |
prod, staging | Environment comparison |
happy, failure | Failure mode overlay |
Elements with no layer property are always visible regardless of which pills are active.
4. Replica naming convention
Node IDs follow a convention that lets the FlowBuilder implement load balancing and round-robin automatically.
If a node ID ends with a numeric suffix (e.g., api-1, api-2, api-3), the FlowBuilder treats those nodes as replicas of the base name api. When an animation step targets api, the system randomly distributes packets across api-1, api-2, and api-3.
backendGroup
.addProcess({ id: 'api-1', label: 'API Pod 1', state: 'running' })
.addProcess({ id: 'api-2', label: 'API Pod 2', state: 'running' })
.addProcess({ id: 'api-3', label: 'API Pod 3', state: 'running' });
// In the animation, .to('api') sends to one of api-1, api-2, api-3
flow.from('lb').to('api').showMessage('[ROUTED] Round-robin pick');IDs with non-numeric suffixes are treated as distinct nodes. cb-counter and cb-state are separate nodes, not replicas of cb.
5. Execution pipeline
When you click Run, the following sequence happens:
1. Script source (string)
│
▼
2. Sandbox runner
(wraps script in async function, injects globals)
│
▼
3. TopologyBuilder.apply()
├── Writes nodes[] to Zustand store
├── Writes edges[] to Zustand store
└── Triggers React re-render
│
▼
4. React Flow render
├── Lays out nodes using stored positions
├── Renders custom node components (ProcessNode, GroupNode, …)
└── Renders custom edge components (AnimatedFloatingEdge, PulseEdge, …)
│
▼
5. FlowBuilder (returned from script)
├── Parses scenario chains into step arrays
├── Resolves node IDs (including replica expansion)
└── Stored in AnimationPlayer state
│
▼
6. AnimationPlayer
├── Plays steps sequentially on Spacebar / Play button
├── Sends packet animations along edges
└── Triggers showMessage / showError overlaysThe key insight is that topology and animation are completely decoupled. The topology is committed once and stays in the store. The animation reads from the store but never modifies it — clicking Play/Replay does not re-run the script.
Summary
| Concept | One-line rule |
|---|---|
| Topology | Describes what exists (infrastructure) |
| Animation | Describes what happens (runtime behavior) |
| Edge direction | connect(A, B) means A opens the socket to B |
| Layers | Tags that toggle visibility via pills |
| Replica IDs | name-N (numeric suffix) = replica of name |
| Execution order | apply() first, then return flow |