Skip to content

docs: add interactive architecture diagram#189

Open
MegaRedHand wants to merge 3 commits intomainfrom
architecture-diagram
Open

docs: add interactive architecture diagram#189
MegaRedHand wants to merge 3 commits intomainfrom
architecture-diagram

Conversation

@MegaRedHand
Copy link
Collaborator

This PR adds an interactive architecture diagram for ethlambda.

To visualize it, run:

curl https://raw.githubusercontent.com/lambdaclass/ethlambda/refs/heads/architecture-diagram/docs/infographics/ethlambda_architecture.html -o ethlambda_architecture.html && open ethlambda_architecture.html

@MegaRedHand MegaRedHand added the documentation Improvements or additions to documentation label Mar 6, 2026
@github-actions
Copy link

github-actions bot commented Mar 6, 2026

🤖 Kimi Code Review

This is a documentation-only PR adding an interactive HTML architecture diagram. No Rust code changes, so no security or correctness issues to review. The diagram accurately reflects the current actor-based design with:

  1. Proper separation of concerns (consensus, networking, storage, crypto)
  2. Correct message flow between actors via Recipient handles
  3. Accurate representation of the 800ms tick system
  4. Proper crate dependency relationships

The documentation is well-structured and will help new contributors understand the codebase architecture. The interactive elements (clickable nodes, tabs) work as expected.


Automated review by Kimi (Moonshot AI) · custom prompt

@github-actions
Copy link

github-actions bot commented Mar 6, 2026

🤖 Codex Code Review

  1. Medium – DOM injection pattern via innerHTML

    • In ethlambda_architecture.html:980, body.innerHTML = html renders strings assembled from COMPONENTS fields (ethlambda_architecture.html:668).
    • Today those strings are static in-file, so exploitability is low now, but this is a fragile pattern if any content later becomes external/generated.
    • Suggestion: build DOM nodes with createElement + textContent for desc, tags, and details.
  2. Low – Resize handler can cause unnecessary redraw churn

    • ethlambda_architecture.html:1142 redraws arrows on every resize event; these fire rapidly and can cause UI jank.
    • Suggestion: debounce/throttle redraw (for example, requestAnimationFrame or a short timeout).
  3. Low – External font dependency in a local doc artifact

    • ethlambda_architecture.html:13 pulls Google Fonts at runtime.
    • Suggestion: vendor fonts or use local/system fallbacks to keep docs reproducible/offline and avoid third-party fetches.

Assumptions:

  • The provided diff only adds one documentation HTML file; no Rust/consensus logic changed, so no fork-choice/STF/attestation/XMSS/SSZ behavior regression is introduced by this PR itself.

Overall:

  • No correctness or consensus-critical code findings in this PR. The comments above are primarily security-hardening and maintainability improvements for the new docs artifact.

Automated review by OpenAI Codex · custom prompt

@github-actions
Copy link

github-actions bot commented Mar 6, 2026

🤖 Claude Code Review

Now I have a complete picture of the PR. Let me compile the review.


PR Review: docs — Interactive Architecture Diagram

This PR adds a single-file interactive HTML architecture diagram (docs/infographics/ethlambda_architecture.html, 1152 lines). There are no Rust code changes, so consensus correctness, state transition logic, and cryptographic handling are unaffected.


External Font Dependency (Functional Concern)

The file loads fonts from Google Fonts at runtime:

<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
  • The diagram requires an internet connection to render as intended. In offline or air-gapped environments the browser falls back to generic sans-serif, which degrades the monospace code labels significantly.
  • Google Fonts requests carry a privacy implication: Google can observe IPs of anyone who opens the file.

Consider either inlining the font via @font-face with base64 data URIs, or using system font stacks as a robust fallback (font-family: 'Inter', system-ui, sans-serif and font-family: 'JetBrains Mono', 'Cascadia Code', monospace).


Tick Interval Count Discrepancy

The COMPONENTS data and the "Block Data Flow" flow-step node describe the tick system as having 5 intervals:

'800ms', '5 intervals', 'send_after', '#[protocol]'
// ...
'Interval 3/4: accept accumulated attestations',

The flow card also reads "Interval 3/4: accept atts". However, CLAUDE.md documents 4 intervals (0–3), each 1 second apart in a 4-second slot. The math in the diagram (5 × 800ms = 4s) is internally consistent, but this contradicts the reference docs and could confuse readers.

