This file provides essential context for LLMs assisting with Logos Messaging development.
Logos Messaging is designed as a shared public network for generalized messaging, not application-specific infrastructure.
This project is a Nim implementation of a libp2p protocol suite for private, censorship-resistant P2P messaging. It targets resource-restricted devices and privacy-preserving communication.
Logos Messaging was formerly known as Waku. Waku-related terminology remains within the codebase for historical reasons.
Key architectural decisions:
Resource-restricted first: Protocols differentiate between full nodes (relay) and light clients (filter, lightpush, store). Light clients can participate without maintaining full message history or relay capabilities. This explains the client/server split in protocol implementations.
Privacy through unlinkability: RLN (Rate Limiting Nullifier) provides DoS protection while preserving sender anonymity. Messages are routed through pubsub topics with automatic sharding across 8 shards. Code prioritizes metadata privacy alongside content encryption.
Scalability via sharding: The network uses automatic content-topic-based sharding to distribute traffic. This is why you'll see sharding logic throughout the codebase and why pubsub topic selection is protocol-level, not application-level.
See documentation for architectural details.
- Relay: Pub/sub message routing using GossipSub
- Store: Historical message retrieval and persistence
- Filter: Lightweight message filtering for resource-restricted clients
- Lightpush: Lightweight message publishing for clients
- Peer Exchange: Peer discovery mechanism
- RLN Relay: Rate limiting nullifier for spam protection
- Metadata: Cluster and shard metadata exchange between peers
- Mix: Mixnet protocol for enhanced privacy through onion routing
- Rendezvous: Alternative peer discovery mechanism
- ENR (Ethereum Node Record): Node identity and capability advertisement
- Multiaddr: libp2p addressing format (e.g.,
/ip4/127.0.0.1/tcp/60000/p2p/16Uiu2...) - PubsubTopic: Gossipsub topic for message routing (e.g.,
/waku/2/default-waku/proto) - ContentTopic: Application-level message categorization (e.g.,
/my-app/1/chat/proto) - Sharding: Partitioning network traffic across topics (static or auto-sharding)
- RLN (Rate Limiting Nullifier): Zero-knowledge proof system for spam prevention
All specs are at rfc.vac.dev/waku. RFCs use WAKU2-XXX format (not legacy WAKU-XXX).
Each protocol typically follows this structure:
waku_<protocol>/
├── protocol.nim # Main protocol type and handler logic
├── client.nim # Client-side API
├── rpc.nim # RPC message types
├── rpc_codec.nim # Protobuf encoding/decoding
├── common.nim # Shared types and constants
└── protocol_metrics.nim # Prometheus metrics
- WakuNode (
waku/node/waku_node.nim) is the central orchestrator - Protocols are "mounted" onto the node's switch (libp2p component)
- PeerManager handles peer selection and connection management
- Switch provides libp2p transport, security, and multiplexing
Example protocol type definition:
type WakuFilter* = ref object of LPProtocol
subscriptions*: FilterSubscriptions
peerManager: PeerManager
messageCache: TimedCache[string]- Nim 2.x (check
waku.nimblefor minimum version) - Rust toolchain (required for RLN dependencies)
- Build system: Make with nimbus-build-system
The project uses Makefile with nimbus-build-system (Status's Nim build framework):
# Initial build (updates submodules)
make wakunode2
# After git pull, update submodules
make update
# Build with custom flags
make wakunode2 NIMFLAGS="-d:chronicles_log_level=DEBUG"Note: The build system uses --mm:refc memory management (automatically enforced). Only relevant if compiling outside the standard build system.
make wakunode2 # Build main node binary
make test # Run all tests
make testcommon # Run common tests only
make libwakuStatic # Build static C library
make chat2 # Build chat example
make install-nph # Install git hook for auto-formatting# Run all tests
make test
# Run specific test file
make test tests/test_waku_enr.nim
# Run specific test case from file
make test tests/test_waku_enr.nim "check capabilities support"
# Build and run test separately (for development iteration)
make test tests/test_waku_enr.nimTest structure uses testutils/unittests:
import testutils/unittests
suite "Waku ENR - Capabilities":
test "check capabilities support":
## Given
let bitfield: CapabilitiesBitfield = 0b0000_1101u8
## Then
check:
bitfield.supportsCapability(Capabilities.Relay)
not bitfield.supportsCapability(Capabilities.Store)Mandatory: All code must be formatted with nph (vendored in vendor/nph)
# Format specific file
make nph/waku/waku_core.nim
# Install git pre-commit hook (auto-formats on commit)
make install-nphThe nph formatter handles all formatting details automatically, especially with the pre-commit hook installed. Focus on semantic correctness.
Uses chronicles library with compile-time configuration:
import chronicles
logScope:
topics = "waku lightpush"
info "handling request", peerId = peerId, topic = pubsubTopic
error "request failed", error = msgCompile with log level:
nim c -d:chronicles_log_level=TRACE myfile.nimCommon pitfalls:
- Always handle Result types explicitly
- Avoid global mutable state: Pass state through parameters
- Keep functions focused: Under 50 lines when possible
- Prefer compile-time checks (
static assert) over runtime checks
- Files/Directories:
snake_case(e.g.,waku_lightpush,peer_manager) - Procedures:
camelCase(e.g.,handleRequest,pushMessage) - Types:
PascalCase(e.g.,WakuFilter,PubsubTopic) - Constants:
PascalCase(e.g.,MaxContentTopicsPerRequest) - Constructors:
func init(T: type Xxx, params): T - For ref types:
func new(T: type Xxx, params): ref T - Exceptions:
XxxErrorfor CatchableError,XxxDefectfor Defect - ref object types:
XxxRefsuffix
Group imports: stdlib, external libs, internal modules:
import
std/[options, sequtils], # stdlib
results, chronicles, chronos, # external
libp2p/peerid
import
../node/peer_manager, # internal (separate import block)
../waku_core,
./commonUses chronos, not stdlib asyncdispatch:
proc handleRequest(
wl: WakuLightPush, peerId: PeerId
): Future[WakuLightPushResult] {.async.} =
let res = await wl.pushHandler(peerId, pubsubTopic, message)
return resThe project uses both Result types and exceptions:
Result types from nim-results are used for protocol and API-level errors:
proc subscribe(
wf: WakuFilter, peerId: PeerID
): Future[FilterSubscribeResult] {.async.} =
if contentTopics.len > MaxContentTopicsPerRequest:
return err(FilterSubscribeError.badRequest("exceeds maximum"))
# Handle Result with isOkOr
(await wf.subscriptions.addSubscription(peerId, criteria)).isOkOr:
return err(FilterSubscribeError.serviceUnavailable(error))
ok()Exceptions still used for:
- chronos async failures (CancelledError, etc.)
- Database/system errors
- Library interop
Most files start with {.push raises: [].} to disable exception tracking, then use try/catch blocks where needed.
{.push raises: [].} # Disable default exception tracking (at file top)
proc myProc(): Result[T, E] {.async.} = # Async procProtocols inherit from libp2p's LPProtocol:
type WakuLightPush* = ref object of LPProtocol
rng*: ref rand.HmacDrbgContext
peerManager*: PeerManager
pushHandler*: PushMessageHandler- Public exports use
*suffix:type WakuFilter* = ... - Fields without
*are module-private
This section summarizes key Nim style guidelines relevant to this project. Full guide: https://status-im.github.io/nim-style-guide/
Import and Export
- Use explicit import paths with std/ prefix for stdlib
- Group imports: stdlib, external, internal (separate blocks)
- Export modules whose types appear in public API
- Avoid include
Macros and Templates
- Avoid macros and templates - prefer simple constructs
- Avoid generating public API with macros
- Put logic in templates, use macros only for glue code
Object Construction
- Prefer Type(field: value) syntax
- Use Type.init(params) convention for constructors
- Default zero-initialization should be valid state
- Avoid using result variable for construction
ref object Types
- Avoid ref object unless needed for:
- Resource handles requiring reference semantics
- Shared ownership
- Reference-based data structures (trees, lists)
- Stable pointer for FFI
- Use explicit ref MyType where possible
- Name ref object types with Ref suffix: XxxRef
Memory Management
- Prefer stack-based and statically sized types in core code
- Use heap allocation in glue layers
- Avoid alloca
- For FFI: use create/dealloc or createShared/deallocShared
Variable Usage
- Use most restrictive of const, let, var (prefer const over let over var)
- Prefer expressions for initialization over var then assignment
- Avoid result variable - use explicit return or expression-based returns
Functions
- Prefer func over proc
- Avoid public (*) symbols not part of intended API
- Prefer openArray over seq for function parameters
Methods (runtime polymorphism)
- Avoid method keyword for dynamic dispatch
- Prefer manual vtable with proc closures for polymorphism
- Methods lack support for generics
Miscellaneous
- Annotate callback proc types with {.raises: [], gcsafe.}
- Avoid explicit {.inline.} pragma
- Avoid converters
- Avoid finalizers
Type Guidelines
Binary Data
- Use byte for binary data
- Use seq[byte] for dynamic arrays
- Convert string to seq[byte] early if stdlib returns binary as string
Integers
- Prefer signed (int, int64) for counting, lengths, indexing
- Use unsigned with explicit size (uint8, uint64) for binary data, bit ops
- Avoid Natural
- Check ranges before converting to int
- Avoid casting pointers to int
- Avoid range types
Strings
- Use string for text
- Use seq[byte] for binary data instead of string
Philosophy
- Prefer Result, Opt for explicit error handling
- Use Exceptions only for legacy code compatibility
Result Types
- Use Result[T, E] for operations that can fail
- Use cstring for simple error messages: Result[T, cstring]
- Use enum for errors needing differentiation: Result[T, SomeErrorEnum]
- Use Opt[T] for simple optional values
- Annotate all modules: {.push raises: [].} at top
Exceptions (when unavoidable)
- Inherit from CatchableError, name XxxError
- Use Defect for panics/logic errors, name XxxDefect
- Annotate functions explicitly: {.raises: [SpecificError].}
- Catch specific error types, avoid catching CatchableError
- Use expression-based try blocks
- Isolate legacy exception code with try/except, convert to Result
Common Defect Sources
- Overflow in signed arithmetic
- Array/seq indexing with []
- Implicit range type conversions
Status Codes
- Avoid status code pattern
- Use Result instead
Standard Library
- Use judiciously, prefer focused packages
- Prefer these replacements:
- async: chronos
- bitops: stew/bitops2
- endians: stew/endians2
- exceptions: results
- io: stew/io2
Results Library
- Use cstring errors for diagnostics without differentiation
- Use enum errors when caller needs to act on specific errors
- Use complex types when additional error context needed
- Use isOkOr pattern for chaining
Wrappers (C/FFI)
- Prefer native Nim when available
- For C libraries: use {.compile.} to build from source
- Create xxx_abi.nim for raw ABI wrapper
- Avoid C++ libraries
Miscellaneous
- Print hex output in lowercase, accept both cases
- Defects lack tracking by {.raises.}
- nil ref causes runtime crashes
- result variable disables branch checking
- Exception hierarchy unclear between Nim versions
- Range types have compiler bugs
- Finalizers infect all instances of type
- Create directory:
waku/waku_myprotocol/ - Define core files:
rpc.nim- Message typesrpc_codec.nim- Protobuf encodingprotocol.nim- Protocol handlerclient.nim- Client APIcommon.nim- Shared types
- Define protocol type in
protocol.nim:type WakuMyProtocol* = ref object of LPProtocol peerManager: PeerManager # ... fields
- Implement request handler
- Mount in WakuNode (
waku/node/waku_node.nim) - Add tests in
tests/waku_myprotocol/ - Export module via
waku/waku_myprotocol.nim
- Define handler in
waku/rest_api/endpoint/myprotocol/ - Implement endpoint following pattern:
proc installMyProtocolApiHandlers*( router: var RestRouter, node: WakuNode ) = router.api(MethodGet, "/waku/v2/myprotocol/endpoint") do () -> RestApiResponse: # Implementation return RestApiResponse.jsonResponse(data, status = Http200)
- Register in
waku/rest_api/handlers.nim
For message_store (SQLite):
- Create
migrations/message_store/NNNNN_description.up.sql - Create corresponding
.down.sqlfor rollback - Increment version number sequentially
- Test migration locally before committing
For PostgreSQL: add in migrations/message_store_postgres/
# Build test binary
make test tests/waku_filter_v2/test_waku_client.nim
# Binary location
./build/tests/waku_filter_v2/test_waku_client.nim.bin
# Or combine
make test tests/waku_filter_v2/test_waku_client.nim "specific test name"Set log level and filter topics:
nim c -r \
-d:chronicles_log_level=TRACE \
-d:chronicles_disabled_topics="eth,dnsdisc" \
tests/mytest.nim- Never edit files directly in vendor - it is auto-generated from git submodules
- Always run
make updateafter pulling changes - Managed by
nimbus-build-system
- Log levels are configured at compile time for performance
- Runtime filtering is available but should be used sparingly:
-d:chronicles_runtime_filtering=on - Default sinks are optimized for production
- Uses
refc(reference counting with cycle collection) - Automatically enforced by the build system (hardcoded in
waku.nimble) - Do not override unless absolutely necessary, as it breaks compatibility
- RLN code requires a Rust toolchain, which explains Rust imports in some modules
- Pre-built
librlnlibraries are checked into the repository
Language: Nim 2.x | License: MIT or Apache 2.0
Makefile- Primary build interfacewaku.nimble- Package definition and build tasks (called via nimbus-build-system)vendor/nimbus-build-system/- Status's build frameworkwaku/node/waku_node.nim- Core node implementationapps/wakunode2/wakunode2.nim- Main CLI applicationwaku/factory/waku_conf.nim- Configuration typeslibrary/libwaku.nim- C bindings entry point
tests/all_tests_waku.nim- All Waku protocol teststests/all_tests_wakunode2.nim- Node application teststests/all_tests_common.nim- Common utilities tests
chronos- Async frameworknim-results- Result type for error handlingchronicles- Logginglibp2p- P2P networkingconfutils- CLI argument parsingpresto- REST servernimcrypto- Cryptographic primitives
Note: For specific version requirements, check waku.nimble.
This project is indexed by GitNexus as logos-delivery (2076 symbols, 2564 relationships, 12 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
If any GitNexus tool warns the index is stale, run
npx gitnexus analyzein terminal first.
- MUST run impact analysis before editing any symbol. Before modifying a function, class, or method, run
gitnexus_impact({target: "symbolName", direction: "upstream"})and report the blast radius (direct callers, affected processes, risk level) to the user. - MUST run
gitnexus_detect_changes()before committing to verify your changes only affect expected symbols and execution flows. - MUST warn the user if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
- When exploring unfamiliar code, use
gitnexus_query({query: "concept"})to find execution flows instead of grepping. It returns process-grouped results ranked by relevance. - When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use
gitnexus_context({name: "symbolName"}).
- NEVER edit a function, class, or method without first running
gitnexus_impacton it. - NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use
gitnexus_renamewhich understands the call graph. - NEVER commit changes without running
gitnexus_detect_changes()to check affected scope.
| Resource | Use for |
|---|---|
gitnexus://repo/logos-delivery/context |
Codebase overview, check index freshness |
gitnexus://repo/logos-delivery/clusters |
All functional areas |
gitnexus://repo/logos-delivery/processes |
All execution flows |
gitnexus://repo/logos-delivery/process/{name} |
Step-by-step execution trace |
| Task | Read this skill file |
|---|---|
| Understand architecture / "How does X work?" | .claude/skills/gitnexus/gitnexus-exploring/SKILL.md |
| Blast radius / "What breaks if I change X?" | .claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md |
| Trace bugs / "Why is X failing?" | .claude/skills/gitnexus/gitnexus-debugging/SKILL.md |
| Rename / extract / split / refactor | .claude/skills/gitnexus/gitnexus-refactoring/SKILL.md |
| Tools, resources, schema reference | .claude/skills/gitnexus/gitnexus-guide/SKILL.md |
| Index, status, clean, wiki CLI commands | .claude/skills/gitnexus/gitnexus-cli/SKILL.md |