Skip to content
Merged
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
45 changes: 45 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Build artifacts
target/
*.rlib
*.rmeta

# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
*~

# Git
.git/
.gitignore

# Documentation
*.md
!README.md
docs/

# Test and demo files
.oif-demo/
scripts/

# Local configuration (mount at runtime instead)
config/*.local.toml

Comment thread
nahimterrazas marked this conversation as resolved.
# Seed overrides (may contain secrets - mount at runtime instead)
config/seed-overrides*.json

# CI/CD
.github/
.gitlab-ci.yml

# Docker
Dockerfile*
docker-compose*
.dockerignore

# Miscellaneous
*.log
*.tmp
.env*
.DS_Store
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Thumbs.db
# Environment
.env
.env.local
.env.docker

# Logs
*.log
Expand Down
65 changes: 65 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# ------------------------------------------------------------------------------
# Build environment
# ------------------------------------------------------------------------------
# Rustc v1.86.0
FROM cgr.dev/chainguard/rust:latest-dev@sha256:33faad9a26e8437ed9725bea3eb2d1e85facd1c035a31af8d485ea8c0a935532 AS builder
Comment thread
tirumerla marked this conversation as resolved.

# Optional features (e.g., "kms" for AWS KMS signer support)
ARG FEATURES=""

USER root
RUN apk update && apk --no-cache add \
openssl-dev \
perl

ENV PKG_CONFIG_PATH=/usr/lib/pkgconfig

WORKDIR /usr/app

# Copy source code
COPY . .

# Build with cargo cache mounts for faster rebuilds
# If FEATURES is set, add --features flag
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,target=/usr/app/target \
if [ -n "$FEATURES" ]; then \
cargo install --root /usr/app --path crates/solver-service --features "$FEATURES" --locked; \
else \
cargo install --root /usr/app --path crates/solver-service --locked; \
fi

# ------------------------------------------------------------------------------
# Runtime environment
# ------------------------------------------------------------------------------
FROM cgr.dev/chainguard/wolfi-base@sha256:417d791afa234c538bca977fe0f44011d2381e60a9fde44c938bd17b9cc38f66

WORKDIR /app

# Copy the compiled binary
COPY --from=builder --chown=nonroot:nonroot /usr/app/bin/solver /app/solver

USER nonroot

# Expose the default API port
EXPOSE 3000

# Set default environment variables
ENV RUST_LOG=info
ENV REDIS_URL=redis://localhost:6379

# Configuration is seeded to Redis on first run, then loaded automatically.
# See config/example.env.docker for required environment variables.
#
# First run (seed configuration):
# docker run --env-file .env.docker -v $(pwd)/config:/app/config:ro oif-solver \
# --seed testnet --seed-overrides /app/config/seed-overrides-testnet.json
#
# Subsequent runs (load from Redis):
# docker run --env-file .env.docker -e SOLVER_ID=your-solver-id oif-solver
#
# With KMS (build with: docker build --build-arg FEATURES=kms -t oif-solver .):
# docker run --env-file .env.docker -v $(pwd)/config:/app/config:ro oif-solver \
# --seed testnet --seed-overrides /app/config/seed-overrides-kms.json
ENTRYPOINT ["/app/solver"]
79 changes: 76 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ A high-performance cross-chain solver implementation for the Open Intents Framew
- [Project Structure](#project-structure)
- [Component Responsibilities](#component-responsibilities)
- [Quick Start](#quick-start)
- [Docker](#docker)
- [Configuration](#configuration)
- [AWS KMS Signing](#aws-kms-signing)
- [API Reference](#api-reference)
Expand Down Expand Up @@ -233,6 +234,78 @@ cargo run -- --seed testnet --seed-overrides config/seed-overrides-testnet.json
cargo run --
```

## Docker

The solver can be run in a Docker container for production deployments.

### Building the Image

```bash
# Standard build (local wallet only)
docker build -t oif-solver .

# Build with KMS support (requires AWS credentials at runtime)
docker build --build-arg FEATURES=kms -t oif-solver .
```

### Running the Container

```bash
# Create a docker-compatible env file (removes 'export' prefixes if present)
sed 's/^export //' .env > .env.docker

# First run: Seed configuration to Redis
docker run -it --rm \
-p 3000:3000 \
--env-file .env.docker \
-v $(pwd)/config:/app/config:ro \
oif-solver --seed testnet --seed-overrides /app/config/seed-overrides-testnet.json

# Subsequent runs: Load from Redis (set SOLVER_ID in .env.docker)
docker run -it --rm \
-p 3000:3000 \
--env-file .env.docker \
oif-solver

# Force re-seed (overwrite existing configuration)
docker run -it --rm \
-p 3000:3000 \
--env-file .env.docker \
-v $(pwd)/config:/app/config:ro \
oif-solver --seed testnet --seed-overrides /app/config/seed-overrides-testnet.json --force-seed
```

### Docker Networking

The solver binds to `0.0.0.0:3000` by default, which works for Docker deployments.

For Redis connectivity from within Docker:
- **Docker Compose:** Use service name (e.g., `redis://redis:6379`)
- **Host machine Redis:** Use `redis://host.docker.internal:6379` (Mac/Windows)
- **External Redis:** Use full URL (e.g., `redis://your-redis-host:6379`)

### Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `REDIS_URL` | Yes | Redis connection URL (for production, use managed services like AWS ElastiCache) |
| `SOLVER_ID` | For loading | Solver ID to load config from Redis (after first seed) |
| `RUST_LOG` | No | Log level (default: `info`) |
| `SOLVER_PRIVATE_KEY` | Conditional | Required for local wallet if no inline `private_key` in seed overrides |
| `AWS_ACCESS_KEY_ID` | For KMS | AWS access key (if using KMS signer) |
| `AWS_SECRET_ACCESS_KEY` | For KMS | AWS secret key (if using KMS signer) |
| `AWS_REGION` | For KMS | AWS region (if using KMS signer, default: from config) |
Comment thread
nahimterrazas marked this conversation as resolved.

**Note:** `SOLVER_PRIVATE_KEY` is required when using the local wallet without an inline `private_key` in seed overrides. Not needed if using KMS or providing `private_key` directly in the JSON.

**AWS Credentials:** When running in AWS (ECS, EKS, EC2), use IAM roles instead of explicit credentials. The SDK automatically uses the instance/task role credentials. Only set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` for local development or non-AWS environments.

### Volume Mounts

| Mount | Purpose |
|-------|---------|
| `/app/config` | Seed overrides JSON file (read-only, only needed for seeding) |

See [docs/config-storage.md](docs/config-storage.md) for detailed documentation.

## Configuration
Expand All @@ -259,11 +332,11 @@ cargo run -- --seed testnet --seed-overrides config/seed-overrides-testnet.json

| Variable | Required | Description |
|----------|----------|-------------|
| `REDIS_URL` | Yes | Redis connection URL (default: `redis://localhost:6379`) |
| `SOLVER_PRIVATE_KEY` | Yes* | 64-character hex private key (without 0x prefix) |
| `REDIS_URL` | Yes | Redis connection URL (default: `redis://localhost:6379`). For production, use managed services like AWS ElastiCache |
| `SOLVER_PRIVATE_KEY` | Conditional | 64-character hex private key (without 0x prefix) |
| `SOLVER_ID` | For loading | Solver ID to load from Redis (set after first seed) |

\* Not required when using AWS KMS for signing (see [AWS KMS Signing](#aws-kms-signing) below).
`SOLVER_PRIVATE_KEY` is only required when using the local wallet without an inline `private_key` in seed overrides. Not needed if using KMS or providing `private_key` directly in the JSON.

### Seed Overrides Format

Expand Down
16 changes: 16 additions & 0 deletions config/example.env.docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Required
REDIS_URL=redis://host.docker.internal:6379

# For local wallet (required unless using KMS)
SOLVER_PRIVATE_KEY=your-64-char-hex-private-key

# For subsequent runs (after seeding)
SOLVER_ID=my-solver

# Logging
RUST_LOG=info

# For KMS signer (build with: docker build --build-arg FEATURES=kms)
# AWS_ACCESS_KEY_ID=your-access-key
# AWS_SECRET_ACCESS_KEY=your-secret-key
# AWS_REGION=us-east-1
8 changes: 4 additions & 4 deletions crates/solver-service/src/config_merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ fn build_discovery_config_from_operator(chain_ids: &[u64]) -> DiscoveryConfig {

// Offchain discovery - receives orders via HTTP API from aggregators
let offchain_config = toml_table(vec![
("api_host", toml::Value::String("127.0.0.1".to_string())),
("api_host", toml::Value::String("0.0.0.0".to_string())),
("api_port", toml::Value::Integer(8081)),
("network_ids", network_ids_array),
]);
Expand Down Expand Up @@ -853,7 +853,7 @@ fn build_api_config_from_operator(admin: &OperatorAdminConfig) -> ApiConfig {

ApiConfig {
enabled: true,
host: "127.0.0.1".to_string(),
host: "0.0.0.0".to_string(),
port: 3000,
timeout_seconds: 30,
max_request_size: 1024 * 1024, // 1MB
Expand Down Expand Up @@ -1094,7 +1094,7 @@ fn build_discovery_config(chain_ids: &[u64], defaults: &SeedDefaults) -> Discove

// Offchain discovery - receives orders via HTTP API from aggregators
let offchain_config = toml_table(vec![
("api_host", toml::Value::String("127.0.0.1".to_string())),
("api_host", toml::Value::String("0.0.0.0".to_string())),
("api_port", toml::Value::Integer(8081)),
("network_ids", network_ids_array),
]);
Expand Down Expand Up @@ -1336,7 +1336,7 @@ fn build_api_config(

ApiConfig {
enabled: true,
host: "127.0.0.1".to_string(),
host: "0.0.0.0".to_string(),
port: 3000,
timeout_seconds: 30,
max_request_size: 1024 * 1024, // 1MB
Expand Down