If the code genuinely has 5 intervals at 800ms, CLAUDE.md should be updated to match. If there are 4 intervals at 1000ms, the diagram labels should be corrected.


Minor Content Notes

  • The RPC node subtitle says Axum HTTP :5054. The codebase docs don't confirm this port — worth double-checking against the actual CLI default.
  • The "Block Data Flow" view shows an arrow from fs-msg (NewBlock received) directly to fs-publish (Gossip Publish). This is misleading for the receive path — republishing only happens at tick interval 0 for proposers, not on every received block. Consider either removing this arrow or annotating it as a proposer-only path.
  • The drawActorArrows / drawFlowArrows / drawDepsArrows functions use svg.lastChild.tagName !== 'defs' to clear SVG children. For SVG elements in an HTML document tagName is lowercase, so 'defs' matches correctly — no bug here, just worth noting it's intentional.

What's Working Well

  • The HTML is entirely self-contained JavaScript with no third-party script loading (aside from fonts). All COMPONENTS data is hardcoded, so innerHTML injection from showDetail() uses only trusted static content — no XSS surface.
  • The four views (Actor Model, Block Data Flow, Crate Layers, Crate Dependencies) cover the architecture at different levels of abstraction well.
  • The color system, CSS variables, and dark theme are consistent and readable.
  • Arrow rendering uses getBoundingClientRect relative to the canvas container, which is correct for absolutely-positioned children and handles resize via the window listener.

Summary

No blocking issues. The font dependency is the most practical concern for usability. The tick interval discrepancy is worth clarifying against the source code before merging to avoid documentation drift.


Automated review by Claude (Anthropic) · custom prompt

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 6, 2026

Greptile Summary

This PR adds a single self-contained interactive HTML file (docs/infographics/ethlambda_architecture.html) that visualizes the ethlambda architecture across four tabbed views: Actor Model, Block Data Flow, Crate Layers, and Crate Dependencies. The file has no impact on the Rust codebase and is purely additive documentation.

Key findings:

  • Content rendering bug: showDetail() inserts tag and detail strings from the COMPONENTS data object directly into innerHTML without HTML-escaping. Strings like Handler<M>, Handler<InitP2P>, Recipient<M>, and Stream<SwarmEvent> are parsed as HTML elements by the browser, causing type parameter names to disappear from the rendered detail panel. This affects many components across all four views.
  • Unused variable: const api is computed in drawActorArrows() but never referenced.
  • External font dependency: Fonts are loaded exclusively from Google Fonts CDN with no offline fallback defined in the CSS font stacks, making the diagram degrade visually without internet access.

Confidence Score: 4/5

  • Safe to merge — purely additive documentation with no impact on the Rust codebase, though the detail panel has a content rendering bug that reduces documentation accuracy.
  • The change is a standalone HTML documentation file with no effect on the application code. The primary finding is a content correctness bug (HTML-unescaped type parameters in innerHTML) that causes type parameter names like <M> and <InitP2P> to be silently dropped from the rendered detail panel, reducing the quality of the documentation. The other two issues are lower severity: one unused variable (minor code quality) and missing font fallbacks for offline viewing (cosmetic/resilience). The PR is safe to merge but the innerHTML rendering bug should ideally be fixed before the diagram accurately represents the architecture.
  • docs/infographics/ethlambda_architecture.html — specifically the showDetail() function around line 966–974 where raw strings containing angle brackets are inserted into innerHTML without escaping.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Main["bin/ethlambda\n(tokio::main)"]

    Main -->|"InitP2P / InitBlockChain"| BC
    Main -->|spawn| P2P
    Main -->|start_rpc_server| RPC

    subgraph Consensus ["BlockChain Actor (crates/blockchain)"]
        BC["BlockChain\n#[actor]"]
        Store["Store\n(fork choice state)"]
        KeyMgr["KeyManager\n(XMSS signing)"]
        ForkChoice["LMD GHOST\n(fork_choice)"]
        STF["State Transition\n(process_slots / process_block)"]
        BC --> Store
        BC --> KeyMgr
        Store --> ForkChoice
        Store --> STF
    end

    subgraph P2PLayer ["P2P Actor (crates/net)"]
        P2P["P2PServer\n#[actor]"]
        SwarmAdapter["SwarmAdapter\n(tokio task)"]
        GossipSub["GossipSub\n(blocks + attestations)"]
        ReqResp["Req/Resp\n(Status + BlocksByRoot)"]
        P2P --> SwarmAdapter
        SwarmAdapter --> GossipSub
        SwarmAdapter --> ReqResp
    end

    subgraph Shared ["Shared Infrastructure"]
        RocksDB["RocksDB Backend\nArc&lt;dyn StorageBackend&gt;"]
        Crypto["XMSS Crypto\n(leansig + leanVM)"]
    end

    NetworkAPI["network-api\n(Recipient&lt;M&gt; messages)"]

    BC <-->|"Recipient&lt;NewBlock&gt;\nRecipient&lt;PublishBlock&gt;"| NetworkAPI
    NetworkAPI <-->|"typed actor messages"| P2P
    RPC -->|"read Store directly"| RocksDB
    BC -->|"persist"| RocksDB
    P2P -->|"persist"| RocksDB
    KeyMgr --> Crypto
