build: add Docker image + GHCR for cross-platform simulation testing

Multi-stage Dockerfile pre-builds all heavy nix dependencies. Runtime
image (~8.5GB) at ghcr.io/adklempner/logos-chat-sim:latest includes
nix store closure + Rust + cargo-risczero/r0vm + system clang.

run_in_docker.sh pulls from GHCR, symlinks pre-built modules, builds
sequencer + run_setup from source (~10 min per run).

Update vendor/logos-lez-rln to a591645 (rebased onto main with
member-expiry merged, single squashed commit).

Add INSTRUCTIONS.md with macOS, Linux (native), and Docker instructions.
This commit is contained in:
Arseniy Klempner 2026-05-08 12:11:14 -06:00
parent 695ecb42bc
commit 9d368aeb44
No known key found for this signature in database
GPG Key ID: 51653F18863BD24B
7 changed files with 348 additions and 92 deletions

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
# Everything — the Dockerfile doesn't COPY from the build context.
# Guest binaries are staged via docker cp separately.
*

170
.github/Dockerfile.sim vendored Normal file
View File

@ -0,0 +1,170 @@
# ===========================================================================
# Stage 1: Builder — build everything, package runtime closure
# ===========================================================================
FROM catthehacker/ubuntu:act-latest AS builder
RUN apt-get update -qq && apt-get install -y -qq \
build-essential pkg-config libssl-dev git curl docker.io xxd \
&& rm -rf /var/lib/apt/lists/*
# Nix
RUN curl -L https://nixos.org/nix/install | bash -s -- --daemon --yes
RUN mkdir -p /root/.config/nix && \
printf 'experimental-features = nix-command flakes\nsandbox = false\n' > /root/.config/nix/nix.conf
RUN rmdir /homeless-shelter 2>/dev/null || true
# Rust + cargo-risczero
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
RUN cargo install cargo-risczero
RUN git config --global url."https://github.com/".insteadOf "git@github.com:"
SHELL ["/bin/bash", "-lc"]
# logos-blockchain-circuits (architecture-aware)
RUN ARCH=$(uname -m | sed 's/arm64/aarch64/') && \
CIRCUITS_URL="https://github.com/logos-blockchain/logos-blockchain-circuits/releases/download/v0.4.2/logos-blockchain-circuits-v0.4.2-linux-${ARCH}.tar.gz" && \
echo "Downloading circuits: $CIRCUITS_URL" && \
mkdir -p /root/.logos-blockchain-circuits && \
curl -sL "$CIRCUITS_URL" | tar xz -C /root/.logos-blockchain-circuits && \
mv /root/.logos-blockchain-circuits/logos-blockchain-circuits-*/* /root/.logos-blockchain-circuits/ 2>/dev/null || true && \
rmdir /root/.logos-blockchain-circuits/logos-blockchain-circuits-* 2>/dev/null || true
# --- LEZ modules ---
RUN source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && \
cd /root && \
git clone -b feat/mix-rln-gifter-sim https://github.com/logos-co/logos-lez-rln.git && \
cd logos-lez-rln && \
git submodule update --init && \
(cd logos-delivery-module && git submodule update --init --recursive)
RUN source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && \
cd /root/logos-lez-rln && \
nix build .#logos-rln-module -o logos-rln-module/result-rln
RUN source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && \
cd /root/logos-lez-rln && \
(cd logos-execution-zone-module && \
RISC0_SKIP_BUILD_KERNELS=1 nix build --impure \
--override-input logos-execution-zone "git+file://$(pwd)/../lssa" \
-o ../logos-rln-module/result-wallet)
RUN source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && \
nix build github:logos-co/logos-liblogos/7df6195 \
--override-input logos-cpp-sdk github:logos-co/logos-cpp-sdk/a4bd66c \
-o /root/logoscore-result
RUN source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && \
cd /root/logos-lez-rln/logos-delivery-module/vendor/logos-delivery && \
make -j1 liblogosdelivery 2>&1 | tail -5
RUN source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && \
cd /root/logos-lez-rln && \
SDK_PATH=$(nix build github:logos-co/logos-cpp-sdk/a4bd66c --no-link --print-out-paths 2>/dev/null) && \
LIBLOGOS_PATH=$(nix build github:logos-co/logos-liblogos/7df6195 \
--override-input logos-cpp-sdk github:logos-co/logos-cpp-sdk/a4bd66c \
--no-link --print-out-paths 2>/dev/null) && \
QT_BASE=$(find /nix/store -maxdepth 1 -name "*qtbase-6*" -type d 2>/dev/null | head -1) && \
QT_RO=$(find /nix/store -maxdepth 1 -name "*qtremoteobjects-6*" -type d 2>/dev/null | head -1) && \
DELIVERY_MOD="$PWD/logos-delivery-module" && \
cd logos-delivery-module && rm -rf build_plugin && mkdir build_plugin && cd build_plugin && \
mkdir -p delivery_root/bin delivery_root/liblogosdelivery && \
cp "$DELIVERY_MOD/vendor/logos-delivery/build/liblogosdelivery."* delivery_root/bin/ 2>/dev/null || true && \
cp "$DELIVERY_MOD/vendor/logos-delivery/liblogosdelivery/liblogosdelivery.h" delivery_root/liblogosdelivery/ && \
nix-shell -p cmake ninja pkg-config postgresql --run " \
cmake .. -GNinja \
-DLOGOS_CPP_SDK_ROOT=$SDK_PATH \
-DLOGOS_LIBLOGOS_ROOT=$LIBLOGOS_PATH \
-DLOGOS_DELIVERY_ROOT=\$PWD/delivery_root \
-DLOGOS_MESSAGING_MODULE_USE_VENDOR=OFF \
-DCMAKE_PREFIX_PATH=\"$QT_BASE;$QT_RO\" \
-DQT_ADDITIONAL_PACKAGES_PREFIX_PATH=$QT_RO && \
ninja" 2>&1 | tail -5
RUN source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && \
CLANG_SO=$(find /nix/store -maxdepth 3 -name 'libclang.so' 2>/dev/null | head -1) && \
export LIBCLANG_PATH=$(dirname "$CLANG_SO") && \
STDBOOL=$(find "$LIBCLANG_PATH" -maxdepth 5 -name 'stdbool.h' 2>/dev/null | head -1) && \
[ -n "$STDBOOL" ] && export BINDGEN_EXTRA_CLANG_ARGS="-I$(dirname $STDBOOL)" ; \
cd /root/logos-lez-rln/lssa && \
cargo build --features standalone -p sequencer_service 2>&1 | tail -5
# Build run_setup binary (deploys LEZ programs to sequencer at sim runtime)
RUN cd /root/logos-lez-rln/lez-rln && \
cargo build --bin run_setup 2>&1 | tail -5
# --- logos-chat (liblogoschat) ---
RUN source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && \
cd /root && \
git clone -b feat/logos-delivery https://github.com/adklempner/logos-chat.git && \
cd logos-chat && \
git submodule update --init --depth 1 && \
(cd vendor/nwaku && git submodule update --init --recursive --depth 1) && \
(cd vendor/nimbus-build-system && git submodule update --init --recursive --depth 1) && \
make update && make liblogoschat 2>&1 | tail -5
# --- logos-chat-module (chat_module_plugin) ---
RUN source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && \
cd /root && \
git clone -b feat/logos-delivery https://github.com/adklempner/logos-chat-module.git && \
cd logos-chat-module && nix build 2>&1 | tail -5
# --- Package runtime closure ---
RUN source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && \
{ \
nix-store -qR /root/logos-lez-rln/logos-rln-module/result-rln; \
nix-store -qR /root/logos-lez-rln/logos-rln-module/result-wallet; \
nix-store -qR /root/logoscore-result; \
nix-store -qR /root/logos-chat-module/result; \
} | sort -u > /tmp/nix-closure-paths.txt && \
tar cf /tmp/nix-closure.tar -T /tmp/nix-closure-paths.txt && \
echo "Closure: $(wc -l < /tmp/nix-closure-paths.txt) paths, $(du -sh /tmp/nix-closure.tar | cut -f1)"
# ===========================================================================
# Stage 2: Runtime — minimal image, no nix/Rust/cargo needed
# ===========================================================================
FROM catthehacker/ubuntu:act-latest
RUN apt-get update -qq && apt-get install -y -qq \
build-essential pkg-config libssl-dev git curl xxd netcat-openbsd lsof \
clang libclang-dev \
&& rm -rf /var/lib/apt/lists/*
# Rust + r0vm (needed for: sequencer build + risc0 dev prover)
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
RUN cargo install cargo-risczero
RUN git config --global url."https://github.com/".insteadOf "git@github.com:"
# Import runtime nix closure from builder
COPY --from=builder /tmp/nix-closure.tar /tmp/
# Import nix store runtime closure from builder. These are loaded by
# direct path (not via nix), so no nix DB registration needed.
# The runtime nix has its own clean DB for nix-shell package downloads.
RUN mkdir -p /nix/store && tar xf /tmp/nix-closure.tar -C / && rm /tmp/nix-closure.tar
# Pre-built LEZ module outputs
COPY --from=builder /root/logos-lez-rln/logos-rln-module/result-rln /root/lez-modules/result-rln
COPY --from=builder /root/logos-lez-rln/logos-rln-module/result-wallet /root/lez-modules/result-wallet
COPY --from=builder /root/logos-lez-rln/logos-delivery-module/build_plugin/modules/ /root/lez-modules/delivery-plugin/
COPY --from=builder /root/logos-lez-rln/logos-delivery-module/vendor/logos-delivery/build/ /root/lez-modules/delivery-build/
# NOTE: sequencer + run_setup are NOT pre-built. Binaries compiled in the
# builder stage crash at runtime due to library mismatch between Docker stages.
# They're built from source at runtime with system clang (~1.5 min).
# The lssa source is cloned at runtime via setup_and_run.sh submodule init.
COPY --from=builder /root/logoscore-result /root/lez-modules/logoscore-result
COPY --from=builder /root/.logos-blockchain-circuits /root/.logos-blockchain-circuits
COPY --from=builder /root/logos-lez-rln/dev /root/lez-modules/dev
# Pre-built logos-chat outputs
COPY --from=builder /root/logos-chat/build/liblogoschat.so /root/lez-modules/liblogoschat.so
COPY --from=builder /root/logos-chat-module/result /root/lez-modules/chat-module-result

View File

@ -1,39 +1,38 @@
#!/usr/bin/env bash
# Run the mix+LEZ chat simulation inside a Docker container (Linux aarch64).
# Run the mix+LEZ chat simulation inside a Docker container (Linux).
#
# The Docker image pre-builds EVERYTHING: LEZ modules, sequencer, logoscore,
# liblogoschat, chat_module_plugin. Each sim run only clones the repo (for
# scripts + configs), symlinks pre-built artifacts, and runs the simulation.
# The Docker image pre-builds all heavy nix modules. Each sim run clones
# the repo, builds sequencer + run_setup from source (nix-shell), symlinks
# pre-built artifacts, and runs the simulation.
#
# First run: ~60 min (one-time docker build)
# Subsequent runs: ~5 min (clone + submodule init + sim)
# Subsequent runs: ~10 min (clone + sequencer build + sim)
#
# Prerequisites: Docker Desktop running.
#
# Usage:
# bash scripts/run_in_docker.sh
# BRANCH=my-branch bash scripts/run_in_docker.sh # test a different branch
#
# Environment variables:
# BRANCH — git branch to clone (default: feat/logos-delivery)
# REPO_URL — git repo URL
# GUEST_BINARIES_DIR — path to pre-built guest .bin files (auto-detected)
# REBUILD_IMAGE — set to 1 to force image rebuild
# SIM_* — simulation parameters, passed through to run_simulation.sh
# BRANCH=my-branch bash scripts/run_in_docker.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
DOCKERFILE="$ROOT/.github/Dockerfile.sim"
IMAGE_NAME="logos-chat-sim"
IMAGE_NAME="${DOCKER_IMAGE:-ghcr.io/adklempner/logos-chat-sim:latest}"
CONTAINER_NAME="logos-chat-sim-run"
BRANCH="${BRANCH:-feat/logos-delivery}"
REPO_URL="${REPO_URL:-https://github.com/adklempner/logos-chat.git}"
# Build image if it doesn't exist or REBUILD_IMAGE=1
if [ "${REBUILD_IMAGE:-0}" = "1" ] || ! docker image inspect "$IMAGE_NAME" >/dev/null 2>&1; then
echo "=== Building Docker image (one-time — ~60 min) ==="
if [ "${REBUILD_IMAGE:-0}" = "1" ]; then
echo "=== Building Docker image locally (~60 min) ==="
docker build -t "$IMAGE_NAME" -f "$DOCKERFILE" "$ROOT"
elif ! docker image inspect "$IMAGE_NAME" >/dev/null 2>&1; then
echo "=== Pulling Docker image from GHCR ==="
docker pull "$IMAGE_NAME" || {
echo "Pull failed, building locally..."
docker build -t "$IMAGE_NAME" -f "$DOCKERFILE" "$ROOT"
}
else
echo "=== Docker image cached ==="
fi
@ -42,7 +41,7 @@ docker rm -f "$CONTAINER_NAME" 2>/dev/null || true
trap 'echo "=== Rescuing logs ==="; docker cp "$CONTAINER_NAME:/root/logos-chat/simulations/mix_lez_chat/.sim_state" ./docker-sim-logs 2>/dev/null || true; docker rm -f "$CONTAINER_NAME" 2>/dev/null || true' EXIT
echo "=== Starting container ==="
docker run --rm -d --name "$CONTAINER_NAME" "$IMAGE_NAME" tail -f /dev/null
docker run -d --name "$CONTAINER_NAME" "$IMAGE_NAME" tail -f /dev/null
# Stage guest binaries
GUEST_REL="vendor/logos-lez-rln/lez-rln/methods/guest/target/riscv32im-risc0-zkvm-elf/docker"
@ -64,58 +63,61 @@ fi
# Collect SIM_* env vars
SIM_ENVS=""
for var in $(env | grep '^SIM_' | cut -d= -f1); do
SIM_ENVS="$SIM_ENVS export $var='${!var}';"
SIM_ENVS="${SIM_ENVS}export $var='${!var}'; "
done
echo "=== Running simulation ==="
docker exec "$CONTAINER_NAME" bash -c "
$SIM_ENVS
# Write the run script to the container (avoids heredoc escaping issues)
docker exec -i "$CONTAINER_NAME" bash -c "cat > /root/run-sim.sh && chmod +x /root/run-sim.sh" << 'SIMSCRIPT'
#!/bin/bash
set -euo pipefail
export RISC0_DEV_MODE=1
# Clone repo (just for scripts + configs, not for building)
# Clone repo
cd /root
rm -rf logos-chat
git clone --depth 1 -b $BRANCH $REPO_URL
git clone --depth 1 -b "$BRANCH" "$REPO_URL"
cd logos-chat
# Only init top-level submodules + logos-lez-rln selectively (for sim script paths)
# Init submodules (lssa needs full history for auto-sync)
git submodule update --init --depth 1
(cd vendor/logos-lez-rln && git submodule update --init --depth 1 lssa logos-delivery logos-delivery-module logos-execution-zone-module)
(cd vendor/logos-lez-rln && git submodule update --init lssa && \
git submodule update --init --depth 1 logos-delivery logos-delivery-module logos-execution-zone-module)
(cd vendor/logos-lez-rln/logos-delivery-module && git submodule update --init --depth 1 vendor/logos-delivery)
# Symlink ALL pre-built artifacts from the Docker image
# Symlink pre-built nix modules from image
LEZ_DIR=vendor/logos-lez-rln
ln -sf /root/lez-modules/result-rln \$LEZ_DIR/logos-rln-module/result-rln
ln -sf /root/lez-modules/result-wallet \$LEZ_DIR/logos-rln-module/result-wallet
mkdir -p \$LEZ_DIR/logos-delivery-module/build_plugin
cp -r /root/lez-modules/delivery-plugin \$LEZ_DIR/logos-delivery-module/build_plugin/modules
mkdir -p \$LEZ_DIR/logos-delivery-module/vendor/logos-delivery/build
cp /root/lez-modules/delivery-build/* \$LEZ_DIR/logos-delivery-module/vendor/logos-delivery/build/ 2>/dev/null || true
# Sequencer is built from source at runtime (pre-built crashes in risc0 dev prover).
# Reuse cargo target cache from image to speed up (~30s vs ~2min).
ln -sf /root/lez-modules/lssa-target-cache \$LEZ_DIR/lssa/target
mkdir -p \$LEZ_DIR/lez-rln/target/debug
cp /root/lez-modules/run_setup \$LEZ_DIR/lez-rln/target/debug/
ln -sf /root/lez-modules/result-rln $LEZ_DIR/logos-rln-module/result-rln
ln -sf /root/lez-modules/result-wallet $LEZ_DIR/logos-rln-module/result-wallet
mkdir -p $LEZ_DIR/logos-delivery-module/build_plugin
cp -r /root/lez-modules/delivery-plugin $LEZ_DIR/logos-delivery-module/build_plugin/modules
mkdir -p $LEZ_DIR/logos-delivery-module/vendor/logos-delivery/build
cp /root/lez-modules/delivery-build/* $LEZ_DIR/logos-delivery-module/vendor/logos-delivery/build/ 2>/dev/null || true
mkdir -p build
cp /root/lez-modules/liblogoschat.so build/
# Restore guest binaries
GUEST_DIR=\"\$LEZ_DIR/lez-rln/methods/guest/target/riscv32im-risc0-zkvm-elf/docker\"
GUEST_DIR="$LEZ_DIR/lez-rln/methods/guest/target/riscv32im-risc0-zkvm-elf/docker"
if [ -f /tmp/guest-bins/rln_registration.bin ]; then
mkdir -p \"\$GUEST_DIR\"
cp /tmp/guest-bins/*.bin \"\$GUEST_DIR/\"
mkdir -p "$GUEST_DIR"
cp /tmp/guest-bins/*.bin "$GUEST_DIR/"
fi
# Chat module — symlink from pre-built in image
ln -sf /root/lez-modules/chat-module-result /root/logos-chat-module/result 2>/dev/null || \
(mkdir -p /root/logos-chat-module && ln -sf /root/lez-modules/chat-module-result /root/logos-chat-module/result)
# Chat module
mkdir -p /root/logos-chat-module
ln -sf /root/lez-modules/chat-module-result /root/logos-chat-module/result
# Set env vars for sim
export LOGOSCORE=\"/root/lez-modules/logoscore-result/bin/logoscore\"
CLANG_SO=\$(find /nix/store -maxdepth 3 -name 'libclang.so' 2>/dev/null | head -1 || true)
[ -n \"\$CLANG_SO\" ] && export LIBCLANG_PATH=\$(dirname \"\$CLANG_SO\")
# Build sequencer + run_setup from source (system clang, r0vm in PATH)
echo "Building sequencer + run_setup..."
export LIBCLANG_PATH=/usr/lib/llvm-18/lib
(cd $LEZ_DIR/lssa && cargo build --features standalone -p sequencer_service 2>&1 | tail -3)
(cd $LEZ_DIR/lez-rln && cargo build --bin run_setup 2>&1 | tail -3)
export LOGOSCORE="/root/lez-modules/logoscore-result/bin/logoscore"
bash simulations/mix_lez_chat/run_simulation.sh --fresh
"
SIMSCRIPT
echo "=== Running simulation ==="
docker exec -e BRANCH="$BRANCH" -e REPO_URL="$REPO_URL" $SIM_ENVS "$CONTAINER_NAME" bash /root/run-sim.sh
echo "=== Done ==="
# Container cleanup handled by EXIT trap (which also rescues logs on failure)

View File

@ -0,0 +1,72 @@
# Running the Mix + LEZ RLN Chat Simulation
End-to-end private chat between two logos-chat-module clients over a 4-node mix network with LEZ-backed RLN spam protection.
Two logoscore instances (sender + receiver) establish an X3DH key agreement via an out-of-band intro bundle, then exchange double-ratchet-encrypted messages routed through 3-hop Sphinx onion routes with per-hop RLN proof generation and verification. Node 0 mounts the rln_gifter service; nodes 1-3 and both chat clients register RLN memberships on-chain via the gifter protocol. The sender publishes via `lightpushPublish(mixify=true)`, the mix exit node verifies the RLN proof before fanning out via gossipsub relay, and the receiver consumes the message via a Waku filter subscription.
## macOS
**Prereqs:** nix (with flakes), Docker, cargo-risczero, SSH access to GitHub.
```bash
git clone -b feat/logos-delivery git@github.com:adklempner/logos-chat.git
cd logos-chat && bash simulations/mix_lez_chat/setup_and_run.sh
```
First run: ~15-25 min. Re-runs: `bash simulations/mix_lez_chat/run_simulation.sh --fresh` (~5 min).
## Linux (native)
**Prereqs:** nix (with flakes), Docker, cargo-risczero, SSH access to GitHub.
```bash
git clone -b feat/logos-delivery git@github.com:adklempner/logos-chat.git
cd logos-chat && bash simulations/mix_lez_chat/setup_and_run.sh
```
Same as macOS. On x86_64 Linux this should work out of the box. On aarch64 Linux, guest zkVM binaries must be pre-built on another platform (rzup doesn't support aarch64-linux) and the wallet module nix build needs `RISC0_SKIP_BUILD_KERNELS=1`.
## Linux (via Docker)
**Prereqs:** Docker.
```bash
git clone -b feat/logos-delivery git@github.com:adklempner/logos-chat.git
cd logos-chat && bash scripts/run_in_docker.sh
```
The pre-built image (`ghcr.io/adklempner/logos-chat-sim`) is pulled automatically (~8.5GB download). Guest zkVM binaries must exist on the host from a previous macOS/x86_64 build, or set `GUEST_BINARIES_DIR`.
Each sim run: ~10 min (clone + sequencer build + sim). To force a local image rebuild: `REBUILD_IMAGE=1 bash scripts/run_in_docker.sh`.
## Pass criteria
**ALL 15 CHECKS PASSED** — 4 mix nodes mounted, gifter service, LEZ RLN active, sender+receiver initialized/started/mix-mounted, intro bundle created, messages sent and received.
## Configuration
```bash
SIM_LOG_LEVEL=TRACE SIM_KADEMLIA_MIN_WAIT=10 bash simulations/mix_lez_chat/run_simulation.sh --fresh
```
Full list of `SIM_*` variables in `simulations/mix_lez_chat/README.md`.
## If it fails
Re-run with fresh state:
```bash
bash simulations/mix_lez_chat/run_simulation.sh --fresh
```
Wallet/sequencer errors:
```bash
rm -f vendor/logos-lez-rln/dev/wallet_config.json vendor/logos-lez-rln/dev/storage.json
```
Guest binary errors after updating submodules:
```bash
rm -rf vendor/logos-lez-rln/lez-rln/methods/guest/target
bash simulations/mix_lez_chat/setup_and_run.sh
```
Docker logs are rescued to `./docker-sim-logs/` on failure.

View File

@ -1,8 +1,47 @@
# Mix + LEZ RLN Chat Simulation
End-to-end private chat between two `logos-chat-module` clients over a 4-node mix network with LEZ-backed RLN spam protection against a local LEZ sequencer.
End-to-end private chat between two logos-chat-module clients over a 4-node mix network with LEZ-backed RLN spam protection.
Two logoscore instances (sender + receiver) load `chat_module` and establish an X3DH key agreement via an out-of-band intro bundle, then exchange double-ratchet-encrypted messages routed through 3-hop Sphinx onion routes with per-hop RLN proof generation and verification. Node 0 mounts the `rln_gifter` service; nodes 1-3 and both chat clients register RLN memberships on-chain via the gifter protocol. The chat clients run `mix: true, relay: false, filter: true` — the sender publishes via `lightpushPublish(mixify=true)`, the mix exit node verifies the RLN proof before fanning out via gossipsub relay to the shard, and the receiver consumes the message via a Waku filter subscription on one of the mix nodes.
Two logoscore instances (sender + receiver) establish an X3DH key agreement via an out-of-band intro bundle, then exchange double-ratchet-encrypted messages routed through 3-hop Sphinx onion routes with per-hop RLN proof generation and verification. Node 0 mounts the rln_gifter service; nodes 1-3 and both chat clients register RLN memberships on-chain via the gifter protocol. The sender publishes via `lightpushPublish(mixify=true)`, the mix exit node verifies the RLN proof before fanning out via gossipsub relay, and the receiver consumes the message via a Waku filter subscription.
## macOS
**Prereqs:** nix (with flakes), Docker, cargo-risczero, SSH access to GitHub.
```bash
git clone -b feat/logos-delivery git@github.com:adklempner/logos-chat.git
cd logos-chat && bash simulations/mix_lez_chat/setup_and_run.sh
```
First run: ~15-25 min. Re-runs: `bash simulations/mix_lez_chat/run_simulation.sh --fresh` (~5 min).
## Linux (native)
**Prereqs:** nix (with flakes), Docker, cargo-risczero, SSH access to GitHub.
```bash
git clone -b feat/logos-delivery git@github.com:adklempner/logos-chat.git
cd logos-chat && bash simulations/mix_lez_chat/setup_and_run.sh
```
Same as macOS. On x86_64 Linux this should work out of the box. On aarch64 Linux, guest zkVM binaries must be pre-built on another platform (rzup doesn't support aarch64-linux) and the wallet module nix build needs `RISC0_SKIP_BUILD_KERNELS=1`.
## Linux (via Docker)
**Prereqs:** Docker with 24GB RAM allocated.
```bash
git clone -b feat/logos-delivery git@github.com:adklempner/logos-chat.git
cd logos-chat && bash scripts/run_in_docker.sh
```
The pre-built image (`ghcr.io/adklempner/logos-chat-sim`) is pulled automatically (~8.5GB download). Guest zkVM binaries must exist on the host from a previous macOS/x86_64 build, or set `GUEST_BINARIES_DIR`.
Each sim run: ~10 min (clone + sequencer build + sim). To force a local image rebuild: `REBUILD_IMAGE=1 bash scripts/run_in_docker.sh`.
## Pass criteria
**ALL 15 CHECKS PASSED** — 4 mix nodes mounted, gifter service, LEZ RLN active, sender+receiver initialized/started/mix-mounted, intro bundle created, messages sent and received.
## Architecture
@ -18,28 +57,9 @@ logoscore (per mix node) logoscore (per chat client)
Node 0 runs the RLN gifter service. Nodes 1-3 register via gifter on startup. Chat clients also register via gifter when `startChat()` runs.
## Quick start
**Prereqs:** nix (with flakes), Docker (for guest zkVM binaries), cargo-risczero, SSH access to GitHub.
**Run from scratch:**
```bash
git clone -b feat/logos-delivery git@github.com:adklempner/logos-chat.git
cd logos-chat && bash simulations/mix_lez_chat/setup_and_run.sh
```
**Re-run (after initial build):**
```bash
bash simulations/mix_lez_chat/run_simulation.sh --fresh
```
Pass = **ALL 15 CHECKS PASSED**.
## Configuration
Override via environment variables:
Override defaults via environment:
| Variable | Default | Description |
|---|---|---|
@ -73,22 +93,13 @@ When `--fresh` is passed:
Without `--fresh`, reuses existing sequencer if port 3040 is already bound.
## Checks (15 total)
| Category | Check | What it verifies |
|---|---|---|
| Mix nodes (4) | Node N mounted mix | Mix protocol handler registered |
| RLN gifter | Node 0 gifter service mounted | `/logos/rln-gifter/1.0.0` protocol handler |
| LEZ RLN | LEZ root polling active | Nodes polling valid Merkle roots from LEZ |
| Chat module (4) | Receiver/Sender initialized | `chatInitResult` event fired |
| | Receiver/Sender started | `Waku client started` + `chatStartResult` |
| | Receiver/Sender mounted mix+LEZ | Mix protocol + LEZ callbacks wired |
| | Receiver created intro bundle | X3DH pre-key bundle generated |
| Message exchange (2) | Sender sent message | `chatNewPrivateConversationResult` |
| | Receiver received message | `chatNewMessage` via filter subscription |
## Troubleshooting
**Re-run with fresh state:**
```bash
bash simulations/mix_lez_chat/run_simulation.sh --fresh
```
**"Sequencer failed to start"** — port 3040 already in use:
```bash
kill $(lsof -ti tcp:3040) && bash simulations/mix_lez_chat/run_simulation.sh --fresh
@ -107,10 +118,7 @@ rm -f /tmp/logos_*
bash simulations/mix_lez_chat/run_simulation.sh --fresh
```
**"FAIL: Receiver received message (0)"** — timing issue, try re-running:
```bash
bash simulations/mix_lez_chat/run_simulation.sh --fresh
```
Docker logs are rescued to `./docker-sim-logs/` on failure.
## Adapting for other LEZ programs
@ -131,7 +139,7 @@ The chat_module sender/receiver instances (phase 5 of run_simulation.sh). Your m
- Methods exposed via `LOGOS_METHOD` for logoscore `-c` invocation
2. **A shared library** with your program logic (like `liblogoschat.so`)
3. **RLN integration** — wire `setRlnConfig` to pass RLN credentials from the C++ plugin to your library
4. **EVENT: stderr fallback** — on Linux, Qt signal forwarding from plugin to logoscore doesn't work across the FFI thread boundary. Write event data to stderr in `EVENT:name:data` format for cross-platform reliability.
4. **EVENT: stderr fallback** — on Linux, Qt signal forwarding from plugin to logoscore doesn't work across the FFI thread boundary. Write event data to stderr in `EVENT:name:data` format (gated by `LOGOS_EVENT_STDERR` env var) for cross-platform reliability.
### How to stage your module
@ -160,3 +168,4 @@ All logs in `simulations/mix_lez_chat/.sim_state/`:
- `node0.log` `node3.log` — mix relay nodes
- `chat_receiver.log` — receiver chat module
- `chat_sender.log` — sender chat module
- `sequencer.log` — LEZ sequencer

View File

@ -137,7 +137,7 @@ else
else
# Sequencer guest binaries must match the lssa rev that lez-rln's host-side
# client pins. Mismatch → DeserializeUnexpectedEnd (wire format divergence).
LSSA_REV=$(grep -oE 'rev\s*=\s*"[0-9a-f]+"' "$LEZ_RLN_DIR/lez-rln/Cargo.toml" | head -1 | sed 's/.*"\([0-9a-f]*\)"/\1/')
LSSA_REV=$(grep -oE '(rev|tag)\s*=\s*"[^"]+"' "$LEZ_RLN_DIR/lez-rln/Cargo.toml" | head -1 | sed 's/.*"\([^"]*\)"/\1/')
[ -z "$LSSA_REV" ] && die "Could not extract lssa rev from lez-rln/Cargo.toml"
if ! git -C "$LEZ_RLN_DIR/lssa" merge-base --is-ancestor "$LSSA_REV" HEAD 2>/dev/null; then
log " Pinning lssa to $LSSA_REV..."

@ -1 +1 @@
Subproject commit ad17d9026b5bc69c6c42d7617fb08b18ae135f42
Subproject commit f3f1aa6c609fae776712e04ccf0aa5a64f004391