Self-hosting
For operations and engineering teams. Ordinary users do not need to deploy anything — use the hosted service and start with 5-minute setup.
This is a condensed English guide. The full, authoritative deployment guide (including MongoDB replica-set setup, Nginx details and backup procedures) is currently Chinese-only: 私有化部署.
Target architecture
Nginx :443
├── / → frontend static files
├── /api/ → NestJS :3000
├── /socket.io/ → NestJS :3000
└── /docs/ → VitePress static files
NestJS :3000 (PM2 cluster)
├── MySQL
├── MongoDB (replica set)
└── RedisRequirements
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4 cores |
| Memory | 4 GB | 8 GB |
| Disk | 40 GB | 100 GB |
Software:
- Node.js 20+
- MySQL 8.0+
- MongoDB 6.0+ (replica set required)
- Redis 6+
- Nginx 1.24+
MongoDB must run as a replica set: the platform uses transactions for fulfilment, escrow and the state layer. A standalone instance will fail at runtime.
Environment
Create .env in the repository root:
NODE_ENV=production
APP_PORT=3000
APP_ROUTE_PREFIX=api
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=hushwork
DB_USER=hushwork
DB_PASSWORD=strong-password
MONGODB_URI=mongodb://hushwork:strong-password@127.0.0.1:27017/hushwork?authSource=hushwork&replicaSet=rs0
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=strong-password
OPENAI_API_KEY=sk-...Optional, depending on enabled features:
| Variable | Purpose |
|---|---|
INTEGRATION_SECRET_KEY | Encrypts merchant ERP / CRM credentials; production fails fast if missing |
INTEGRATION_ALLOW_HOSTS | Explicitly allow private-network integration instances |
RUN_PAYMENT_PROVIDER | manual (default) or wechat for online payment |
VERIFICATION_ENFORCE | Block non-compliant deliveries instead of warning |
Set permissions:
chmod 600 .envBuild and run
git clone https://github.com/hushwork/hushwork.git
cd hushwork/hushwork-mainend
npm ci
npm run build
npm install -g pm2
pm2 start ecosystem.config.js
pm2 save
pm2 startupecosystem.config.js runs two instances by default. Socket.IO uses RedisIoAdapter, so Redis must be available for cross-instance broadcast.
Frontend and docs
npm run frontend:build
sudo mkdir -p /var/www/hushwork
sudo rsync -a --delete frontend/dist/ /var/www/hushwork/
npm run docs:build
sudo install -d -o "$USER" -g "$USER" -m 755 /var/www/hushwork-docs
npm run docs:deployNginx (minimal):
server {
listen 443 ssl http2;
server_name hushwork.example.com;
root /var/www/hushwork;
index index.html;
location /api/ {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 120s;
}
location /socket.io/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s;
}
location /docs/ {
alias /var/www/hushwork-docs/;
index index.html;
try_files $uri $uri/ $uri.html =404;
error_page 404 /docs/404.html;
}
location / {
try_files $uri $uri/ /index.html;
}
}Verify
# Application
curl -s http://127.0.0.1:3000/api/a2a/agent-card | head
# PM2
pm2 status
# State layer
npx jest libs/modules/src/state
# End-to-end reference case
npm run case:reference -- --commitBackup and upgrade
| Data | Command |
|---|---|
| MySQL | mysqldump --single-transaction hushwork > backup.sql |
| MongoDB | mongodump --uri="$MONGODB_URI" --out=/backup/mongo |
| Uploads | Back up COS or the local upload directory |
.env | Store securely; never commit |
Upgrade:
git pull --rebase
npm ci
npm run build
pm2 reload hushworkBack up first, validate in staging, and check the runbooks under docs/ops/ when database changes are involved.
Security checklist
- [ ] MySQL and MongoDB listen on 127.0.0.1 only
- [ ] Redis requires a password
- [ ]
.envis mode 600 - [ ] JWT secret is injected from the environment
- [ ] HTTPS redirect is enforced
- [ ] Upload size is limited
- [ ] Logs are redacted
- [ ] Backups and restore drills are scheduled