Loading

Last reviewed commit: 1cef6e8

Comment on lines +966 to +974
let html = '<div class="section-label">Overview</div><p>' + data.desc + '</p>';
if (data.tags?.length) {
html += '<div class="section-label">Tags</div>';
html += data.tags.map(t => '<span class="tag" style="background:' + lc.bg + ';border:1px solid ' + lc.border + ';color:' + lc.text + ';">' + t + '</span>').join('');
}
if (data.details?.length) {
html += '<div class="section-label">Details</div><ul>' + data.details.map(d => '<li>' + d + '</li>').join('') + '</ul>';
}
body.innerHTML = html;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type parameters stripped from rendered detail panel

showDetail() builds the HTML for tags and details by directly concatenating raw strings from the COMPONENTS object into an innerHTML assignment. Several tags and details values contain angle-bracket notation (e.g. Handler<M>, Handler<InitP2P>, Recipient<M>, start_swarm_adapter() -> (Stream<SwarmEvent>, SwarmHandle)) that the browser will parse as unknown HTML elements rather than literal text.

For single-letter type params like <M>, the browser creates an empty unknown element and the letter is swallowed — so Handler<M> renders as just "Handler". For longer type params like <InitP2P>, the element name disappears and the following text becomes the hidden inner text of an unknown element.

Affected values include (non-exhaustive):

  • Tags: Handler<M> (blockchain, p2p), Recipient<M> (netapi)
  • Details: Handler<InitP2P>, Handler<InitBlockChain>, Handler<PublishBlock>, Handler<NewBlock>, Handler<WrappedSwarmEvent>, Recipient<M> is type-erased…, start_swarm_adapter() -> (Stream<SwarmEvent>…)

Fix by escaping < and > before inserting into HTML:

