Skip to content

docs: Add architecture guide for component composition#149

Open
benaadams with Copilot wants to merge 2 commits into
masterfrom
copilot/create-architecture-guide
Open

docs: Add architecture guide for component composition#149
benaadams with Copilot wants to merge 2 commits into
masterfrom
copilot/create-architecture-guide

Conversation

Copilot AI commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Contributors lack visibility into how Magma components compose, blocking understanding of where new code integrates.

Changes

Core Documentation (docs/ARCHITECTURE.md)

  • OSI layer mapping: Mermaid diagram showing component relationships from Application layer through platform backends
  • Packet flow: Receive and transmit paths with zero-copy buffer ownership semantics
  • Core abstractions: IPacketTransmitter/IPacketReceiver contract and implementation patterns
  • Delegate chain: How Magma.Network uses delegates for extensible protocol processing
  • Platform integration: NetMap, AF_XDP, and WinTun backend architecture with ring buffer diagrams
  • Extension patterns: Guidelines for adding protocols and platform backends

Key Concepts Explained

Packet processing chain:

// Delegates compose protocol layers
public delegate bool ConsumeInternetLayer(
    in Ethernet ethernetFrame, 
    ReadOnlySpan<byte> input);

// PacketReceiver chains delegates for dispatch
switch (ethernetFrame.Ethertype)
{
    case EtherType.IPv4:
        result = IPv4Consumer?.Invoke(in ethernetFrame, data);
        break;
    // ...
}

Platform abstraction:

// Common contract across NetMap, AF_XDP, WinTun
public interface IPacketTransmitter
{
    bool TryGetNextBuffer(out Memory<byte> buffer);
    Task SendBuffer(ReadOnlyMemory<byte> buffer);
}

Includes buffer ownership model, header parsing patterns (Unsafe.As<,>() type-punning), and backend comparison matrix.

Original prompt

This section details on the original issue you should resolve

<issue_title>docs: Architecture guide</issue_title>
<issue_description>## Problem

No document explains how the components compose. Contributors need this to understand where new code fits.

Sub-tasks

  • Create docs/ARCHITECTURE.md
  • Diagram: OSI layer mapping to Magma projects (Mermaid or SVG)
  • Explain the delegate-based packet processing chain in Magma.Network
  • Document the IPacketTransmitter / IPacketReceiver contract
  • Describe how platform backends (NetMap, AF_XDP, WinTun) plug into the abstraction

Dependencies

Depends on: nothing
Unlocks: #114 (getting started tutorial), helps all implementation work

Phase

Phase 0 (Foundation) - can start immediately</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: benaadams <1142958+benaadams@users.noreply.github.com>
Copilot AI changed the title [WIP] Add architecture guide documentation docs: Add architecture guide for component composition Feb 11, 2026
Copilot AI requested a review from benaadams February 11, 2026 05:01
@benaadams benaadams marked this pull request as ready for review February 11, 2026 05:01
Copilot AI review requested due to automatic review settings February 11, 2026 05:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new architecture guide to document how Magma’s components compose across OSI layers, how packets flow through the stack, and how platform backends integrate via common abstractions—aimed at helping contributors understand where new code fits.

Changes:

  • Introduces docs/ARCHITECTURE.md describing OSI-layer mapping, packet flow, and backend integration.
  • Documents IPacketTransmitter / IPacketReceiver contracts and delegate-based processing in Magma.Network.
  • Adds diagrams (Mermaid + ASCII) and extension guidelines for adding protocols/backends.

Comment thread docs/ARCHITECTURE.md

**Key Points:**
- **Buffer Ownership**: Caller gets buffer from transmitter, fills it, returns it
- **Async Send**: `SendBuffer()` returns immediately; actual transmission is async

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

This describes SendBuffer() as returning immediately with "actual transmission is async". At least one implementation (WinTunTransitter.SendBuffer) awaits the underlying write and only completes when the send finishes. Consider rephrasing to the more accurate contract: SendBuffer is asynchronous (returns a Task) and completion semantics depend on the backend implementation.

Suggested change
- **Async Send**: `SendBuffer()` returns immediately; actual transmission is async
- **Async Send Contract**: `SendBuffer()` is asynchronous (returns a `Task`); whether the task completes after queuing or after the underlying write/transmission finishes depends on the backend implementation

Copilot uses AI. Check for mistakes.
Comment thread docs/ARCHITECTURE.md

1. **Bounded Access**: All buffer accesses are bounds-checked via `Span<T>`
2. **Struct Layout**: `StructLayout` attributes guarantee correct alignment
3. **No Pointer Arithmetic**: Use `Span<T>` methods instead of raw pointers

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

