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
12 changes: 12 additions & 0 deletions deploy-v2/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Rustok prod stack (deploy-v2) — copy to `.env` (chmod 600) on the host and fill in.
# Mirrors /opt/rustok/deploy-v2/.env. Placeholders ONLY — never commit real values.

# Unlocks the agent keystore (set at `create-wallet`). REQUIRED.
RUSTOK_KEYRING_PASSWORD=

# Bearer key the MCP / clients present to the Gateway (Authorization: Bearer). REQUIRED.
RUSTOK_MCP_API_KEY=

# Inbound bearer key clients present to the MCP SSE transport. REQUIRED in prod.
# Generate: openssl rand -hex 32
RUSTOK_MCP_INBOUND_API_KEY=
28 changes: 28 additions & 0 deletions deploy-v2/Caddyfile-v2
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Cutover Caddyfile — api.rustokwallet.com now points at the new Gateway
# (Core+Gateway+MCP stack), replacing the old rustok-api monolith.
# aiwell.dev static site is preserved unchanged.

api.rustokwallet.com {
# Access logs to stdout (docker logs rustok-caddy) so we can observe any
# consumer hitting the new backend post-cutover and catch broken integrations.
log {
format json
}
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
}

reverse_proxy rustok-gateway:3000
}

aiwell.dev, www.aiwell.dev {
root * /var/www/aiwell.dev
file_server
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
}
}
13 changes: 13 additions & 0 deletions deploy-v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# deploy-v2 — production deployment artifacts

Mirror of `/opt/rustok/deploy-v2/` on the prod host (`ssh rustok-prod`), committed
for reproducibility. The new stack (compose project `rustokv2`): Core + Gateway +
MCP + Redis, fronted by Caddy on `api.rustokwallet.com`.

- `docker-compose.yml` — the deployed compose (env-interpolated; **no secrets**).
- `Caddyfile-v2` — TLS reverse-proxy config for `api.rustokwallet.com`.
- `.env.example` — required env keys; copy to `.env` (chmod 600) on the host and fill.

Images are shipped privately via `docker save | ssh | docker load` (Core source
stays private). Public images live on GHCR (`rustok-wallet` public, `rustok-core`
private). See `../docs/PROJECT-OVERVIEW.md` §6.
85 changes: 85 additions & 0 deletions deploy-v2/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: rustokv2

services:
redis:
image: redis:7-alpine
container_name: rustok-redis
restart: unless-stopped
networks: [rustokv2]

core:
image: rustok-core:v0.1.0
container_name: rustok-core
restart: unless-stopped
environment:
RUSTOK_GRPC_ADDR: "0.0.0.0:50051"
RUSTOK_REDIS_URL: "redis://rustok-redis:6379"
RUSTOK_KEYRING_PASSWORD: ${RUSTOK_KEYRING_PASSWORD:?}
RUSTOK_ALLOWED_CHAINS: "1,8453"
RUSTOK_RPC_URLS_1: "https://ethereum-rpc.publicnode.com"
RUSTOK_RPC_URLS_8453: "https://base-rpc.publicnode.com"
volumes:
- rustok-data:/home/rustok/.rustok/agent
networks: [rustokv2]
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=64m
user: "1000:1000"

gateway:
image: rustok-core:v0.1.0
container_name: rustok-gateway
entrypoint: ["/usr/local/bin/gateway"]
restart: unless-stopped
ports:
- "127.0.0.1:3001:3000" # loopback test port (live monolith stays on 3000)
environment:
RUSTOK_GATEWAY_ADDR: "0.0.0.0:3000"
RUSTOK_CORE_ADDR: "http://rustok-core:50051"
RUSTOK_MCP_API_KEY: ${RUSTOK_MCP_API_KEY:?}
networks: [rustokv2, shared] # shared = existing caddy network (for cutover)
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=64m
user: "1000:1000"
# Override the image's core healthcheck (probes :50051) — the gateway
# listens on :3000. No curl/wget in the image, so probe /health over /dev/tcp.
healthcheck:
test:
- CMD
- bash
- -c
- >-
exec 3<>/dev/tcp/127.0.0.1/3000 &&
printf 'GET /health HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3 &&
grep -q '"status":"ok"' <&3
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
depends_on: [core]

mcp:
image: rustok-mcp:v0.1.0
container_name: rustok-mcp
restart: unless-stopped
ports:
- "127.0.0.1:3002:3001" # loopback test port
environment:
RUSTOK_MCP_HOST: "0.0.0.0"
RUSTOK_MCP_PORT: "3001"
RUSTOK_MCP_GATEWAY_URL: "http://rustok-gateway:3000"
RUSTOK_MCP_API_KEY: ${RUSTOK_MCP_API_KEY:?}
RUSTOK_MCP_INBOUND_API_KEY: ${RUSTOK_MCP_INBOUND_API_KEY:?}
networks: [rustokv2]
depends_on: [gateway]

volumes:
rustok-data:

networks:
rustokv2:
driver: bridge
shared:
name: deploy_default
external: true