Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
/target

# WASM build artifacts (generated by docker-compose)
/www/pkg/

# Backup files
*.old
*.orig
13 changes: 13 additions & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
:8080 {
# Serve static files
root * /app/www
file_server

# Reverse proxy WebSocket to websockify
reverse_proxy /ws/* websockify:8081

# Disable caching for development
header {
Cache-Control "no-cache"
}
}
41 changes: 41 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 18 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,31 @@ version = "0.0.0"
edition = "2021"

[features]
default = ["x25_org"]
default = ["x25_org", "native"]
x25_org = []
native = ["clap", "crossterm"]
wasm = []

[lib]
crate-type = ["cdylib", "rlib"]

[workspace]

[dependencies]
bytes = "1.6.0"
chrono = "0.4.38"
clap = { version = "4.5.4", default-features = false, features = ["std", "derive", "usage", "help", "error-context"] }
crossterm = "0.27.0"
tracing-mutex = "0.3.0"

libxotpad = { path = "libxotpad" }

# Native dependencies
clap = { version = "4.5.4", default-features = false, features = ["std", "derive", "usage", "help", "error-context"], optional = true }
crossterm = { version = "0.27.0", optional = true }

# WASM dependencies
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["WebSocket", "MessageEvent", "BinaryType", "ErrorEvent", "CloseEvent", "console", "Window", "Location"] }
console_error_panic_hook = "0.1"
35 changes: 35 additions & 0 deletions Dockerfile.wasm
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Build WASM module only
FROM rust:1.75-slim

# Install wasm-pack and dependencies
RUN apt-get update && apt-get install -y \
curl \
pkg-config \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*

RUN curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

# Add wasm target
RUN rustup target add wasm32-unknown-unknown

# Set working directory
WORKDIR /build

# Copy source files
COPY Cargo.toml Cargo.lock ./
COPY src/ ./src/
COPY libxotpad/ ./libxotpad/
COPY build-wasm.sh ./

# Build WASM module with x25.org DNS support
RUN chmod +x build-wasm.sh && \
wasm-pack build \
--target web \
--out-dir www/pkg \
--no-typescript \
--features wasm,x25_org \
--no-default-features

# Output will be in /build/www/pkg/
CMD ["sh", "-c", "cp -r /build/www/pkg/* /output/"]
16 changes: 16 additions & 0 deletions Dockerfile.websockify
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.11-slim

# Install websockify
RUN pip3 install --no-cache-dir websockify

# Create app directory
WORKDIR /app

# Copy token plugin and websockify wrapper
COPY token_plugin.py websockify_wrapper.py ./

# Expose websockify port
EXPOSE 8081

# Run via wrapper that adds pong timeout detection
CMD ["python3", "websockify_wrapper.py", "--heartbeat=10", "--token-plugin=token_plugin.TokenPlugin", "0.0.0.0:8081"]
31 changes: 31 additions & 0 deletions build-wasm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash
# Build script for xotpad WASM target

set -e

echo "Building xotpad for WebAssembly..."

# Check if wasm-pack is installed
if ! command -v wasm-pack &> /dev/null; then
echo "Error: wasm-pack is not installed"
echo "Install it with: cargo install wasm-pack"
exit 1
fi

# Build the WASM module with x25.org DNS support
wasm-pack build \
--target web \
--out-dir www/pkg \
--no-typescript \
--features wasm,x25_org \
--no-default-features

echo "✓ WASM build complete!"
echo ""
echo "Output directory: www/pkg/"
echo ""
echo "To test locally, run a web server in the www/ directory:"
echo " cd www && python3 -m http.server 8080"
echo ""
echo "Note: You'll also need a websockify server to relay TCP connections."
echo "See www/WASM_SETUP.md for detailed instructions."
60 changes: 60 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
services:
# Build WASM module (runs automatically before caddy starts)
wasm-builder:
build:
context: .
dockerfile: Dockerfile.wasm
volumes:
- ./www/pkg:/output
command: sh -c "cp -r /build/www/pkg/* /output/"
# Exit after copying files
restart: "no"

# Caddy web server serving static files and proxying WebSocket
caddy:
image: caddy:2-alpine
restart: unless-stopped
container_name: xotpad-caddy
ports:
- "8080:8080"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./www:/app/www:ro
- caddy_data:/data
- caddy_config:/config
environment:
- TZ=UTC
depends_on:
wasm-builder:
condition: service_completed_successfully
websockify:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/"]
interval: 30s
timeout: 3s
retries: 3
start_period: 5s

# Websockify for WebSocket to TCP relay
websockify:
build:
context: .
dockerfile: Dockerfile.websockify
restart: unless-stopped
container_name: xotpad-websockify
environment:
- TZ=UTC
- PYTHONPATH=/app
expose:
- "8081"
healthcheck:
test: ["CMD", "python3", "-c", "import socket; s=socket.socket(); s.connect(('127.0.0.1', 8081)); s.close()"]
interval: 30s
timeout: 3s
retries: 3
start_period: 5s

volumes:
caddy_data:
caddy_config:
10 changes: 10 additions & 0 deletions libxotpad/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@ name = "libxotpad"
version = "0.0.0"
edition = "2021"

[features]
default = []

[dependencies]
bytes = "1.6.0"
either = "1.10.0"
regex = "1.10.4"
tracing-mutex = "0.3.0"

# WASM dependencies
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["WebSocket", "MessageEvent", "BinaryType", "ErrorEvent", "CloseEvent", "Window", "Location"] }
5 changes: 5 additions & 0 deletions libxotpad/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#[cfg(not(target_arch = "wasm32"))]
pub mod pad;
pub mod stream;
pub mod x121;
pub mod x25;
pub mod x29;
pub mod x3;
pub mod xot;

#[cfg(target_arch = "wasm32")]
pub mod wasm_async;
4 changes: 2 additions & 2 deletions libxotpad/src/pad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::x121::X121Addr;
use crate::x25::{Svc, Vc, X25Params};
use crate::x29::{X29CallUserData, X29PadMessage};
use crate::x3::{X3Echo, X3Editing, X3Forward, X3Idle, X3LfInsert, X3ParamError, X3Params};
use crate::xot::XotLink;
use crate::xot::PlatformXotLink;

type SendQueue = (VecDeque<u8>, Option<Instant>);
type IndicateMessage = Vec<(u8, Result<u8, X3ParamError>)>;
Expand Down Expand Up @@ -199,7 +199,7 @@ impl<Q: X3Params + Send + Sync + 'static> Pad<Q> {
}

pub fn call(
link: XotLink,
link: PlatformXotLink,
channel: u16,
addr: &X121Addr,
call_data: &[u8],
Expand Down
Loading