The "Unsafe Code Safety" rules claim "No Pointer Arithmetic", but the repo does use pointer arithmetic in places (e.g., WinTun memory pool uses byte* offsetting). Consider tightening this to what is actually enforced: minimize pointer arithmetic and confine it to well-audited buffer/memory-manager code, while preferring Span<T> operations for parsing/processing.

Suggested change
3. **No Pointer Arithmetic**: Use `Span<T>` methods instead of raw pointers
3. **Minimize Pointer Arithmetic**: Confine raw pointer arithmetic to well-audited buffer/memory-manager code; prefer `Span<T>` operations for parsing and processing

Copilot uses AI. Check for mistakes.
Comment thread docs/ARCHITECTURE.md

#### Magma.Transport.Tcp (Transport Layer)
Full TCP implementation:
- **`TcpHeader` struct**: 20-byte base TCP header

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

This section references a TcpHeader struct as the 20-byte base TCP header, but the codebase's base TCP header type is Magma.Network.Header.Tcp (with TcpHeaderWithOptions layering options on top). Updating the type name here will make it easier for contributors to locate the correct implementation.

Suggested change
- **`TcpHeader` struct**: 20-byte base TCP header
- **`Magma.Network.Header.Tcp` struct**: 20-byte base TCP header

Copilot uses AI. Check for mistakes.
Comment thread docs/ARCHITECTURE.md
Comment on lines +399 to +402
// Packet consumed, dispose and return null
input.Dispose();
return default;
}

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

The example implementation comment and return value here say "return null" to indicate a consumed packet, but TryConsume<T> returns T where T : IMemoryOwner<byte> and callers check default/empty rather than null. Please change this example to return default (and optionally explain how "empty" is detected for struct wrappers).

Copilot uses AI. Check for mistakes.
Comment thread docs/ARCHITECTURE.md
Comment on lines +411 to +415
**Contract Rules:**
- `TryConsume()` returns `null` if packet was consumed
- `TryConsume()` returns `input` if packet should be passed to OS
- Caller must dispose `input` if it returns non-null
- `FlushPendingAcks()` is called periodically to send deferred transmissions

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

IPacketReceiver.TryConsume<T> does not consistently use null to signal consumption. The interface returns T where T : IMemoryOwner<byte> (not constrained to reference types), and current callers check for default/empty (e.g., NetMap checks .IsEmpty, WinTun checks == default). Please update the contract description and rules here to describe default(T) (or an empty wrapper) as the consumed signal, and clarify the ownership/return responsibility for the non-consumed case accordingly.

Copilot uses AI. Check for mistakes.
Comment thread docs/ARCHITECTURE.md
2. **Parse IP** → Dispatch based on protocol number
3. **Parse Transport** → Deliver to application

If any layer returns `false`, the packet is passed to the host OS network stack.

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

The statement that "If any layer returns false, the packet is passed to the host OS network stack" is not accurate for malformed frames. In Magma.Network.PacketReceiver.TryConsume, an Ethernet parse failure sets result = true (consume/drop) rather than passing through. Consider rewording to distinguish: delegates returning false => passthrough, but invalid/unparseable frames are consumed/dropped.

Suggested change
If any layer returns `false`, the packet is passed to the host OS network stack.
If parsing succeeds and a delegate at any layer returns `false`, the packet is passed to the host OS network stack; if a frame cannot be parsed (for example, an invalid Ethernet header), `PacketReceiver` consumes/drops it instead of passing it through.

Copilot uses AI. Check for mistakes.
Comment thread docs/ARCHITECTURE.md
Comment on lines +545 to +559
│ │ WinTun Session │ │
│ │ │ │
│ │ Receive Thread ───calls──→ IPacketReceiver │ │
│ │ │ │ │
│ │ │ (polls WintunReceivePacket) │ │
│ │ │ │ │
│ │ WinTunTransmitter ──implements──→ IPacketTransmitter │ │
│ │ │ │ │
│ │ │ (calls WintunAllocateSendPacket/SendPacket) │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ WinTun Driver (kernel mode) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Virtual Network Adapter │ │

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

The WinTun backend diagram text references APIs like WintunReceivePacket / WintunAllocateSendPacket, but the current Windows backend code opens a *.tap device via CreateFile and uses a FileStream to read/write. Please update this section to match the actual implementation (TAP device + async file I/O) or rename the backend if it is meant to target the WireGuard Wintun API.

