Migracija Nikita + Ana + WhatsApp MCP na jura-server

Kompletna seoba dva ClaudeClaw Telegram bota (Nikita za Juru, Ana za Zlatka Krešića) s glavnog Plesk servera na novu Plesk-less platformu. Plus duplicirani WhatsApp MCP bridge i multi-account Claude Code (3 accounta).

Datum: 2026-05-16 Trajanje: ~2 h aktivnog rada Downtime: ~5 min/bot Status: Done
TL;DR. Bots-prebačeni na jura-server (178.105.131.67), oba aktivna sa Memory v2 + cron + GWS/Himalaya credentials preserved. WhatsApp bridge paralelni na novom (drugi linked device, glavni ostao aktivan dok ne migrira first5labs ERP). Sve hardened (systemd ProtectSystem, ReadWritePaths trimmed, fail2ban, Hetzner FW). Otkriven + dokumentiran session-flush gotcha za buduće bot migracije.
Sadržaj
  1. Kontekst i ciljevi
  2. Arhitektura jura-servera
  3. Faze migracije (kronološki)
  4. Što je instalirano
  5. Bot infrastruktura
  6. Multi-account Claude Code
  7. WhatsApp MCP (paralelni bridge)
  8. Security posture
  9. Lessons learned / gotchas
  10. Sljedeći koraci
  11. Quick reference (komande + paths)

1. Kontekst i ciljevi

Glavni server server.2klika.eu hosta 19+ vhostova (Plesk panel, Apache/Nginx, PHP-FPM po verziji). Najteži klijent — first5labs ERP — raste, deserves vlastiti server. Jura je kupio drugi Hetzner. Cilj: clean split.

Ova migracija pokriva samo bots: Nikita (Jurin AI asistent), Ana (Zlatko Krešić, Jurina desna ruka), WhatsApp MCP. ERP migracija dolazi posebno (~30 min downtime, kasnu večer).

Zašto Plesk-less?

2. Arhitektura jura-servera

