Skip to content

Diagram conventions

Every architecture diagram in these docs colours each box by the tier it lives in. Five tiers, the same everywhere — memorise them once and you can read any diagram at a glance: colour tells you what kind of thing a box is before you read its label.

The colours are deterministic hex, not theme-derived. Diagrams render client-side and re-theme with the light/dark toggle, but a box’s tier colour stays constant across themes so a screenshot in one theme still reads correctly in the other.

Tier Class fill / stroke What lives here
frontend frontend #6366f1 / #4338ca (indigo) soundverse-saas-2.0 (the Next.js SPA + BFF) and the standalone, demo-only UI2.0 studio
gateway gateway #0ea5e9 / #0369a1 (sky) The trust boundary and request pipeline: core-gateway-consumer, core-identity, and core-mcp (the agent-side boundary + second billing pipeline)
data plane data #8b5cf6 / #6d28d9 (violet) The data owners: core-database (the single Postgres door), core-storage (blob owner), and core-billing
worker worker #10b981 / #047857 (emerald) Task-claiming tool services — every core-tool-* worker, including the agent worker (core-tool-agent)
external external #f59e0b fill, #b45309 stroke (amber) Third parties: Logto (OIDC), the LLM / audio model providers (Anthropic, Kimi, and the song/media model APIs), and Azure Blob

Paste this at the bottom of any Mermaid diagram, then tag each node with :::<class>. It is the canonical legend — do not invent new colours or reorder the tiers.

the shared classDef block
classDef frontend fill:#6366f1,color:#fff,stroke:#4338ca
classDef gateway fill:#0ea5e9,color:#fff,stroke:#0369a1
classDef data fill:#8b5cf6,color:#fff,stroke:#6d28d9
classDef worker fill:#10b981,color:#fff,stroke:#047857
classDef external fill:#f59e0b,color:#111,stroke:#b45309

Amber (external) uses dark label text (color:#111) because the fill is light; the other four use white (color:#fff). Keeping these fixed is what makes the fleet’s diagrams read as one system.

  1. Draw the diagram with plain node ids and labels — no styling yet.

  2. Tag each node with its tier using Mermaid’s inline class syntax id:::className (or a trailing class id className line). A node with no tag falls back to the theme default, which is the right choice only for the neutral backing stores above.

  3. Append the classDef block verbatim from the section above. Order the five lines the same way every time so diffs stay clean.

A minimal flowchart that exercises all five tiers plus one neutral store:

flowchart LR
  UI["saas-2.0<br/>SPA + BFF"]:::frontend
  GW["core-gateway-consumer"]:::gateway
  ID["core-identity"]:::gateway
  DB["core-database"]:::data
  W["core-tool-sansaarm"]:::worker
  LLM["Logto / model providers"]:::external
  PG[("Postgres")]

  UI -->|"/api/* BFF"| GW
  GW -->|validate| ID
  ID -->|JWKS| LLM
  GW -->|"single door"| DB
  DB --- PG
  GW -. "enqueue task" .-> W
  W -->|"model call"| LLM

  classDef frontend fill:#6366f1,color:#fff,stroke:#4338ca
  classDef gateway  fill:#0ea5e9,color:#fff,stroke:#0369a1
  classDef data     fill:#8b5cf6,color:#fff,stroke:#6d28d9
  classDef worker   fill:#10b981,color:#fff,stroke:#047857
  classDef external fill:#f59e0b,color:#111,stroke:#b45309

The trust boundary reads visually: everything sky (gateway) is the authorise → price → reserve → queue pipeline, violet (data) is the one Postgres door behind it, emerald (worker) is the fleet doing the heavy lifting off the request path, and amber (external) is anything the fleet doesn’t own.

  • Direction: prefer flowchart LR for topology (frontend on the left, external on the right, so the trust boundary runs vertically down the middle) and sequenceDiagram with autonumber for request lifecycles.
  • Edges tell you the plane: a solid arrow is a synchronous call; a dotted arrow (-. label .->) is an async enqueue onto the task queue. The gateway-to-worker hop is always dotted — the worker claims the task later, it isn’t called inline.
  • Theme-neutral, always. Don’t hardcode a background or rely on default node fills for meaning — the toggle flips them. Meaning lives in the tier classDef, which is fixed.
  • One tier per box. If a box feels like two tiers (for example core-mcp is both a boundary and a billing engine), pick the tier that matches its role in that diagram and say the rest in prose.