Suggested change
let html = '<div class="section-label">Overview</div><p>' + data.desc + '</p>';
if (data.tags?.length) {
html += '<div class="section-label">Tags</div>';
html += data.tags.map(t => '<span class="tag" style="background:' + lc.bg + ';border:1px solid ' + lc.border + ';color:' + lc.text + ';">' + t + '</span>').join('');
}
if (data.details?.length) {
html += '<div class="section-label">Details</div><ul>' + data.details.map(d => '<li>' + d + '</li>').join('') + '</ul>';
}
body.innerHTML = html;
function escapeHtml(str) {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
function showDetail(id) {
const data = COMPONENTS[id];
if (!data) return;
const langColors = {
consensus: { bg: 'rgba(155,89,182,0.15)', border: 'var(--consensus)', text: 'var(--consensus-l)' },
net: { bg: 'rgba(0,180,216,0.15)', border: 'var(--net)', text: 'var(--net-l)' },
storage: { bg: 'rgba(46,204,113,0.15)', border: 'var(--storage)', text: 'var(--storage-l)' },
crypto: { bg: 'rgba(243,156,18,0.15)', border: 'var(--crypto)', text: 'var(--crypto-l)' },
types: { bg: 'rgba(231,76,60,0.15)', border: 'var(--types)', text: 'var(--types-l)' },
entry: { bg: 'rgba(224,123,57,0.2)', border: 'var(--rust)', text: 'var(--rust-light)' },
api: { bg: 'rgba(26,188,156,0.15)', border: 'var(--api)', text: 'var(--api-l)' },
};
const lc = langColors[data.lang] || langColors.entry;
const header = document.getElementById('panel-header');
const body = document.getElementById('panel-body');
const ph = document.getElementById('panel-placeholder');
header.style.display = 'block';
header.style.borderColor = lc.border;
document.getElementById('panel-title').textContent = data.title;
document.getElementById('panel-title').style.color = lc.text;
document.getElementById('panel-subtitle').textContent = data.subtitle;
if (ph) ph.remove();
let html = '<div class="section-label">Overview</div><p>' + escapeHtml(data.desc) + '</p>';
if (data.tags?.length) {
html += '<div class="section-label">Tags</div>';
html += data.tags.map(t => '<span class="tag" style="background:' + lc.bg + ';border:1px solid ' + lc.border + ';color:' + lc.text + ';">' + escapeHtml(t) + '</span>').join('');
}
if (data.details?.length) {
html += '<div class="section-label">Details</div><ul>' + data.details.map(d => '<li>' + escapeHtml(d) + '</li>').join('') + '</ul>';
}
body.innerHTML = html;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/infographics/ethlambda_architecture.html
Line: 966-974

Comment:
**Type parameters stripped from rendered detail panel**

`showDetail()` builds the HTML for tags and details by directly concatenating raw strings from the `COMPONENTS` object into an `innerHTML` assignment. Several `tags` and `details` values contain angle-bracket notation (e.g. `Handler<M>`, `Handler<InitP2P>`, `Recipient<M>`, `start_swarm_adapter() -> (Stream<SwarmEvent>, SwarmHandle)`) that the browser will parse as unknown HTML elements rather than literal text.

For single-letter type params like `<M>`, the browser creates an empty unknown element and the letter is swallowed — so `Handler<M>` renders as just **"Handler"**. For longer type params like `<InitP2P>`, the element name disappears and the following text becomes the hidden inner text of an unknown element.

Affected values include (non-exhaustive):
- Tags: `Handler<M>` (blockchain, p2p), `Recipient<M>` (netapi)
- Details: `Handler<InitP2P>`, `Handler<InitBlockChain>`, `Handler<PublishBlock>`, `Handler<NewBlock>`, `Handler<WrappedSwarmEvent>`, `Recipient<M> is type-erased…`, `start_swarm_adapter() -> (Stream<SwarmEvent>…)`

Fix by escaping `<` and `>` before inserting into HTML:

```suggestion
function escapeHtml(str) {
  return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

function showDetail(id) {
  const data = COMPONENTS[id];
  if (!data) return;
  const langColors = {
    consensus: { bg: 'rgba(155,89,182,0.15)', border: 'var(--consensus)',   text: 'var(--consensus-l)' },
    net:       { bg: 'rgba(0,180,216,0.15)',  border: 'var(--net)',         text: 'var(--net-l)' },
    storage:   { bg: 'rgba(46,204,113,0.15)', border: 'var(--storage)',     text: 'var(--storage-l)' },
    crypto:    { bg: 'rgba(243,156,18,0.15)', border: 'var(--crypto)',      text: 'var(--crypto-l)' },
    types:     { bg: 'rgba(231,76,60,0.15)',  border: 'var(--types)',       text: 'var(--types-l)' },
    entry:     { bg: 'rgba(224,123,57,0.2)',  border: 'var(--rust)',        text: 'var(--rust-light)' },
    api:       { bg: 'rgba(26,188,156,0.15)', border: 'var(--api)',         text: 'var(--api-l)' },
  };
  const lc = langColors[data.lang] || langColors.entry;
  const header = document.getElementById('panel-header');
  const body = document.getElementById('panel-body');
  const ph = document.getElementById('panel-placeholder');
  header.style.display = 'block';
  header.style.borderColor = lc.border;
  document.getElementById('panel-title').textContent = data.title;
  document.getElementById('panel-title').style.color = lc.text;
  document.getElementById('panel-subtitle').textContent = data.subtitle;
  if (ph) ph.remove();
  let html = '<div class="section-label">Overview</div><p>' + escapeHtml(data.desc) + '</p>';
  if (data.tags?.length) {
    html += '<div class="section-label">Tags</div>';
    html += data.tags.map(t => '<span class="tag" style="background:' + lc.bg + ';border:1px solid ' + lc.border + ';color:' + lc.text + ';">' + escapeHtml(t) + '</span>').join('');
  }
  if (data.details?.length) {
    html += '<div class="section-label">Details</div><ul>' + data.details.map(d => '<li>' + escapeHtml(d) + '</li>').join('') + '</ul>';
  }
  body.innerHTML = html;
}
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +1033 to +1037
const bc = getCenter(document.getElementById('an-blockchain'), canvas);
const p2p = getCenter(document.getElementById('an-p2p'), canvas);
const api = getCenter(document.getElementById('an-netapi'), canvas);
makeArrow(svg, bc.right, bc.cy - 6, p2p.left, p2p.cy - 6, 'rgba(26,188,156,0.7)', 'arr-api', true);
makeArrow(svg, p2p.left, p2p.cy + 6, bc.right, bc.cy + 6, 'rgba(26,188,156,0.7)', 'arr-api', true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable api

api is computed via getCenter() on line 1035 but never referenced. Both makeArrow calls on the following lines use only bc and p2p. Remove the dead code:

Suggested change
const bc = getCenter(document.getElementById('an-blockchain'), canvas);
const p2p = getCenter(document.getElementById('an-p2p'), canvas);
const api = getCenter(document.getElementById('an-netapi'), canvas);
makeArrow(svg, bc.right, bc.cy - 6, p2p.left, p2p.cy - 6, 'rgba(26,188,156,0.7)', 'arr-api', true);
makeArrow(svg, p2p.left, p2p.cy + 6, bc.right, bc.cy + 6, 'rgba(26,188,156,0.7)', 'arr-api', true);
const bc = getCenter(document.getElementById('an-blockchain'), canvas);
const p2p = getCenter(document.getElementById('an-p2p'), canvas);
makeArrow(svg, bc.right, bc.cy - 6, p2p.left, p2p.cy - 6, 'rgba(26,188,156,0.7)', 'arr-api', true);
makeArrow(svg, p2p.left, p2p.cy + 6, bc.right, bc.cy + 6, 'rgba(26,188,156,0.7)', 'arr-api', true);
Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/infographics/ethlambda_architecture.html
Line: 1033-1037

Comment:
**Unused variable `api`**

`api` is computed via `getCenter()` on line 1035 but never referenced. Both `makeArrow` calls on the following lines use only `bc` and `p2p`. Remove the dead code:

```suggestion
  const bc = getCenter(document.getElementById('an-blockchain'), canvas);
  const p2p = getCenter(document.getElementById('an-p2p'), canvas);
  makeArrow(svg, bc.right, bc.cy - 6, p2p.left, p2p.cy - 6, 'rgba(26,188,156,0.7)', 'arr-api', true);
  makeArrow(svg, p2p.left, p2p.cy + 6, bc.right, bc.cy + 6, 'rgba(26,188,156,0.7)', 'arr-api', true);
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +52 to +58
html, body {
height: 100%;
font-family: 'Inter', sans-serif;
background: var(--bg);
color: var(--text);
overflow: hidden;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

External font dependency — missing offline fallback

The diagram loads Inter and JetBrains Mono exclusively from fonts.googleapis.com (line 7). The CSS font stacks provide no system-font fallbacks, so the diagram will not render with intended typography when viewed offline, and each viewer's browser makes a request to Google's CDN.

Consider adding system-font fallbacks so the file remains usable without internet access:

Suggested change
html, body {
height: 100%;
font-family: 'Inter', sans-serif;
background: var(--bg);
color: var(--text);
overflow: hidden;
}
html, body {
height: 100%;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--text);
overflow: hidden;
}

Also update monospace font usage (lines 79, 109, 120, 124, etc.) from 'JetBrains Mono', monospace to include fallbacks like 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace.

Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/infographics/ethlambda_architecture.html
Line: 52-58

Comment:
**External font dependency — missing offline fallback**

The diagram loads `Inter` and `JetBrains Mono` exclusively from `fonts.googleapis.com` (line 7). The CSS font stacks provide no system-font fallbacks, so the diagram will not render with intended typography when viewed offline, and each viewer's browser makes a request to Google's CDN.

Consider adding system-font fallbacks so the file remains usable without internet access:

```suggestion
  html, body {
    height: 100%;
    font-family: 'Inter', system-ui, -apple-system, sans-serif;
    background: var(--bg);
    color: var(--text);
    overflow: hidden;
  }
```

Also update monospace font usage (lines 79, 109, 120, 124, etc.) from `'JetBrains Mono', monospace` to include fallbacks like `'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace`.

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant