feat: add Hostinger VPS remote deployment tooling
Add complete deployment infrastructure for Hostinger VPS servers: - setup-server.sh: initial VPS provisioning (Docker/binary, firewall, fail2ban) - deploy.sh: automated remote deploy via SSH (Docker or binary methods) - rollback.sh: quick rollback to previous version - status.sh: remote health and status monitoring - docker-compose.production.yml: production-ready compose with resource limits - picoclaw.service: systemd unit with security hardening - DEPLOY.md: step-by-step deployment guide (Portuguese) - Makefile targets: deploy-hostinger, deploy-hostinger-setup, status, rollback https://claude.ai/code/session_019vXaqxGmkdCjM8m3jp6rYj
This commit is contained in:
parent
13e4028d42
commit
42a2918665
8 changed files with 1122 additions and 5 deletions
57
Makefile
57
Makefile
|
|
@ -1,4 +1,4 @@
|
||||||
.PHONY: all build install uninstall clean help test
|
.PHONY: all build install uninstall clean help test deploy-hostinger deploy-hostinger-setup deploy-hostinger-status deploy-hostinger-rollback
|
||||||
|
|
||||||
# Build variables
|
# Build variables
|
||||||
BINARY_NAME=picoclaw
|
BINARY_NAME=picoclaw
|
||||||
|
|
@ -148,6 +148,44 @@ check: deps fmt vet test
|
||||||
run: build
|
run: build
|
||||||
@$(BUILD_DIR)/$(BINARY_NAME) $(ARGS)
|
@$(BUILD_DIR)/$(BINARY_NAME) $(ARGS)
|
||||||
|
|
||||||
|
# ── Hostinger Deployment ─────────────────────────────
|
||||||
|
HOSTINGER_HOST?=
|
||||||
|
HOSTINGER_USER?=root
|
||||||
|
HOSTINGER_SSH_KEY?=$(HOME)/.ssh/id_rsa
|
||||||
|
HOSTINGER_SSH_PORT?=22
|
||||||
|
HOSTINGER_DEPLOY_METHOD?=docker
|
||||||
|
|
||||||
|
## deploy-hostinger-setup: Run initial server setup on Hostinger VPS
|
||||||
|
deploy-hostinger-setup:
|
||||||
|
@bash deploy/hostinger/setup-server.sh
|
||||||
|
|
||||||
|
## deploy-hostinger: Deploy PicoClaw to Hostinger VPS
|
||||||
|
deploy-hostinger:
|
||||||
|
@bash deploy/hostinger/deploy.sh \
|
||||||
|
-h "$(HOSTINGER_HOST)" \
|
||||||
|
-u "$(HOSTINGER_USER)" \
|
||||||
|
-k "$(HOSTINGER_SSH_KEY)" \
|
||||||
|
-p "$(HOSTINGER_SSH_PORT)" \
|
||||||
|
-m "$(HOSTINGER_DEPLOY_METHOD)"
|
||||||
|
|
||||||
|
## deploy-hostinger-status: Check PicoClaw status on Hostinger VPS
|
||||||
|
deploy-hostinger-status:
|
||||||
|
@bash deploy/hostinger/status.sh \
|
||||||
|
-h "$(HOSTINGER_HOST)" \
|
||||||
|
-u "$(HOSTINGER_USER)" \
|
||||||
|
-k "$(HOSTINGER_SSH_KEY)" \
|
||||||
|
-p "$(HOSTINGER_SSH_PORT)" \
|
||||||
|
-m "$(HOSTINGER_DEPLOY_METHOD)"
|
||||||
|
|
||||||
|
## deploy-hostinger-rollback: Rollback PicoClaw on Hostinger VPS
|
||||||
|
deploy-hostinger-rollback:
|
||||||
|
@bash deploy/hostinger/rollback.sh \
|
||||||
|
-h "$(HOSTINGER_HOST)" \
|
||||||
|
-u "$(HOSTINGER_USER)" \
|
||||||
|
-k "$(HOSTINGER_SSH_KEY)" \
|
||||||
|
-p "$(HOSTINGER_SSH_PORT)" \
|
||||||
|
-m "$(HOSTINGER_DEPLOY_METHOD)"
|
||||||
|
|
||||||
## help: Show this help message
|
## help: Show this help message
|
||||||
help:
|
help:
|
||||||
@echo "picoclaw Makefile"
|
@echo "picoclaw Makefile"
|
||||||
|
|
@ -162,12 +200,21 @@ help:
|
||||||
@echo " make build # Build for current platform"
|
@echo " make build # Build for current platform"
|
||||||
@echo " make install # Install to ~/.local/bin"
|
@echo " make install # Install to ~/.local/bin"
|
||||||
@echo " make uninstall # Remove from /usr/local/bin"
|
@echo " make uninstall # Remove from /usr/local/bin"
|
||||||
@echo " make install-skills # Install skills to workspace"
|
@echo ""
|
||||||
|
@echo "Hostinger Deployment:"
|
||||||
|
@echo " make deploy-hostinger-setup HOSTINGER_HOST=1.2.3.4 # Initial server setup"
|
||||||
|
@echo " make deploy-hostinger HOSTINGER_HOST=1.2.3.4 # Deploy to VPS"
|
||||||
|
@echo " make deploy-hostinger-status HOSTINGER_HOST=1.2.3.4 # Check status"
|
||||||
|
@echo " make deploy-hostinger-rollback HOSTINGER_HOST=1.2.3.4 # Rollback"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Environment Variables:"
|
@echo "Environment Variables:"
|
||||||
@echo " INSTALL_PREFIX # Installation prefix (default: ~/.local)"
|
@echo " INSTALL_PREFIX # Installation prefix (default: ~/.local)"
|
||||||
@echo " WORKSPACE_DIR # Workspace directory (default: ~/.picoclaw/workspace)"
|
@echo " WORKSPACE_DIR # Workspace directory (default: ~/.picoclaw/workspace)"
|
||||||
@echo " VERSION # Version string (default: git describe)"
|
@echo " VERSION # Version string (default: git describe)"
|
||||||
|
@echo " HOSTINGER_HOST # Hostinger VPS IP address"
|
||||||
|
@echo " HOSTINGER_USER # SSH user (default: root)"
|
||||||
|
@echo " HOSTINGER_SSH_KEY # SSH key path (default: ~/.ssh/id_rsa)"
|
||||||
|
@echo " HOSTINGER_DEPLOY_METHOD # Deploy method: docker or binary (default: docker)"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Current Configuration:"
|
@echo "Current Configuration:"
|
||||||
@echo " Platform: $(PLATFORM)/$(ARCH)"
|
@echo " Platform: $(PLATFORM)/$(ARCH)"
|
||||||
|
|
|
||||||
205
deploy/hostinger/DEPLOY.md
Normal file
205
deploy/hostinger/DEPLOY.md
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
# PicoClaw - Deploy na Hostinger VPS
|
||||||
|
|
||||||
|
Guia completo para deploy do PicoClaw em um servidor VPS da Hostinger.
|
||||||
|
|
||||||
|
## Pre-requisitos
|
||||||
|
|
||||||
|
- VPS Hostinger com Ubuntu 22.04+ ou Debian 12+ (KVM)
|
||||||
|
- Acesso SSH configurado (chave SSH recomendado)
|
||||||
|
- API keys dos provedores que deseja usar (Anthropic, OpenAI, etc.)
|
||||||
|
|
||||||
|
> **Nota:** Hospedagem compartilhada da Hostinger **nao** suporta PicoClaw.
|
||||||
|
> Voce precisa de um plano VPS ou Cloud com acesso root.
|
||||||
|
|
||||||
|
## Metodo 1: Deploy via Docker (Recomendado)
|
||||||
|
|
||||||
|
### Passo 1: Configurar acesso SSH
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Gerar chave SSH (se ainda nao tem)
|
||||||
|
ssh-keygen -t ed25519 -C "picoclaw-hostinger"
|
||||||
|
|
||||||
|
# Copiar chave para o servidor
|
||||||
|
ssh-copy-id -i ~/.ssh/id_ed25519 root@SEU_IP_VPS
|
||||||
|
```
|
||||||
|
|
||||||
|
### Passo 2: Setup inicial do servidor
|
||||||
|
|
||||||
|
Execute uma unica vez para preparar o servidor:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Opcao A: Via make
|
||||||
|
make deploy-hostinger-setup HOSTINGER_HOST=SEU_IP_VPS
|
||||||
|
|
||||||
|
# Opcao B: Direto via SSH
|
||||||
|
ssh root@SEU_IP_VPS 'bash -s' < deploy/hostinger/setup-server.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Isso instala: Docker, firewall (ufw), fail2ban, e cria a estrutura de diretorios.
|
||||||
|
|
||||||
|
### Passo 3: Configurar API keys no servidor
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@SEU_IP_VPS
|
||||||
|
|
||||||
|
# Editar chaves de API
|
||||||
|
nano /opt/picoclaw/config/.env
|
||||||
|
|
||||||
|
# Editar configuracao do PicoClaw
|
||||||
|
nano /opt/picoclaw/config/config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Exemplo minimo do `.env`:
|
||||||
|
```bash
|
||||||
|
ANTHROPIC_API_KEY=sk-ant-sua-chave-aqui
|
||||||
|
TELEGRAM_BOT_TOKEN=123456:ABC-seu-token
|
||||||
|
TZ=America/Sao_Paulo
|
||||||
|
```
|
||||||
|
|
||||||
|
### Passo 4: Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy com Docker
|
||||||
|
make deploy-hostinger HOSTINGER_HOST=SEU_IP_VPS
|
||||||
|
|
||||||
|
# Ou com variaveis de ambiente
|
||||||
|
export HOSTINGER_HOST=SEU_IP_VPS
|
||||||
|
export HOSTINGER_USER=root
|
||||||
|
make deploy-hostinger
|
||||||
|
```
|
||||||
|
|
||||||
|
### Passo 5: Verificar
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Status completo
|
||||||
|
make deploy-hostinger-status HOSTINGER_HOST=SEU_IP_VPS
|
||||||
|
|
||||||
|
# Health check rapido
|
||||||
|
curl http://SEU_IP_VPS:18790/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## Metodo 2: Deploy via Binario (Menor consumo)
|
||||||
|
|
||||||
|
Para VPS com pouca memoria (512MB-1GB), o deploy via binario usa menos recursos.
|
||||||
|
|
||||||
|
### Setup do servidor
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@SEU_IP_VPS 'bash -s -- binary' < deploy/hostinger/setup-server.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build local e upload (mais rapido se sua maquina e potente)
|
||||||
|
make deploy-hostinger HOSTINGER_HOST=SEU_IP_VPS HOSTINGER_DEPLOY_METHOD=binary
|
||||||
|
|
||||||
|
# Ou build no servidor (nao precisa de Go local)
|
||||||
|
# O script sincroniza o codigo e compila no VPS
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gerenciar servico
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@SEU_IP_VPS
|
||||||
|
|
||||||
|
# Status
|
||||||
|
systemctl status picoclaw
|
||||||
|
|
||||||
|
# Logs em tempo real
|
||||||
|
tail -f /opt/picoclaw/logs/picoclaw.log
|
||||||
|
|
||||||
|
# Reiniciar
|
||||||
|
systemctl restart picoclaw
|
||||||
|
|
||||||
|
# Parar
|
||||||
|
systemctl stop picoclaw
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comandos Make
|
||||||
|
|
||||||
|
| Comando | Descricao |
|
||||||
|
|---------|-----------|
|
||||||
|
| `make deploy-hostinger-setup` | Setup inicial do servidor |
|
||||||
|
| `make deploy-hostinger` | Deploy/atualizar PicoClaw |
|
||||||
|
| `make deploy-hostinger-status` | Verificar status |
|
||||||
|
| `make deploy-hostinger-rollback` | Reverter para versao anterior |
|
||||||
|
|
||||||
|
### Variaveis de ambiente
|
||||||
|
|
||||||
|
| Variavel | Padrao | Descricao |
|
||||||
|
|----------|--------|-----------|
|
||||||
|
| `HOSTINGER_HOST` | (obrigatorio) | IP ou hostname do VPS |
|
||||||
|
| `HOSTINGER_USER` | `root` | Usuario SSH |
|
||||||
|
| `HOSTINGER_SSH_KEY` | `~/.ssh/id_rsa` | Caminho da chave SSH |
|
||||||
|
| `HOSTINGER_SSH_PORT` | `22` | Porta SSH |
|
||||||
|
| `HOSTINGER_DEPLOY_METHOD` | `docker` | `docker` ou `binary` |
|
||||||
|
|
||||||
|
## Estrutura no Servidor
|
||||||
|
|
||||||
|
```
|
||||||
|
/opt/picoclaw/
|
||||||
|
├── bin/ # Binario do PicoClaw (metodo binary)
|
||||||
|
├── config/
|
||||||
|
│ ├── .env # Chaves de API (chmod 600)
|
||||||
|
│ └── config.json # Configuracao do PicoClaw
|
||||||
|
├── workspace/ # Workspace persistente
|
||||||
|
│ ├── memory/ # Memoria de sessoes
|
||||||
|
│ └── skills/ # Skills customizadas
|
||||||
|
├── logs/ # Logs (metodo binary)
|
||||||
|
├── backups/ # Backups de versoes anteriores
|
||||||
|
└── src/ # Codigo fonte (para build remoto)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Seguranca
|
||||||
|
|
||||||
|
- Firewall (ufw) configurado: apenas SSH (22) e Gateway (18790)
|
||||||
|
- fail2ban ativo para protecao contra brute-force SSH
|
||||||
|
- Servico roda com usuario dedicado `picoclaw` (sem root)
|
||||||
|
- Hardening via systemd (ProtectSystem, NoNewPrivileges, etc.)
|
||||||
|
- Arquivos `.env` e `config.json` com permissao 600
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Container nao inicia (Docker)
|
||||||
|
```bash
|
||||||
|
ssh root@SEU_IP_VPS
|
||||||
|
docker compose -f /opt/picoclaw/docker-compose.yml logs picoclaw-gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
### Servico nao inicia (Binary)
|
||||||
|
```bash
|
||||||
|
ssh root@SEU_IP_VPS
|
||||||
|
journalctl -u picoclaw -n 50 --no-pager
|
||||||
|
cat /opt/picoclaw/logs/picoclaw-error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Health check falha
|
||||||
|
```bash
|
||||||
|
# Verificar se a porta esta aberta
|
||||||
|
ss -tlnp | grep 18790
|
||||||
|
|
||||||
|
# Verificar firewall
|
||||||
|
ufw status
|
||||||
|
|
||||||
|
# Testar localmente no servidor
|
||||||
|
curl -v http://localhost:18790/health
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memoria insuficiente
|
||||||
|
Se o VPS tem pouca RAM, use o metodo binary em vez de Docker:
|
||||||
|
```bash
|
||||||
|
make deploy-hostinger HOSTINGER_HOST=SEU_IP_VPS HOSTINGER_DEPLOY_METHOD=binary
|
||||||
|
```
|
||||||
|
|
||||||
|
## Atualizacoes
|
||||||
|
|
||||||
|
Para atualizar o PicoClaw, basta rodar novamente:
|
||||||
|
```bash
|
||||||
|
make deploy-hostinger HOSTINGER_HOST=SEU_IP_VPS
|
||||||
|
```
|
||||||
|
|
||||||
|
Para reverter:
|
||||||
|
```bash
|
||||||
|
make deploy-hostinger-rollback HOSTINGER_HOST=SEU_IP_VPS
|
||||||
|
```
|
||||||
255
deploy/hostinger/deploy.sh
Executable file
255
deploy/hostinger/deploy.sh
Executable file
|
|
@ -0,0 +1,255 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================
|
||||||
|
# PicoClaw - Remote Deploy to Hostinger VPS
|
||||||
|
# ============================================================
|
||||||
|
# Deploys PicoClaw to a remote Hostinger VPS via SSH.
|
||||||
|
# Supports both Docker and binary deployment methods.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./deploy/hostinger/deploy.sh [OPTIONS]
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# -h, --host HOST VPS IP or hostname (required, or set HOSTINGER_HOST)
|
||||||
|
# -u, --user USER SSH user (default: root)
|
||||||
|
# -k, --key KEY SSH key path (default: ~/.ssh/id_rsa)
|
||||||
|
# -m, --method METHOD Deploy method: "docker" or "binary" (default: docker)
|
||||||
|
# -p, --port PORT SSH port (default: 22)
|
||||||
|
# --build-local Build binary locally and upload (for binary method)
|
||||||
|
# --help Show this help
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log() { echo -e "${GREEN}[DEPLOY]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||||
|
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||||
|
|
||||||
|
# ── Default Configuration ────────────────────────────
|
||||||
|
HOST="${HOSTINGER_HOST:-}"
|
||||||
|
USER="${HOSTINGER_USER:-root}"
|
||||||
|
SSH_KEY="${HOSTINGER_SSH_KEY:-${HOME}/.ssh/id_rsa}"
|
||||||
|
SSH_PORT="${HOSTINGER_SSH_PORT:-22}"
|
||||||
|
METHOD="${HOSTINGER_DEPLOY_METHOD:-docker}"
|
||||||
|
BUILD_LOCAL=false
|
||||||
|
REMOTE_DIR="/opt/picoclaw"
|
||||||
|
|
||||||
|
# ── Parse Arguments ──────────────────────────────────
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-h|--host) HOST="$2"; shift 2 ;;
|
||||||
|
-u|--user) USER="$2"; shift 2 ;;
|
||||||
|
-k|--key) SSH_KEY="$2"; shift 2 ;;
|
||||||
|
-m|--method) METHOD="$2"; shift 2 ;;
|
||||||
|
-p|--port) SSH_PORT="$2"; shift 2 ;;
|
||||||
|
--build-local) BUILD_LOCAL=true; shift ;;
|
||||||
|
--help)
|
||||||
|
head -20 "$0" | tail -15
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*) error "Unknown option: $1" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Validate ─────────────────────────────────────────
|
||||||
|
[ -z "${HOST}" ] && error "Host required. Use -h/--host or set HOSTINGER_HOST env var."
|
||||||
|
[ ! -f "${SSH_KEY}" ] && error "SSH key not found: ${SSH_KEY}"
|
||||||
|
|
||||||
|
SSH_OPTS="-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 -p ${SSH_PORT} -i ${SSH_KEY}"
|
||||||
|
SSH_CMD="ssh ${SSH_OPTS} ${USER}@${HOST}"
|
||||||
|
SCP_CMD="scp ${SSH_OPTS}"
|
||||||
|
|
||||||
|
# ── Verify Connection ────────────────────────────────
|
||||||
|
log "Verifying SSH connection to ${USER}@${HOST}:${SSH_PORT}..."
|
||||||
|
${SSH_CMD} "echo 'Connection OK'" || error "Cannot connect to ${HOST}. Check SSH settings."
|
||||||
|
|
||||||
|
# ── Get project root ─────────────────────────────────
|
||||||
|
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
cd "${PROJECT_ROOT}"
|
||||||
|
|
||||||
|
log "Deploying PicoClaw to ${HOST} via ${METHOD} method"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Deploy via Docker ────────────────────────────────
|
||||||
|
deploy_docker() {
|
||||||
|
log "=== Docker Deployment ==="
|
||||||
|
|
||||||
|
# 1. Sync project files to server
|
||||||
|
log "Syncing project files..."
|
||||||
|
rsync -avz --delete \
|
||||||
|
-e "ssh ${SSH_OPTS}" \
|
||||||
|
--exclude '.git' \
|
||||||
|
--exclude 'build/' \
|
||||||
|
--exclude '.env' \
|
||||||
|
--exclude 'config/config.json' \
|
||||||
|
--exclude '*.test' \
|
||||||
|
"${PROJECT_ROOT}/" "${USER}@${HOST}:${REMOTE_DIR}/src/"
|
||||||
|
|
||||||
|
# 2. Copy docker-compose production file
|
||||||
|
log "Copying Docker Compose production config..."
|
||||||
|
${SCP_CMD} "${PROJECT_ROOT}/deploy/hostinger/docker-compose.production.yml" \
|
||||||
|
"${USER}@${HOST}:${REMOTE_DIR}/docker-compose.yml"
|
||||||
|
|
||||||
|
# 3. Build and restart on server
|
||||||
|
log "Building and starting containers on server..."
|
||||||
|
${SSH_CMD} <<'REMOTEOF'
|
||||||
|
set -e
|
||||||
|
cd /opt/picoclaw
|
||||||
|
|
||||||
|
# Build the image
|
||||||
|
echo "[REMOTE] Building Docker image..."
|
||||||
|
docker compose build --no-cache picoclaw-gateway
|
||||||
|
|
||||||
|
# Stop existing container gracefully
|
||||||
|
echo "[REMOTE] Stopping existing container..."
|
||||||
|
docker compose down --timeout 30 2>/dev/null || true
|
||||||
|
|
||||||
|
# Start new container
|
||||||
|
echo "[REMOTE] Starting PicoClaw gateway..."
|
||||||
|
docker compose up -d picoclaw-gateway
|
||||||
|
|
||||||
|
# Wait and verify
|
||||||
|
sleep 5
|
||||||
|
if docker compose ps picoclaw-gateway | grep -q "Up"; then
|
||||||
|
echo "[REMOTE] PicoClaw is running!"
|
||||||
|
docker compose ps
|
||||||
|
else
|
||||||
|
echo "[REMOTE] ERROR: Container failed to start"
|
||||||
|
docker compose logs --tail=50 picoclaw-gateway
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
echo "[REMOTE] Checking health..."
|
||||||
|
sleep 3
|
||||||
|
if curl -sf http://localhost:18790/health > /dev/null 2>&1; then
|
||||||
|
echo "[REMOTE] Health check PASSED"
|
||||||
|
else
|
||||||
|
echo "[REMOTE] Health check failed (may still be starting up)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup old images
|
||||||
|
echo "[REMOTE] Cleaning up old Docker images..."
|
||||||
|
docker image prune -f
|
||||||
|
REMOTEOF
|
||||||
|
|
||||||
|
log "Docker deployment complete!"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Deploy via Binary ────────────────────────────────
|
||||||
|
deploy_binary() {
|
||||||
|
log "=== Binary Deployment ==="
|
||||||
|
|
||||||
|
if [ "${BUILD_LOCAL}" = true ]; then
|
||||||
|
# Build locally for linux/amd64
|
||||||
|
log "Building binary locally for linux/amd64..."
|
||||||
|
cd "${PROJECT_ROOT}"
|
||||||
|
make generate
|
||||||
|
GOOS=linux GOARCH=amd64 go build -v \
|
||||||
|
-ldflags "-X main.version=$(git describe --tags --always --dirty 2>/dev/null || echo dev) \
|
||||||
|
-X main.gitCommit=$(git rev-parse --short=8 HEAD 2>/dev/null || echo dev) \
|
||||||
|
-X main.buildTime=$(date +%FT%T%z)" \
|
||||||
|
-o build/picoclaw-linux-amd64 ./cmd/picoclaw
|
||||||
|
BINARY_PATH="build/picoclaw-linux-amd64"
|
||||||
|
log "Binary built: ${BINARY_PATH}"
|
||||||
|
else
|
||||||
|
# Build on server
|
||||||
|
log "Syncing source code to server..."
|
||||||
|
rsync -avz --delete \
|
||||||
|
-e "ssh ${SSH_OPTS}" \
|
||||||
|
--exclude '.git' \
|
||||||
|
--exclude 'build/' \
|
||||||
|
--exclude '.env' \
|
||||||
|
--exclude 'config/config.json' \
|
||||||
|
"${PROJECT_ROOT}/" "${USER}@${HOST}:${REMOTE_DIR}/src/"
|
||||||
|
|
||||||
|
log "Building on server..."
|
||||||
|
${SSH_CMD} <<'REMOTEOF'
|
||||||
|
set -e
|
||||||
|
cd /opt/picoclaw/src
|
||||||
|
export PATH=$PATH:/usr/local/go/bin
|
||||||
|
make build
|
||||||
|
cp build/picoclaw /opt/picoclaw/bin/picoclaw
|
||||||
|
chmod +x /opt/picoclaw/bin/picoclaw
|
||||||
|
echo "[REMOTE] Build complete: $(/opt/picoclaw/bin/picoclaw version 2>/dev/null || echo 'built')"
|
||||||
|
REMOTEOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${BUILD_LOCAL}" = true ]; then
|
||||||
|
# Upload binary
|
||||||
|
log "Uploading binary to server..."
|
||||||
|
${SCP_CMD} "${BINARY_PATH}" "${USER}@${HOST}:${REMOTE_DIR}/bin/picoclaw"
|
||||||
|
${SSH_CMD} "chmod +x ${REMOTE_DIR}/bin/picoclaw"
|
||||||
|
|
||||||
|
# Initialize workspace if needed
|
||||||
|
${SSH_CMD} <<REMOTEOF
|
||||||
|
set -e
|
||||||
|
if [ ! -d "${REMOTE_DIR}/workspace/memory" ]; then
|
||||||
|
echo "[REMOTE] Initializing workspace..."
|
||||||
|
sudo -u picoclaw ${REMOTE_DIR}/bin/picoclaw onboard
|
||||||
|
fi
|
||||||
|
REMOTEOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restart service
|
||||||
|
log "Restarting PicoClaw service..."
|
||||||
|
${SSH_CMD} <<'REMOTEOF'
|
||||||
|
set -e
|
||||||
|
echo "[REMOTE] Restarting picoclaw service..."
|
||||||
|
systemctl restart picoclaw
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
if systemctl is-active --quiet picoclaw; then
|
||||||
|
echo "[REMOTE] PicoClaw is running!"
|
||||||
|
systemctl status picoclaw --no-pager
|
||||||
|
else
|
||||||
|
echo "[REMOTE] ERROR: Service failed to start"
|
||||||
|
journalctl -u picoclaw --no-pager -n 30
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
sleep 3
|
||||||
|
if curl -sf http://localhost:18790/health > /dev/null 2>&1; then
|
||||||
|
echo "[REMOTE] Health check PASSED"
|
||||||
|
else
|
||||||
|
echo "[REMOTE] Health check failed (may still be starting up)"
|
||||||
|
tail -20 /opt/picoclaw/logs/picoclaw.log 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
REMOTEOF
|
||||||
|
|
||||||
|
log "Binary deployment complete!"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Execute Deploy ───────────────────────────────────
|
||||||
|
DEPLOY_START=$(date +%s)
|
||||||
|
|
||||||
|
case "${METHOD}" in
|
||||||
|
docker) deploy_docker ;;
|
||||||
|
binary) deploy_binary ;;
|
||||||
|
*) error "Unknown method: ${METHOD}. Use 'docker' or 'binary'." ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
DEPLOY_END=$(date +%s)
|
||||||
|
DEPLOY_DURATION=$((DEPLOY_END - DEPLOY_START))
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
log "=========================================="
|
||||||
|
log " Deployment successful!"
|
||||||
|
log " Duration: ${DEPLOY_DURATION}s"
|
||||||
|
log " Host: ${HOST}"
|
||||||
|
log " Method: ${METHOD}"
|
||||||
|
log "=========================================="
|
||||||
|
echo ""
|
||||||
|
info "Useful commands:"
|
||||||
|
echo " Check status: ${SSH_CMD} 'systemctl status picoclaw'"
|
||||||
|
echo " View logs: ${SSH_CMD} 'tail -f /opt/picoclaw/logs/picoclaw.log'"
|
||||||
|
echo " Health check: curl http://${HOST}:18790/health"
|
||||||
|
echo ""
|
||||||
48
deploy/hostinger/docker-compose.production.yml
Normal file
48
deploy/hostinger/docker-compose.production.yml
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
# ============================================================
|
||||||
|
# PicoClaw - Production Docker Compose for Hostinger VPS
|
||||||
|
# ============================================================
|
||||||
|
# Usage:
|
||||||
|
# docker compose up -d picoclaw-gateway
|
||||||
|
# docker compose logs -f picoclaw-gateway
|
||||||
|
# docker compose down
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
services:
|
||||||
|
picoclaw-gateway:
|
||||||
|
build:
|
||||||
|
context: ./src
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: picoclaw-gateway
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "18790:18790"
|
||||||
|
volumes:
|
||||||
|
# Configuration (read-only)
|
||||||
|
- /opt/picoclaw/config/config.json:/root/.picoclaw/config.json:ro
|
||||||
|
# Persistent workspace (sessions, memory, logs)
|
||||||
|
- picoclaw-workspace:/root/.picoclaw/workspace
|
||||||
|
env_file:
|
||||||
|
- /opt/picoclaw/config/.env
|
||||||
|
environment:
|
||||||
|
- TZ=${TZ:-America/Sao_Paulo}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-q", "--spider", "http://localhost:18790/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "5"
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 128M
|
||||||
|
reservations:
|
||||||
|
memory: 32M
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
picoclaw-workspace:
|
||||||
|
driver: local
|
||||||
51
deploy/hostinger/picoclaw.service
Normal file
51
deploy/hostinger/picoclaw.service
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
# ============================================================
|
||||||
|
# PicoClaw - systemd Service File
|
||||||
|
# ============================================================
|
||||||
|
# Install: sudo cp picoclaw.service /etc/systemd/system/
|
||||||
|
# sudo systemctl daemon-reload
|
||||||
|
# sudo systemctl enable picoclaw
|
||||||
|
# sudo systemctl start picoclaw
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=PicoClaw AI Assistant Gateway
|
||||||
|
Documentation=https://github.com/agenciaspace/picoclaw
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
StartLimitIntervalSec=300
|
||||||
|
StartLimitBurst=5
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=picoclaw
|
||||||
|
Group=picoclaw
|
||||||
|
WorkingDirectory=/opt/picoclaw
|
||||||
|
ExecStart=/opt/picoclaw/bin/picoclaw gateway
|
||||||
|
ExecReload=/bin/kill -HUP $MAINPID
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
TimeoutStopSec=30
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
StandardOutput=append:/opt/picoclaw/logs/picoclaw.log
|
||||||
|
StandardError=append:/opt/picoclaw/logs/picoclaw-error.log
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
EnvironmentFile=-/opt/picoclaw/config/.env
|
||||||
|
Environment=HOME=/opt/picoclaw
|
||||||
|
Environment=PICOCLAW_CONFIG=/opt/picoclaw/config/config.json
|
||||||
|
|
||||||
|
# Security hardening
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ReadWritePaths=/opt/picoclaw
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
RestrictSUIDSGID=yes
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
109
deploy/hostinger/rollback.sh
Executable file
109
deploy/hostinger/rollback.sh
Executable file
|
|
@ -0,0 +1,109 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================
|
||||||
|
# PicoClaw - Rollback to Previous Version
|
||||||
|
# ============================================================
|
||||||
|
# Usage:
|
||||||
|
# ./deploy/hostinger/rollback.sh -h YOUR_VPS_IP
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log() { echo -e "${GREEN}[ROLLBACK]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
HOST="${HOSTINGER_HOST:-}"
|
||||||
|
USER="${HOSTINGER_USER:-root}"
|
||||||
|
SSH_KEY="${HOSTINGER_SSH_KEY:-${HOME}/.ssh/id_rsa}"
|
||||||
|
SSH_PORT="${HOSTINGER_SSH_PORT:-22}"
|
||||||
|
METHOD="${HOSTINGER_DEPLOY_METHOD:-docker}"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-h|--host) HOST="$2"; shift 2 ;;
|
||||||
|
-u|--user) USER="$2"; shift 2 ;;
|
||||||
|
-k|--key) SSH_KEY="$2"; shift 2 ;;
|
||||||
|
-m|--method) METHOD="$2"; shift 2 ;;
|
||||||
|
-p|--port) SSH_PORT="$2"; shift 2 ;;
|
||||||
|
*) error "Unknown option: $1" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[ -z "${HOST}" ] && error "Host required. Use -h/--host or set HOSTINGER_HOST env var."
|
||||||
|
|
||||||
|
SSH_OPTS="-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 -p ${SSH_PORT} -i ${SSH_KEY}"
|
||||||
|
SSH_CMD="ssh ${SSH_OPTS} ${USER}@${HOST}"
|
||||||
|
|
||||||
|
log "Rolling back PicoClaw on ${HOST}..."
|
||||||
|
|
||||||
|
if [ "${METHOD}" = "docker" ]; then
|
||||||
|
${SSH_CMD} <<'REMOTEOF'
|
||||||
|
set -e
|
||||||
|
cd /opt/picoclaw
|
||||||
|
|
||||||
|
# List available images
|
||||||
|
echo "Available images:"
|
||||||
|
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.CreatedAt}}" | grep picoclaw || true
|
||||||
|
|
||||||
|
# Docker rollback: restart with previous image
|
||||||
|
echo "[REMOTE] Stopping current container..."
|
||||||
|
docker compose down --timeout 30
|
||||||
|
|
||||||
|
echo "[REMOTE] Starting previous version..."
|
||||||
|
# The previous image should still be cached
|
||||||
|
docker compose up -d picoclaw-gateway
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
if docker compose ps picoclaw-gateway | grep -q "Up"; then
|
||||||
|
echo "[REMOTE] Rollback successful!"
|
||||||
|
docker compose ps
|
||||||
|
else
|
||||||
|
echo "[REMOTE] Rollback failed!"
|
||||||
|
docker compose logs --tail=20 picoclaw-gateway
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
REMOTEOF
|
||||||
|
else
|
||||||
|
${SSH_CMD} <<'REMOTEOF'
|
||||||
|
set -e
|
||||||
|
BACKUP_DIR="/opt/picoclaw/backups"
|
||||||
|
|
||||||
|
# Find latest backup
|
||||||
|
LATEST_BACKUP=$(ls -t ${BACKUP_DIR}/picoclaw-*.bak 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
if [ -z "${LATEST_BACKUP}" ]; then
|
||||||
|
echo "[REMOTE] No backup found to rollback to!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[REMOTE] Rolling back to: ${LATEST_BACKUP}"
|
||||||
|
|
||||||
|
# Stop service
|
||||||
|
systemctl stop picoclaw
|
||||||
|
|
||||||
|
# Restore binary
|
||||||
|
cp "${LATEST_BACKUP}" /opt/picoclaw/bin/picoclaw
|
||||||
|
chmod +x /opt/picoclaw/bin/picoclaw
|
||||||
|
|
||||||
|
# Start service
|
||||||
|
systemctl start picoclaw
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
if systemctl is-active --quiet picoclaw; then
|
||||||
|
echo "[REMOTE] Rollback successful!"
|
||||||
|
systemctl status picoclaw --no-pager
|
||||||
|
else
|
||||||
|
echo "[REMOTE] Rollback failed!"
|
||||||
|
journalctl -u picoclaw --no-pager -n 20
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
REMOTEOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Rollback complete!"
|
||||||
304
deploy/hostinger/setup-server.sh
Executable file
304
deploy/hostinger/setup-server.sh
Executable file
|
|
@ -0,0 +1,304 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================
|
||||||
|
# PicoClaw - Hostinger VPS Initial Setup
|
||||||
|
# ============================================================
|
||||||
|
# Run this script ONCE on a fresh Hostinger VPS to prepare it
|
||||||
|
# for PicoClaw deployment.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ssh root@YOUR_VPS_IP 'bash -s' < deploy/hostinger/setup-server.sh
|
||||||
|
#
|
||||||
|
# Or copy and run directly on the server:
|
||||||
|
# scp deploy/hostinger/setup-server.sh root@YOUR_VPS_IP:/tmp/
|
||||||
|
# ssh root@YOUR_VPS_IP 'bash /tmp/setup-server.sh'
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log() { echo -e "${GREEN}[SETUP]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||||
|
|
||||||
|
# ── Configuration ──────────────────────────────────────
|
||||||
|
PICOCLAW_USER="picoclaw"
|
||||||
|
PICOCLAW_HOME="/opt/picoclaw"
|
||||||
|
DEPLOY_METHOD="${1:-docker}" # "docker" or "binary"
|
||||||
|
|
||||||
|
log "PicoClaw Hostinger VPS Setup"
|
||||||
|
log "Deploy method: ${DEPLOY_METHOD}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── 1. System Update ──────────────────────────────────
|
||||||
|
log "Updating system packages..."
|
||||||
|
if command -v apt-get &>/dev/null; then
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get upgrade -y -qq
|
||||||
|
apt-get install -y -qq curl wget git ufw fail2ban unzip jq
|
||||||
|
elif command -v yum &>/dev/null; then
|
||||||
|
yum update -y -q
|
||||||
|
yum install -y -q curl wget git firewalld fail2ban unzip jq
|
||||||
|
elif command -v dnf &>/dev/null; then
|
||||||
|
dnf update -y -q
|
||||||
|
dnf install -y -q curl wget git firewalld fail2ban unzip jq
|
||||||
|
else
|
||||||
|
error "Unsupported package manager. This script supports apt, yum, and dnf."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 2. Create dedicated user ─────────────────────────
|
||||||
|
if ! id "${PICOCLAW_USER}" &>/dev/null; then
|
||||||
|
log "Creating dedicated user: ${PICOCLAW_USER}"
|
||||||
|
useradd --system --create-home --home-dir "${PICOCLAW_HOME}" \
|
||||||
|
--shell /bin/bash "${PICOCLAW_USER}"
|
||||||
|
else
|
||||||
|
log "User ${PICOCLAW_USER} already exists"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 3. Create directory structure ─────────────────────
|
||||||
|
log "Creating directory structure..."
|
||||||
|
mkdir -p "${PICOCLAW_HOME}"/{bin,config,workspace,logs,backups}
|
||||||
|
chown -R "${PICOCLAW_USER}:${PICOCLAW_USER}" "${PICOCLAW_HOME}"
|
||||||
|
|
||||||
|
# ── 4. Install Docker (if docker method) ─────────────
|
||||||
|
if [ "${DEPLOY_METHOD}" = "docker" ]; then
|
||||||
|
if ! command -v docker &>/dev/null; then
|
||||||
|
log "Installing Docker..."
|
||||||
|
curl -fsSL https://get.docker.com | sh
|
||||||
|
systemctl enable docker
|
||||||
|
systemctl start docker
|
||||||
|
usermod -aG docker "${PICOCLAW_USER}"
|
||||||
|
log "Docker installed successfully"
|
||||||
|
else
|
||||||
|
log "Docker already installed: $(docker --version)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install Docker Compose plugin if not present
|
||||||
|
if ! docker compose version &>/dev/null; then
|
||||||
|
log "Installing Docker Compose plugin..."
|
||||||
|
apt-get install -y -qq docker-compose-plugin 2>/dev/null || \
|
||||||
|
yum install -y -q docker-compose-plugin 2>/dev/null || \
|
||||||
|
dnf install -y -q docker-compose-plugin 2>/dev/null || {
|
||||||
|
COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r .tag_name)
|
||||||
|
curl -fsSL "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" \
|
||||||
|
-o /usr/local/bin/docker-compose
|
||||||
|
chmod +x /usr/local/bin/docker-compose
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
log "Docker Compose ready: $(docker compose version 2>/dev/null || docker-compose --version 2>/dev/null)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 5. Install Go (if binary method) ─────────────────
|
||||||
|
if [ "${DEPLOY_METHOD}" = "binary" ]; then
|
||||||
|
if ! command -v go &>/dev/null; then
|
||||||
|
log "Installing Go..."
|
||||||
|
GO_VERSION="1.23.4"
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
case "${ARCH}" in
|
||||||
|
x86_64) GO_ARCH="amd64" ;;
|
||||||
|
aarch64) GO_ARCH="arm64" ;;
|
||||||
|
*) GO_ARCH="${ARCH}" ;;
|
||||||
|
esac
|
||||||
|
curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" -o /tmp/go.tar.gz
|
||||||
|
rm -rf /usr/local/go
|
||||||
|
tar -C /usr/local -xzf /tmp/go.tar.gz
|
||||||
|
rm /tmp/go.tar.gz
|
||||||
|
echo 'export PATH=$PATH:/usr/local/go/bin' > /etc/profile.d/go.sh
|
||||||
|
export PATH=$PATH:/usr/local/go/bin
|
||||||
|
log "Go installed: $(go version)"
|
||||||
|
else
|
||||||
|
log "Go already installed: $(go version)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install make if not present
|
||||||
|
if ! command -v make &>/dev/null; then
|
||||||
|
apt-get install -y -qq make 2>/dev/null || \
|
||||||
|
yum install -y -q make 2>/dev/null || \
|
||||||
|
dnf install -y -q make 2>/dev/null
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 6. Configure Firewall ────────────────────────────
|
||||||
|
log "Configuring firewall..."
|
||||||
|
if command -v ufw &>/dev/null; then
|
||||||
|
ufw --force reset
|
||||||
|
ufw default deny incoming
|
||||||
|
ufw default allow outgoing
|
||||||
|
ufw allow ssh
|
||||||
|
ufw allow 18790/tcp comment "PicoClaw Gateway"
|
||||||
|
# Uncomment if you need webhook ports:
|
||||||
|
# ufw allow 18791/tcp comment "PicoClaw Line Webhook"
|
||||||
|
ufw --force enable
|
||||||
|
log "UFW firewall configured"
|
||||||
|
elif command -v firewall-cmd &>/dev/null; then
|
||||||
|
systemctl enable firewalld
|
||||||
|
systemctl start firewalld
|
||||||
|
firewall-cmd --permanent --add-service=ssh
|
||||||
|
firewall-cmd --permanent --add-port=18790/tcp
|
||||||
|
firewall-cmd --reload
|
||||||
|
log "firewalld configured"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 7. Configure fail2ban ────────────────────────────
|
||||||
|
log "Configuring fail2ban..."
|
||||||
|
systemctl enable fail2ban
|
||||||
|
systemctl start fail2ban
|
||||||
|
|
||||||
|
# ── 8. Create environment template ───────────────────
|
||||||
|
if [ ! -f "${PICOCLAW_HOME}/config/.env" ]; then
|
||||||
|
log "Creating environment template..."
|
||||||
|
cat > "${PICOCLAW_HOME}/config/.env" <<'ENVEOF'
|
||||||
|
# ============================================================
|
||||||
|
# PicoClaw Production Environment
|
||||||
|
# ============================================================
|
||||||
|
# Edit this file with your actual API keys and tokens.
|
||||||
|
# NEVER commit this file to version control.
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# ── LLM Provider (uncomment one) ──────────────────────
|
||||||
|
# ANTHROPIC_API_KEY=sk-ant-xxx
|
||||||
|
# OPENAI_API_KEY=sk-xxx
|
||||||
|
# OPENROUTER_API_KEY=sk-or-v1-xxx
|
||||||
|
# GEMINI_API_KEY=xxx
|
||||||
|
|
||||||
|
# ── Chat Channel ─────────────────────────────────────
|
||||||
|
# TELEGRAM_BOT_TOKEN=123456:ABC...
|
||||||
|
# DISCORD_BOT_TOKEN=xxx
|
||||||
|
|
||||||
|
# ── Web Search (optional) ────────────────────────────
|
||||||
|
# BRAVE_SEARCH_API_KEY=BSA...
|
||||||
|
|
||||||
|
# ── Timezone ─────────────────────────────────────────
|
||||||
|
TZ=America/Sao_Paulo
|
||||||
|
ENVEOF
|
||||||
|
chown "${PICOCLAW_USER}:${PICOCLAW_USER}" "${PICOCLAW_HOME}/config/.env"
|
||||||
|
chmod 600 "${PICOCLAW_HOME}/config/.env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 9. Create config.json template ───────────────────
|
||||||
|
if [ ! -f "${PICOCLAW_HOME}/config/config.json" ]; then
|
||||||
|
log "Creating config.json template..."
|
||||||
|
cat > "${PICOCLAW_HOME}/config/config.json" <<'JSONEOF'
|
||||||
|
{
|
||||||
|
"agents": {
|
||||||
|
"defaults": {
|
||||||
|
"workspace": "/opt/picoclaw/workspace",
|
||||||
|
"restrict_to_workspace": true,
|
||||||
|
"model": "claude-sonnet-4-20250514",
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"max_tool_iterations": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"channels": {
|
||||||
|
"telegram": {
|
||||||
|
"enabled": false,
|
||||||
|
"token": "",
|
||||||
|
"allow_from": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"providers": {
|
||||||
|
"anthropic": {
|
||||||
|
"api_key": "",
|
||||||
|
"api_base": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heartbeat": {
|
||||||
|
"enabled": true,
|
||||||
|
"interval": 30
|
||||||
|
},
|
||||||
|
"gateway": {
|
||||||
|
"host": "0.0.0.0",
|
||||||
|
"port": 18790
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSONEOF
|
||||||
|
chown "${PICOCLAW_USER}:${PICOCLAW_USER}" "${PICOCLAW_HOME}/config/config.json"
|
||||||
|
chmod 600 "${PICOCLAW_HOME}/config/config.json"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 10. Install systemd service (for binary method) ──
|
||||||
|
if [ "${DEPLOY_METHOD}" = "binary" ]; then
|
||||||
|
log "Installing systemd service..."
|
||||||
|
cat > /etc/systemd/system/picoclaw.service <<SVCEOF
|
||||||
|
[Unit]
|
||||||
|
Description=PicoClaw AI Assistant Gateway
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
StartLimitIntervalSec=300
|
||||||
|
StartLimitBurst=5
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=${PICOCLAW_USER}
|
||||||
|
Group=${PICOCLAW_USER}
|
||||||
|
WorkingDirectory=${PICOCLAW_HOME}
|
||||||
|
ExecStart=${PICOCLAW_HOME}/bin/picoclaw gateway
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
StandardOutput=append:${PICOCLAW_HOME}/logs/picoclaw.log
|
||||||
|
StandardError=append:${PICOCLAW_HOME}/logs/picoclaw-error.log
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
EnvironmentFile=-${PICOCLAW_HOME}/config/.env
|
||||||
|
Environment=HOME=${PICOCLAW_HOME}
|
||||||
|
Environment=PICOCLAW_CONFIG=${PICOCLAW_HOME}/config/config.json
|
||||||
|
|
||||||
|
# Security hardening
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ReadWritePaths=${PICOCLAW_HOME}
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
SVCEOF
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable picoclaw
|
||||||
|
log "systemd service installed and enabled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 11. Create logrotate config ──────────────────────
|
||||||
|
cat > /etc/logrotate.d/picoclaw <<LOGEOF
|
||||||
|
${PICOCLAW_HOME}/logs/*.log {
|
||||||
|
daily
|
||||||
|
missingok
|
||||||
|
rotate 14
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
notifempty
|
||||||
|
create 0640 ${PICOCLAW_USER} ${PICOCLAW_USER}
|
||||||
|
postrotate
|
||||||
|
systemctl reload picoclaw 2>/dev/null || true
|
||||||
|
endscript
|
||||||
|
}
|
||||||
|
LOGEOF
|
||||||
|
|
||||||
|
# ── Done ─────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
log "=========================================="
|
||||||
|
log " Server setup complete!"
|
||||||
|
log "=========================================="
|
||||||
|
echo ""
|
||||||
|
log "Next steps:"
|
||||||
|
echo " 1. Edit API keys: nano ${PICOCLAW_HOME}/config/.env"
|
||||||
|
echo " 2. Edit config: nano ${PICOCLAW_HOME}/config/config.json"
|
||||||
|
if [ "${DEPLOY_METHOD}" = "docker" ]; then
|
||||||
|
echo " 3. Deploy: Run 'make deploy-hostinger' from your local machine"
|
||||||
|
else
|
||||||
|
echo " 3. Deploy: Run 'make deploy-hostinger' from your local machine"
|
||||||
|
echo " 4. Start service: systemctl start picoclaw"
|
||||||
|
echo " 5. Check status: systemctl status picoclaw"
|
||||||
|
echo " 6. View logs: tail -f ${PICOCLAW_HOME}/logs/picoclaw.log"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
log "Firewall ports open: SSH (22), PicoClaw Gateway (18790)"
|
||||||
|
echo ""
|
||||||
98
deploy/hostinger/status.sh
Executable file
98
deploy/hostinger/status.sh
Executable file
|
|
@ -0,0 +1,98 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================
|
||||||
|
# PicoClaw - Check Remote Server Status
|
||||||
|
# ============================================================
|
||||||
|
# Usage:
|
||||||
|
# ./deploy/hostinger/status.sh -h YOUR_VPS_IP
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log() { echo -e "${GREEN}[STATUS]${NC} $*"; }
|
||||||
|
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||||
|
|
||||||
|
HOST="${HOSTINGER_HOST:-}"
|
||||||
|
USER="${HOSTINGER_USER:-root}"
|
||||||
|
SSH_KEY="${HOSTINGER_SSH_KEY:-${HOME}/.ssh/id_rsa}"
|
||||||
|
SSH_PORT="${HOSTINGER_SSH_PORT:-22}"
|
||||||
|
METHOD="${HOSTINGER_DEPLOY_METHOD:-docker}"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-h|--host) HOST="$2"; shift 2 ;;
|
||||||
|
-u|--user) USER="$2"; shift 2 ;;
|
||||||
|
-k|--key) SSH_KEY="$2"; shift 2 ;;
|
||||||
|
-m|--method) METHOD="$2"; shift 2 ;;
|
||||||
|
-p|--port) SSH_PORT="$2"; shift 2 ;;
|
||||||
|
*) shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[ -z "${HOST}" ] && { echo "Usage: $0 -h HOST"; exit 1; }
|
||||||
|
|
||||||
|
SSH_OPTS="-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 -p ${SSH_PORT} -i ${SSH_KEY}"
|
||||||
|
SSH_CMD="ssh ${SSH_OPTS} ${USER}@${HOST}"
|
||||||
|
|
||||||
|
log "Checking PicoClaw status on ${HOST}..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
${SSH_CMD} <<REMOTEOF
|
||||||
|
echo "── System ──────────────────────────────────"
|
||||||
|
echo "Hostname: \$(hostname)"
|
||||||
|
echo "Uptime: \$(uptime -p)"
|
||||||
|
echo "Memory: \$(free -h | awk '/Mem:/ {print \$3 "/" \$2}')"
|
||||||
|
echo "Disk: \$(df -h / | awk 'NR==2 {print \$3 "/" \$2 " (" \$5 " used)"}')"
|
||||||
|
echo "Load: \$(cat /proc/loadavg | awk '{print \$1, \$2, \$3}')"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "── PicoClaw ────────────────────────────────"
|
||||||
|
if [ "${METHOD}" = "docker" ]; then
|
||||||
|
if command -v docker &>/dev/null; then
|
||||||
|
echo "Method: Docker"
|
||||||
|
docker compose -f /opt/picoclaw/docker-compose.yml ps 2>/dev/null || echo "Container: not running"
|
||||||
|
echo ""
|
||||||
|
echo "── Recent Logs ─────────────────────────────"
|
||||||
|
docker compose -f /opt/picoclaw/docker-compose.yml logs --tail=10 picoclaw-gateway 2>/dev/null || true
|
||||||
|
else
|
||||||
|
echo "Docker not installed"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Method: Binary (systemd)"
|
||||||
|
if systemctl is-active --quiet picoclaw 2>/dev/null; then
|
||||||
|
echo "Status: RUNNING"
|
||||||
|
version=\$(/opt/picoclaw/bin/picoclaw version 2>/dev/null || echo "unknown")
|
||||||
|
echo "Version: \${version}"
|
||||||
|
else
|
||||||
|
echo "Status: STOPPED"
|
||||||
|
fi
|
||||||
|
systemctl status picoclaw --no-pager -l 2>/dev/null || true
|
||||||
|
echo ""
|
||||||
|
echo "── Recent Logs ─────────────────────────────"
|
||||||
|
tail -10 /opt/picoclaw/logs/picoclaw.log 2>/dev/null || echo "No logs found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "── Health Check ────────────────────────────"
|
||||||
|
if curl -sf http://localhost:18790/health > /dev/null 2>&1; then
|
||||||
|
echo "Gateway: HEALTHY (port 18790)"
|
||||||
|
else
|
||||||
|
echo "Gateway: UNREACHABLE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "── Firewall ────────────────────────────────"
|
||||||
|
if command -v ufw &>/dev/null; then
|
||||||
|
ufw status 2>/dev/null | head -10
|
||||||
|
elif command -v firewall-cmd &>/dev/null; then
|
||||||
|
firewall-cmd --list-all 2>/dev/null | head -10
|
||||||
|
fi
|
||||||
|
REMOTEOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
log "Status check complete"
|
||||||
Loading…
Add table
Reference in a new issue