diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..c53c024a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,66 @@ +# Git files +.git +.gitignore +.gitattributes + +# GitHub workflows and documentation +.github +docs/book +docs/TODO.md + +# Target and build artifacts +target/** +!target/release/wassette +bin +*.wasm + +# Examples and tests +examples/*/target +tests + +# Documentation +*.md +!README.md +LICENSE +NOTICE +SECURITY.md +CODE_OF_CONDUCT.md +CONTRIBUTING.md + +# Development files +.vscode +.idea +*.swp +*.swo +*~ + +# Rust artifacts +**/*.rs.bk +Cargo.lock.bak + +# OS files +.DS_Store +Thumbs.db + +# CI/Docker files +Dockerfile.ci +docker-compose.yml + +# Scripts +scripts + +# Package manager files +Formula +flake.nix +flake.lock +winget + +# Other +assets +audit.toml +deny.toml +_typos.toml +rustfmt.toml +component-registry.json +policy.yaml +install.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c0c47af..107b9851 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Added +- Comprehensive Docker documentation and Dockerfile for running Wassette in containers with enhanced security isolation, including examples for mounting components, secrets, configuration files, and production deployment patterns with Docker Compose - `rust-toolchain.toml` file specifying Rust 1.90 as the stable toolchain version, ensuring consistent Rust version across development environments and CI/CD pipelines - AI agent development guides (`AGENTS.md` and `Claude.md`) that consolidate development guidelines from `.github/instructions/` into accessible documentation for AI agents working on the project - Comprehensive installation guide page consolidating all installation methods (one-liner script, Homebrew, Nix, WinGet) organized by platform (Linux, macOS, Windows) with verification steps and troubleshooting sections diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..d2aa6d0f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,58 @@ +# Wassette Docker Image +# This Dockerfile provides a containerized runtime for Wassette with additional security isolation + +# Stage 1: Build the Wassette binary +FROM rust:1.90-bookworm AS builder + +# Install ca-certificates for HTTPS support during build +RUN apt-get update && \ + apt-get install -y --no-install-recommends ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /build + +# Copy the project files +COPY Cargo.toml Cargo.lock ./ +COPY src ./src +COPY crates ./crates +COPY build.rs ./ + +# Build the release binary +RUN cargo build --release --bin wassette + +# Stage 2: Create the runtime image +FROM debian:bookworm-slim + +# Install runtime dependencies +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + libssl3 && \ + rm -rf /var/lib/apt/lists/* + +# Create a non-root user for running Wassette +RUN useradd -m -u 1000 -s /bin/bash wassette + +# Create necessary directories with proper permissions +RUN mkdir -p /home/wassette/.local/share/wassette/components && \ + mkdir -p /home/wassette/.config/wassette/secrets && \ + chown -R wassette:wassette /home/wassette + +# Copy the binary from the builder stage +COPY --from=builder /build/target/release/wassette /usr/local/bin/wassette + +# Set up environment +ENV HOME=/home/wassette +ENV XDG_DATA_HOME=/home/wassette/.local/share +ENV XDG_CONFIG_HOME=/home/wassette/.config + +# Switch to the non-root user +USER wassette +WORKDIR /home/wassette + +# Expose the default HTTP port (when using --http or --sse) +EXPOSE 9001 + +# Default command: start Wassette with streamable-http transport +# Override this in docker run or docker-compose for different transports +CMD ["wassette", "serve", "--streamable-http"] diff --git a/Dockerfile.prebuilt b/Dockerfile.prebuilt new file mode 100644 index 00000000..b7a5893b --- /dev/null +++ b/Dockerfile.prebuilt @@ -0,0 +1,42 @@ +# Wassette Docker Image (Pre-built Binary) +# This Dockerfile uses a pre-built Wassette binary for faster builds +# Useful when you already have the binary compiled on your host system + +FROM debian:bookworm-slim + +# Install runtime dependencies +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + libssl3 && \ + rm -rf /var/lib/apt/lists/* + +# Create a non-root user for running Wassette +RUN useradd -m -u 1000 -s /bin/bash wassette + +# Create necessary directories with proper permissions +RUN mkdir -p /home/wassette/.local/share/wassette/components && \ + mkdir -p /home/wassette/.config/wassette/secrets && \ + chown -R wassette:wassette /home/wassette + +# Copy the pre-built binary from the host (build context) +# Build the binary first with: cargo build --release --bin wassette +# Then copy it: COPY target/release/wassette /usr/local/bin/wassette +COPY target/release/wassette /usr/local/bin/wassette +RUN chmod +x /usr/local/bin/wassette + +# Set up environment +ENV HOME=/home/wassette +ENV XDG_DATA_HOME=/home/wassette/.local/share +ENV XDG_CONFIG_HOME=/home/wassette/.config + +# Switch to the non-root user +USER wassette +WORKDIR /home/wassette + +# Expose the default HTTP port (when using --http or --sse) +EXPOSE 9001 + +# Default command: start Wassette with streamable-http transport +# Override this in docker run or docker-compose for different transports +CMD ["wassette", "serve", "--streamable-http"] diff --git a/README.md b/README.md index 767cd96c..90785704 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,28 @@ Available installation methods: - **[Homebrew](./docs/installation.md#homebrew)** for macOS and Linux - **[WinGet](./docs/installation.md#windows)** for Windows - **[Nix flakes](./docs/installation.md#nix-all-platforms)** for reproducible environments +- **[Docker](./docs/deployment/docker.md)** for containerized deployments - **[Manual download](https://github.com/microsoft/wassette/releases)** from GitHub Releases +### Docker Deployment + +For enhanced security isolation and reproducible environments, Wassette can run in Docker containers: + +```bash +# Build the image +docker build -t wassette:latest . + +# Run with streamable-http transport (default) +docker run --rm -p 9001:9001 wassette:latest + +# Mount components directory +docker run --rm -p 9001:9001 \ + -v ./components:/home/wassette/.local/share/wassette/components:ro \ + wassette:latest +``` + +See the **[Docker deployment guide](./docs/deployment/docker.md)** for detailed documentation on running Wassette in containers, including security best practices, component mounting, and production deployment patterns. + ## Using Wassette With Wassette installed, the next step is to register it with your agent of diff --git a/docker-compose.example.yml b/docker-compose.example.yml new file mode 100644 index 00000000..5457332b --- /dev/null +++ b/docker-compose.example.yml @@ -0,0 +1,84 @@ +# Example Docker Compose configuration for Wassette +# Copy this file to docker-compose.yml and customize for your needs + +version: '3.8' + +services: + wassette: + build: . + image: wassette:latest + + # Expose port 9001 for streamable-http transport (default) + ports: + - "9001:9001" + + # Mount volumes for components, secrets, and configuration + volumes: + # Component directory (read-only for security) + - ./components:/home/wassette/.local/share/wassette/components:ro + + # Secrets directory (read-only) + # Store API keys and credentials here + - ./secrets:/home/wassette/.config/wassette/secrets:ro + + # Optional: Custom configuration file + # - ./config.toml:/home/wassette/.config/wassette/config.toml:ro + + # Optional: Persistent component storage + # Use this if you want to load components via the MCP interface + # and persist them across container restarts + # - wassette-components:/home/wassette/.local/share/wassette/components + + # Environment variables + environment: + # Set log level (trace, debug, info, warn, error) + - RUST_LOG=info + + # Add any additional environment variables your components need + # - OPENWEATHER_API_KEY=your_api_key_here + + # Command to run (override the default CMD from Dockerfile) + # Note: Default is streamable-http, but you can override it + + # Default: Streamable HTTP transport (uses port 9001) + # Uses the default CMD from Dockerfile - no need to specify + + # Option 1: Override with stdio transport + # command: ["wassette", "serve", "--stdio"] + + # Option 2: Override with SSE transport + # command: ["wassette", "serve", "--sse"] + + # Security: Limit container resources + deploy: + resources: + limits: + cpus: '1.0' + memory: 512M + reservations: + cpus: '0.5' + memory: 256M + + # Security: Drop unnecessary capabilities + cap_drop: + - ALL + + # Security: Prevent privilege escalation + security_opt: + - no-new-privileges:true + + # Optional: Health check for SSE/HTTP transports + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:9001/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # Restart policy + restart: unless-stopped + +# Optional: Named volumes for persistent storage +# volumes: +# wassette-components: +# driver: local diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 1c04171b..c95101f9 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -2,6 +2,7 @@ - [Overview](./overview.md) - [Installation](./installation.md) + - [Docker](./deployment/docker.md) - [MCP Clients](./mcp-clients.md) - [FAQ](./faq.md) diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md new file mode 100644 index 00000000..bf4f6271 --- /dev/null +++ b/docs/deployment/docker.md @@ -0,0 +1,402 @@ +# Running Wassette in Docker + +This guide explains how to run Wassette in a Docker container for enhanced security isolation. Containerizing Wassette provides an additional layer of defense by isolating the runtime environment from your host system. + +## Why Use Docker with Wassette? + +Running Wassette in Docker provides several benefits: + +- **Enhanced Security**: Docker containers provide an additional isolation layer on top of Wassette's WebAssembly sandbox +- **Reproducible Environment**: Ensures consistent runtime behavior across different systems +- **Easy Deployment**: Simplifies deployment to production environments +- **Resource Control**: Allows fine-grained control over CPU, memory, and network resources + +## Prerequisites + +- Docker installed on your system ([Install Docker](https://docs.docker.com/get-docker/)) +- Basic familiarity with Docker commands + +## Quick Start + +### Build the Docker Image + +From the Wassette repository root: + +```bash +docker build -t wassette:latest . +``` + +This builds a multi-stage Docker image that: +1. Compiles Wassette from source in a Rust build environment +2. Creates a minimal runtime image with only necessary dependencies +3. Runs as a non-root user for enhanced security + +### Run with Streamable HTTP Transport (Default) + +The Docker image defaults to streamable-http transport: + +```bash +docker run --rm -p 9001:9001 wassette:latest +``` + +Then connect to `http://localhost:9001` from your MCP client. + +### Run with Stdio Transport + +For use with MCP clients that expect stdio, override the default command: + +```bash +docker run -i --rm wassette:latest wassette serve --stdio +``` + +### Run with SSE Transport + +For SSE transport, override the default command: + +```bash +docker run --rm -p 9001:9001 wassette:latest wassette serve --sse +``` + +Then connect to `http://localhost:9001/sse` from your MCP client. + +## Mounting Components + +To use custom WebAssembly components with Wassette in Docker, you need to mount the component directory: + +### Mount a Local Component Directory + +```bash +# Mount your local components directory +docker run -i --rm \ + -v /path/to/your/components:/home/wassette/.local/share/wassette/components:ro \ + wassette:latest +``` + +**Important**: Use `:ro` (read-only) for the component directory when possible to prevent accidental modifications. + +### Example: Running with Filesystem Component + +```bash +# Build example components first (on host) +cd examples/filesystem-rs +cargo build --release --target wasm32-wasip2 + +# Run Wassette with the example component mounted (streamable-http transport) +docker run --rm -p 9001:9001 \ + -v $(pwd)/examples/filesystem-rs/target/wasm32-wasip2/release:/home/wassette/.local/share/wassette/components:ro \ + wassette:latest + +# For stdio transport, override the default: +# docker run -i --rm \ +# -v $(pwd)/examples/filesystem-rs/target/wasm32-wasip2/release:/home/wassette/.local/share/wassette/components:ro \ +# wassette:latest wassette serve --stdio +``` + +### Example: Running with Multiple Component Directories + +You can mount multiple component directories using multiple `-v` flags: + +```bash +docker run --rm -p 9001:9001 \ + -v /path/to/components1:/home/wassette/.local/share/wassette/components:ro \ + -v /path/to/data:/data:rw \ + wassette:latest +``` + +## Mounting Secrets + +If your components require secrets (API keys, credentials, etc.), mount the secrets directory: + +```bash +docker run --rm -p 9001:9001 \ + -v /path/to/secrets:/home/wassette/.config/wassette/secrets:ro \ + -v /path/to/components:/home/wassette/.local/share/wassette/components:ro \ + wassette:latest +``` + +**Security Note**: Always mount secrets as read-only (`:ro`) and ensure proper file permissions. + +## Configuration + +### Using Environment Variables + +Pass environment variables to the container: + +```bash +docker run --rm -p 9001:9001 \ + -e RUST_LOG=debug \ + -e OPENWEATHER_API_KEY=your_api_key \ + wassette:latest +``` + +### Using a Configuration File + +Mount a custom configuration file: + +```bash +docker run --rm -p 9001:9001 \ + -v /path/to/config.toml:/home/wassette/.config/wassette/config.toml:ro \ + wassette:latest +``` + +Example `config.toml`: + +```toml +# Directory where components are stored +plugin_dir = "/home/wassette/.local/share/wassette/components" + +# Environment variables to be made available to components +[environment_vars] +API_KEY = "your_api_key" +LOG_LEVEL = "info" +``` + +## Docker Compose + +For more complex setups, use Docker Compose: + +```yaml +# docker-compose.yml +version: '3.8' + +services: + wassette: + build: . + image: wassette:latest + ports: + - "9001:9001" + volumes: + # Mount component directory (read-only) + - ./components:/home/wassette/.local/share/wassette/components:ro + # Mount secrets directory (read-only) + - ./secrets:/home/wassette/.config/wassette/secrets:ro + # Mount config file (optional) + - ./config.toml:/home/wassette/.config/wassette/config.toml:ro + environment: + - RUST_LOG=info + # Default is streamable-http, but you can override: + # command: ["wassette", "serve", "--sse"] + # command: ["wassette", "serve", "--stdio"] + # Security: Run with limited resources + deploy: + resources: + limits: + cpus: '1.0' + memory: 512M +``` + +Run with: + +```bash +docker-compose up +``` + +## Security Best Practices + +### 1. Run as Non-Root User + +The Dockerfile already configures Wassette to run as a non-root user (`wassette:1000`). Never run as root: + +```bash +# Good: Uses default non-root user +docker run --rm -p 9001:9001 wassette:latest + +# Bad: Don't do this! +# docker run -i --rm --user root wassette:latest +``` + +### 2. Use Read-Only Mounts + +Mount component and secret directories as read-only when possible: + +```bash +docker run --rm -p 9001:9001 \ + -v /path/to/components:/home/wassette/.local/share/wassette/components:ro \ + wassette:latest +``` + +### 3. Limit Container Resources + +Prevent resource exhaustion by setting limits: + +```bash +docker run --rm -p 9001:9001 \ + --memory="512m" \ + --cpus="1.0" \ + --pids-limit=100 \ + wassette:latest +``` + +### 4. Use Read-Only Root Filesystem + +For maximum security, run with a read-only root filesystem: + +```bash +docker run --rm -p 9001:9001 \ + --read-only \ + --tmpfs /tmp:rw,noexec,nosuid,size=50m \ + -v /path/to/components:/home/wassette/.local/share/wassette/components:ro \ + wassette:latest +``` + +### 5. Drop Unnecessary Capabilities + +Drop Linux capabilities that Wassette doesn't need: + +```bash +docker run --rm -p 9001:9001 \ + --cap-drop=ALL \ + --security-opt=no-new-privileges:true \ + wassette:latest +``` + +### 6. Enable Security Profiles + +Use AppArmor or SELinux for additional security: + +```bash +# With AppArmor +docker run --rm -p 9001:9001 \ + --security-opt apparmor=docker-default \ + wassette:latest + +# With SELinux +docker run --rm -p 9001:9001 \ + --security-opt label=type:container_runtime_t \ + wassette:latest +``` + +## Advanced Usage + +### Multi-Stage Build with Custom Base + +If you need a custom base image: + +```dockerfile +FROM rust:1.90-bookworm AS builder +# ... build stage ... + +FROM your-custom-base:latest +# ... runtime stage ... +``` + +### Health Checks + +Add health checks when running with HTTP/SSE transport: + +```yaml +# docker-compose.yml +services: + wassette: + # ... other config ... + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9001/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s +``` + +### Persistent Component Storage + +For persistent component storage across container restarts: + +```bash +# Create a named volume +docker volume create wassette-components + +# Use the volume +docker run --rm -p 9001:9001 \ + -v wassette-components:/home/wassette/.local/share/wassette/components \ + wassette:latest +``` + +## Troubleshooting + +### Permission Denied Errors + +If you encounter permission errors when mounting volumes: + +```bash +# Check the ownership of your mounted directories +ls -la /path/to/components + +# Ensure the wassette user (UID 1000) can read the files +sudo chown -R 1000:1000 /path/to/components +``` + +### Container Cannot Access Components + +Verify the mount path matches Wassette's expected directory: + +```bash +# Check inside the container +docker run -i --rm \ + -v /path/to/components:/home/wassette/.local/share/wassette/components:ro \ + wassette:latest sh -c "ls -la /home/wassette/.local/share/wassette/components" +``` + +### Network Connectivity Issues + +When using HTTP/SSE transport, ensure the port is properly exposed: + +```bash +# Check if the port is listening +docker run -d --name wassette-test -p 9001:9001 wassette:latest wassette serve --sse +docker logs wassette-test +curl http://localhost:9001/sse +docker rm -f wassette-test +``` + +## Building from Pre-Built Binaries + +For faster builds, you can create a Dockerfile that uses pre-built Wassette binaries: + +```dockerfile +FROM debian:bookworm-slim + +# Install runtime dependencies +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + libssl3 \ + curl && \ + rm -rf /var/lib/apt/lists/* + +# Download and install Wassette binary +ARG WASSETTE_VERSION=latest +RUN curl -fsSL https://github.com/microsoft/wassette/releases/download/${WASSETTE_VERSION}/wassette-linux-x86_64 -o /usr/local/bin/wassette && \ + chmod +x /usr/local/bin/wassette + +# Create non-root user and directories +RUN useradd -m -u 1000 -s /bin/bash wassette && \ + mkdir -p /home/wassette/.local/share/wassette/components && \ + mkdir -p /home/wassette/.config/wassette/secrets && \ + chown -R wassette:wassette /home/wassette + +ENV HOME=/home/wassette +ENV XDG_DATA_HOME=/home/wassette/.local/share +ENV XDG_CONFIG_HOME=/home/wassette/.config + +USER wassette +WORKDIR /home/wassette + +EXPOSE 9001 + +CMD ["wassette", "serve", "--stdio"] +``` + +This approach is faster as it doesn't require compiling from source. + +## Next Steps + +- Learn about [Wassette's permission system](../using/permissions.md) +- Explore [component examples](https://github.com/microsoft/wassette/tree/main/examples) +- Read the [CLI reference](../reference/cli.md) for all available commands +- Check the [FAQ](../faq.md) for common questions + +## Resources + +- [Docker Documentation](https://docs.docker.com/) +- [Docker Security Best Practices](https://docs.docker.com/engine/security/) +- [Wassette GitHub Repository](https://github.com/microsoft/wassette)