This guide walks you through deploying Woodwire in your own AWS account and running the local bot.
- An AWS account
- The
awsCLI configured with credentials - A Cloudflare account and Workers subscription
- Node.js (v18+) for the Worker
- Python 3.10+ and uv for the bot
- A local LLM backend (OpenClaw or Ollama) or use the mock backend for testing
Woodwire's message flow involves two distinct pipelines:
Inbound Pipeline (Browser → SQS):
[ Browser PWA ] ──► [ Cloudflare Worker ] ──► [ AWS SQS Queue ]
(Static) (Zero-trust gateway) (Event buffer)
Outbound Pipeline (S3 → Browser):
[ Local Bot ] ──► [ Private S3 Bucket ] ──► [ Browser polls Worker ]
(OpenClaw/etc) (outbox/ responses) (for status checks)
aws cloudformation deploy \
--stack-name woodwire-chat-bucket \
--template-file infra/woodwire-chat-bucket.yaml \
--parameter-overrides \
ChatBucketName=woodwire-chat-bucket-<YOUR_ORG_ID> \
AllowedCorsOrigin=https://app.example.comNote: Choose a globally unique S3 bucket name. Save the ChatBucketName and ChatBucketArn outputs.
aws cloudformation deploy \
--stack-name woodwire-chat-queue \
--template-file infra/woodwire-chat-queue.yaml \
--parameter-overrides \
ChatQueueName=woodwire-chatSave the ChatQueueUrl, ChatQueueArn, and ChatDeadLetterQueueArn outputs.
If you want to deploy the app from GitHub Actions to AWS S3 + CloudFront:
aws cloudformation deploy \
--stack-name woodwire-iam \
--template-file infra/woodwire-iam.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1 \
--parameter-overrides \
ProjectName=woodwire \
ChatBucketName=<YOUR_CHAT_BUCKET_NAME> \
ChatQueueArn=<YOUR_CHAT_QUEUE_ARN> \
PwaHostingBucketName=woodwire-pwa \
CloudFrontDistributionId=<YOUR_CLOUDFRONT_DISTRIBUTION_ID> \
GitHubRepositoryOwner=<YOUR_GITHUB_USERNAME> \
GitHubRepositoryName=woodwire \
GitHubOidcProviderArn=<YOUR_GITHUB_OIDC_PROVIDER_ARN> \
BillingAlarmSnsTopicArn=<YOUR_SNS_TOPIC_ARN>Important: Deploy this stack in us-east-1 because it includes the AWS Billing estimated charges alarm.
cd worker
# Store the shared authentication passphrase
wrangler secret put WOODWIRE_AUTH
# Store AWS credentials (from your IAM user or temporary credentials)
wrangler secret put AWS_ACCESS_KEY_ID
wrangler secret put AWS_SECRET_ACCESS_KEY
# Optional: For temporary credentials, also set the session token
wrangler secret put AWS_SESSION_TOKENEdit worker/wrangler.toml and set:
[env.production]
vars = {
AWS_REGION = "us-east-1",
CHAT_QUEUE_URL = "https://sqs.us-east-1.amazonaws.com/123456789012/woodwire-chat",
CHAT_BUCKET_NAME = "woodwire-chat-bucket-your-org-id",
PWA_ORIGIN = "https://app.example.com",
RATE_LIMIT_REQUESTS = "30",
RATE_LIMIT_WINDOW_SECONDS = "60",
STATUS_CACHE_TTL_SECONDS = "3"
}# Test the Worker locally
npx --yes vitest run worker/index.test.js
npx wrangler deploy --dry-run
# Deploy to production
npx wrangler deploySee worker/README.md for full configuration details.
Create a .env file in the repository root:
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=us-east-1
SQS_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/123456789012/woodwire-chat
S3_BUCKET_NAME=woodwire-chat-bucket-your-org-id
AI_BACKEND=openclaw
OPENCLAW_URL=http://127.0.0.1:8080/process
# Optional: Enable client-side end-to-end encryption (32-byte base64 key)
# WOODWIRE_E2EE_KEY=your-base64-encoded-32-byte-keycd bot
uv sync
source .venv/bin/activate
python main.pyOr use uv run directly:
cd bot
uv sync
PYTHONPATH=.. uv run python main.pyFor a quick smoke test without setting up OpenClaw:
cd bot
AI_BACKEND=mock uv run python main.pySee bot/README.md for full setup and troubleshooting.
aws s3 sync src/ s3://woodwire-pwa/ \
--delete \
--cache-control "public, max-age=3600"
# Invalidate CloudFront cache (if using CloudFront)
aws cloudfront create-invalidation \
--distribution-id E1ABCDEF2GHIJK \
--paths "/*"- Provision the IAM resources (Step 1.3 above)
- Add repository variables in Settings → Secrets and variables → Actions → Variables:
AWS_ROLE_ARN: Your GitHub Actions IAM roleAWS_REGION:us-east-1S3_BUCKET_NAME: Your PWA hosting bucket nameCLOUDFRONT_DISTRIBUTION_ID: Your CloudFront distribution ID
- Push to
mainto trigger automatic deployment, or run the workflow manually
See src/README.md for development details.
curl https://your-worker.your-username.workers.dev/api/health
# Expected response: {"status":"ok"}- Navigate to your deployed app URL
- Enter the shared passphrase (from Step 2.1)
- Try sending a message and verify it reaches your bot process
- Send a message from the PWA
- Check bot logs to see the inbound message
- Verify the bot's response appears in S3 (
s3://your-bucket/outbox/) - Refresh the PWA to see the response
The bot automatically processes audio attachments if you have ffmpeg installed and voice engines configured. See bot/README.md for details.
Set WOODWIRE_E2EE_KEY in your bot .env file and the PWA settings to enable AES-256-GCM encryption. The Worker never receives the key. See README.md for details.
For a quick GitHub-hosted demo:
- Go to Settings → Pages
- Set Source to GitHub Actions
- Push to
mainto deploysrc/automatically
- Verify
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEYhave SQS permissions - Check
SQS_QUEUE_URLis correct (from Step 1.2) - Confirm the bot is in the same AWS region
- Verify
WOODWIRE_AUTHpassphrase matches between Worker and PWA - Check the
X-Woodwire-Authheader is being sent in browser requests
- Check bot logs for SQS connection errors
- Verify
CHAT_BUCKET_NAMEandCHAT_QUEUE_URLmatch your CloudFormation outputs - Ensure the IAM user has S3 and SQS permissions
- Install
ffmpeg:brew install ffmpeg(macOS) orapt-get install ffmpeg(Linux) - Set
PIPER_MODEL_PATHto your downloaded voice model file - Check bot logs for
VoiceEngineUnavailableErrorwarnings
- Read meta/DEVELOPMENT_PHILOSOPHY.md to understand the project's design principles
- Explore meta/adr/ for architectural decision records
- Review component-specific documentation: src/README.md, worker/README.md, bot/README.md, infra/README.md
This is a personal project built for my own use. While you're welcome to clone it and use it, expect limited maintenance and support. For issues, feature requests, or questions, feel free to open a GitHub issue.