Suggested change
│ │ WinTun Session │ │
│ │ │ │
│ │ Receive Thread ───calls──→ IPacketReceiver │ │
│ │ │ │ │
│ │ │ (polls WintunReceivePacket) │ │
│ │ │ │ │
│ │ WinTunTransmitter ──implements──→ IPacketTransmitter │ │
│ │ │ │ │
│ │ │ (calls WintunAllocateSendPacket/SendPacket) │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
WinTun Driver (kernel mode)
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Virtual Network Adapter │ │
│ │ WinTun/TAP Session │ │
│ │ │ │
│ │ Receive Thread ───calls──→ IPacketReceiver │ │
│ │ │ │ │
│ │ │ (async reads from TAP device FileStream) │ │
│ │ │ │ │
│ │ WinTunTransmitter ──implements──→ IPacketTransmitter │ │
│ │ │ │ │
│ │ │ (async writes to TAP device FileStream) │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
Windows TAP Driver (kernel mode) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ TAP Virtual Network Adapter │ │

Copilot uses AI. Check for mistakes.
Comment thread docs/ARCHITECTURE.md
- **Zero-Copy**: The packet buffer stays in shared memory throughout
- **Type-Punning**: Headers are overlaid on byte spans using `Unsafe.As<,>()`
- **Early Rejection**: Invalid packets are rejected as early as possible
- **Passthrough**: Unhandled packets return non-null from `TryConsume()` to pass to host OS

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

This section says "Unhandled packets return non-null from TryConsume()". In this codebase, consumption is signaled by returning default(T)/empty (not necessarily null), and passthrough is signaled by returning a non-default owner (e.g., NetMap checks .IsEmpty, WinTun checks == default). Please update wording here to avoid implying reference-type null semantics.

Suggested change
- **Passthrough**: Unhandled packets return non-null from `TryConsume()` to pass to host OS
- **Passthrough**: Unhandled packets return a non-default owner from `TryConsume()` (default/empty means consumed; non-default passes the buffer to the host OS)

Copilot uses AI. Check for mistakes.
Comment thread docs/ARCHITECTURE.md
While Magma uses `unsafe` code extensively, it follows strict safety rules:

1. **Bounded Access**: All buffer accesses are bounds-checked via `Span<T>`
2. **Struct Layout**: `StructLayout` attributes guarantee correct alignment

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

StructLayout (especially with Pack = 1) guarantees field layout/padding, but it does not guarantee “correct alignment” (it typically forces 1-byte alignment, which can lead to unaligned reads). Consider rewording this bullet to focus on wire-compat layout rather than alignment guarantees.

Suggested change
2. **Struct Layout**: `StructLayout` attributes guarantee correct alignment
2. **Struct Layout**: `StructLayout` attributes ensure deterministic field layout that matches the wire format (not CPU alignment)

Copilot uses AI. Check for mistakes.
Comment thread docs/ARCHITECTURE.md
Comment on lines +583 to +587
1. **Platform Backend Allocates**: Buffers come from shared memory region (mmap, UMEM)
2. **Wrapped in IMemoryOwner**: Buffer wrapped with ownership tracking
3. **Passed Up Stack**: Each layer parses headers, slices to payload
4. **Single Owner**: Only one component "owns" buffer at a time
5. **Dispose to Release**: Calling `Dispose()` returns buffer to pool

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

The buffer ownership model says platform buffers come from a shared memory region (mmap/UMEM). That’s true for NetMap/AF_XDP, but the Windows backend currently allocates its pool via Marshal.AllocHGlobal and uses TAP file I/O (not a shared UMEM-style region). Consider qualifying this point as backend-dependent to avoid overstating zero-copy/shared-memory guarantees.

Suggested change
1. **Platform Backend Allocates**: Buffers come from shared memory region (mmap, UMEM)
2. **Wrapped in IMemoryOwner**: Buffer wrapped with ownership tracking
3. **Passed Up Stack**: Each layer parses headers, slices to payload
4. **Single Owner**: Only one component "owns" buffer at a time
5. **Dispose to Release**: Calling `Dispose()` returns buffer to pool
1. **Platform Backend Allocates**: AF_XDP/NetMap use a shared memory region (mmap/UMEM); WinTun uses an unmanaged buffer pool (e.g. `Marshal.AllocHGlobal`) with TAP file I/O
2. **Wrapped in IMemoryOwner**: Buffers are exposed to Magma via `IMemoryOwner<byte>` (or equivalent) for ownership tracking
3. **Passed Up Stack**: Each layer parses headers, slices to payload
4. **Single Owner**: Only one component "owns" buffer at a time
5. **Dispose to Release**: Calling `Dispose()` returns buffer to the backend-specific pool

Copilot uses AI. Check for mistakes.
@benaadams

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs: Architecture guide

3 participants