Internet (Hetzner Cloud Firewall: 6222, 80, 443 only) │ Caddy v2 ←─── /etc/caddy/Caddyfile + sites.d/*.caddy │ Auto Let's Encrypt SSL ┌────┼────┐ │ │ │ Docker Static Reverse-Proxy apps files to local port │ (/opt/vhosts/<slug>/public/) │ PostgreSQL / SQLite / etc. (per-vhost docker-compose) Bot infrastructure (systemd, isolated): /opt/nikita/ → @jura_nikita_bot (Jurin asistent) /opt/ana/ → @ana_zlatko_bot (Zlatkov asistent) /opt/whatsapp-mcp/ → bridge :8080 → MCP za Janu Flego
OS
Ubuntu 24.04.4 LTS
Hetzner default
CPU/RAM/Disk
— · 15 GB · 150 GB
5% korišteno (138 GB free)
SSH port
6222
ED25519 key-only, no password
Firewall
Hetzner Cloud
22, 80, 443 ONLY · UFW off

3. Faze migracije (kronološki)

4. Što je instalirano

ComponentVerzijaSourcePurpose
Ubuntu24.04.4 LTSBase OS
OpenSSHsystemaptSSH (key-only, port 6222)
fail2bansystemaptSSH brute-force protection
Caddy2.11.3cloudsmith stableWeb server + auto SSL
Docker CE29.5.0docker.comContainer runtime
Docker Composev5.1.3docker.comOrchestration
Node.js22.22.2NodeSourceJS runtime (botovi, gws)
Python3.12.3aptScripting + MCP servers
Go1.26.3/usr/local/goWhatsApp bridge (whatsmeow)
uv0.11.14astral.shPython tool installer
Claude Code2.1.143rsync from mainAI CLI
gws0.22.5@googleworkspace/cli (npm)Google services (Drive/Gmail/Calendar)
himalaya1.2.0binary (musl)IMAP/SMTP (business email)
graphify0.8.5uv tool install graphifyyKnowledge graph (Radionica integracija)
qrencodesystemaptWhatsApp QR generation

Shared resources

U /opt/claude-shared/ (640 MB) — read-only za botove:

5. Bot infrastruktura

BotOwnerTelegramClaude accountRAMService
NikitaJura Šandrk (First5Labs CEO)@jura_nikita_bot.claude-acc2 (Jurin Max)117 MBnikita.service
AnaZlatko Krešić (Jurina desna ruka)@ana_zlatko_bot.claude-acc2 (shared)190 MBana.service
WhatsApp bridgeJosip (za Janu Flego u first5labs)61 MBwhatsapp-bridge.service :8080

Po-bot izolacija (systemd hardening)

ProtectSystem=strict
PrivateTmp=true
NoNewPrivileges=true
RestrictSUIDSGID=true
ReadWritePaths=/opt/<bot> /root/.claude-acc2 /var/log /run
ReadOnlyPaths=/opt/claude-shared
InaccessiblePaths=/home /root/.ssh

# Per-bot environment
Environment=CLAUDE_CONFIG_DIR=/root/.claude-acc2
Environment=GOOGLE_WORKSPACE_CLI_CONFIG_DIR=/opt/<bot>/.config/gws
Environment=GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file
Environment=HIMALAYA_CONFIG=/opt/<bot>/.config/himalaya/config.toml
Environment=SKILLS_DIR=/opt/<bot>/skills

# Resources
MemoryMax=2G  MemoryHigh=1.5G  TasksMax=300

Cron entries

FileFrequencyŠto radi
/etc/cron.d/nikita-promo-cleanupevery 2hGmail promo cleanup (Jurin)
/etc/cron.d/nikita-updatedaily 04:00Claude Code SDK auto-update
/etc/cron.d/ana-updatedaily 04:00Claude Code SDK auto-update
/etc/cron.d/whatsapp-bridge-healthevery 30 minBridge health check (alert via Molly)

Migration data

Nikita
3 memories · 12 skills
681 MB · DB integrity ok
Ana
2 memories · 11 skills
770 MB · DB integrity ok

6. Multi-account Claude Code

Tri account-direktorija pripremljena na jura-serveru:

DirektorijAccountPurpose
/root/.claudeJurin (info@first5labs.eu)Default: Josipov interaktivni rad na jura
/root/.claude-acc2Jurin clone (info@first5labs.eu)Botovi (Nikita+Ana) pišu odavde — hardcoded u systemd
/root/.claude-acc3— (empty)Rezerva: Josipov osobni Gmail kad rate-limit hit (claude /login)

Sva tri imaju chmod 700 root:root. .credentials.json 600. .claude-acc2 ima 15 apsolutnih symlinkova na /root/.claude/ (agents, skills, hooks, plugins, commands, rules, docs, etc.) — shared infrastructure.

PATH sustavski dostupan preko /etc/profile.d/local-bin.sh (claude/graphify/uv u /root/.local/bin/).

7. WhatsApp MCP (paralelni bridge)

WhatsApp dozvoljava do 4 linked devices po accountu. Strategija: dupliciraj — glavni bridge ostane aktivan, jura-server postaje drugi linked device. Kad first5labs ERP migrira, glavni gasimo, jura nastavlja.

Paired: 38598781506:89@s.whatsapp.net — Josipov broj kao 89. linked device. Bridge sync-ao history kontakata + chatova (DevOps Club, CroAI general, Product Club, Slanica, OG 2 zajebancija, …).

Stack

8. Security posture

Backup: Hetzner Cloud Backup enabled (~€7/mo). Plus per-project daily DB dumps. Migration secrets backup u /root/migration-secrets-2026-05-16/ (chmod 600).

9. Lessons learned / gotchas

Gotcha 1: tsx u devDependencies, koristi se u runtime. Bot ExecStart=/usr/bin/node --import tsx src/index.ts ne radi s npm install --omit=dev. Treba full npm install (uključuje tsx). Memory action item: premjestiti tsx u dependencies u package.json botova.
Gotcha 2: mcp-image-gen bez requirements.txt. Python venv ima deps install-ane direktno (bez pinned requirements). Workaround: pip freeze > requirements.txt na glavnom, scp na novi, regen venv. Memory action: dodati requirements.txt u repo.
Gotcha 3: systemd InaccessiblePaths sadrži non-existent paths. Bot systemd na glavnom referencira /var/www/vhosts i /root/.claude-walle — ne postoje na jura. Trim za jura-server: InaccessiblePaths=/home /root/.ssh (samo realno postojeći).
Gotcha 4 (KRITIČNO): Stale Claude session IDs nakon migracije. Bot pamti Claude Code session ID per chat u SQLite. Pri migraciji DB se kopira (session IDs preservane), ALI .claude-acc2/projects/ i sessions/ ne migriraju (ephemeral state). Prva poruka u svakom chatu pada s "No conversation found with session ID". Bot kaže "Session cleared" ali poruku ne odgovara. Fix: sqlite3 /opt/<bot>/data/<bot>.db "DELETE FROM sessions;" PRIJE prvog start na novom serveru. Dokumentirano u memory reference_bot_migration_session_flush.md.
Lesson: WhatsApp dozvoljava 4 linked devices. Paralelne bridge instalacije su moguće (svaka sa svojim QR scanom). Različite session DB-ovi, isti broj. Idealno za zero-downtime migracije.
Lesson: Per-server CLAUDE.md prilagodba. Glavni CLAUDE.md ima Plesk-specifične sekcije (System Users tablica, Project Routing s 19+ vhostova). Na jura-server-u to ne vrijedi — drugačiji stack (Caddy + Docker + bash). Rješenje: server-specifični /root/CLAUDE.md (591 linija) koji NASLJEDUJE pravila s glavnog (RULES #-1, #0, #0b, LEGO, Radionica) ali ima vlastiti context (vhost framework, bot infrastructure, multi-account).

10. Sljedeći koraci

TaskTrajanjeDowntimeKada
Smoke test — /start oba bota + WhatsApp Jan ping5 minSad (Josip)
first5labseu migracija (static Astro site)~20 min0Bilo kada
first5labs ERP migracija (CRITICAL)~30 min~30 minSubotom ujutro ili kasnu večer
DNS TTL 300s na MyDataKnox za first5labs.eu5 min024h PRIJE first5labs migracije
Telegram alerts setup (/root/secrets/telegram-alerts.env)5 min0Asap (za health checks)
Disable WhatsApp bridge na glavnom (nakon first5labs migracije)1 min~10 sec (rare)Nakon first5labs
/opt/server-config/ → git remote (off-server backup)15 min0Bilo kada

11. Quick reference

Operativne komande

# SSH iz glavnog servera
ssh jura-server

# Bot management
systemctl status|restart|stop nikita | ana | whatsapp-bridge
journalctl -u nikita -f

# DB integrity (bilo kada)
sqlite3 /opt/nikita/data/nikita.db "PRAGMA integrity_check;"

# WhatsApp
whatsapp-allowlist list | add <jid> | remove <jid>

# Vhost
add-vhost <domain> static|proxy|docker [opts]
list-vhosts
remove-vhost <slug> [--keep-data]

# Logs
journalctl -u caddy -f
tail -f /var/log/caddy/<slug>.access.log | jq
fail2ban-client status sshd

Bitne lokacije

PathŠto
/root/CLAUDE.mdServer-specific Claude rules (591 linija)
/opt/server-config/Vhost framework + monitoring (git, source na glavnom)
/opt/vhosts/<slug>/Per-vhost root (public/, logs/, tmp/, data/)
/opt/nikita/, /opt/ana/, /opt/whatsapp-mcp/Botovi
/opt/claude-shared/Skills/hooks/memory shared (read-only za botove)
/root/.claude{,-acc2,-acc3}/Multi-account Claude Code
/root/secrets/API keys, Telegram tokens (chmod 700)
/root/migration-secrets-2026-05-16/Migration .env backups (chmod 600)
/etc/caddy/sites.d/Per-vhost Caddy blocks (auto-managed)
/etc/systemd/system/nikita.service, ana.service, whatsapp-bridge.service
/etc/cron.d/nikita-promo-cleanup, *-update, whatsapp-bridge-health
/var/log/caddy/Caddy access logs (JSON, rotated)