WMS Agile
Enterprise 4PL Warehouse Management System
Scheda Tecnica v2026.1

Architettura e Specifiche Tecniche

Microservizi PHP 8.4, API REST JWT, real-time SSE, AI integrata, Docker su Hetzner — tutto progettato per scalabilità e sicurezza enterprise.

10
Microservizi
PHP 8.4
Runtime
MySQL 8.0
Database
JWT HS256
Autenticazione
SSE
Real-time
Docker
Container

Pattern a Microservizi

WMS Agile è costruito su un'architettura a microservizi indipendenti comunicanti via REST, coordinati da un API Gateway Nginx. Ogni servizio ha il suo database MySQL isolato.

┌─────────────────────────────────────────────────────────────────────────────┐ │ INTERNET / CLIENT BROWSER │ └─────────────────────────┬───────────────────────────────────────────────────┘ │ HTTPS :443 ┌─────────────────────────▼───────────────────────────────────────────────────┐ │ Apache2 HOST (SSL Termination) │ │ wms.agile.software → Let's Encrypt TLS 1.3 │ └─────────────────────────┬───────────────────────────────────────────────────┘ │ HTTP :3080 ┌─────────────────────────▼───────────────────────────────────────────────────┐ │ WMS-GATEWAY (Nginx) :3080 │ │ SPA frontend app/ + Reverse Proxy routing per tutti i microservizi │ │ SSE: proxy_buffering off │ CORS headers │ gzip │ health checks │ └────────┬────────┬────────┬──┴────┬────────┬────────┬──────┴──────────────────┘ :3081 :3082 :3083 :3084 :3085 :3086 :3087 auth-ms inv-ms wh-ms in-ms out-ms rep-ms ai-ms auth_db inv_db wh_db in_db out_db rep_db wh_db+inv_db │ Claude API Redis :6399 RabbitMQ :5692

Tecnologie e Versioni

Stack moderno, stabile e battle-tested. Nessuna dipendenza da framework pesanti o vendor lock-in.

Backend
PHP 8.4 Slim Framework 4 MySQL 8.0 InnoDB Redis 7 RabbitMQ 3.13 JWT HS256 (firebase/php-jwt) Composer 2 PDO + Prepared Statements
Frontend
Vanilla JS ES2022 Chart.js 4.4.7 Font Awesome 6.5.1 marked.js 12 EventSource (SSE) i18n 16 lingue PWA palmare @media print CSS
Infrastruttura
Docker + Compose Nginx 1.27 (gateway) Apache 2.4 (servizi PHP) Let's Encrypt TLS 1.3 Hetzner Cloud CX22 Gitea (self-hosted) Limits: 256MB / servizio
ComponenteTecnologiaVersioneRuolo
API GatewayNginx1.27Reverse proxy, SSL passthrough, routing regex, gzip, SSE buffering
Microservizi PHPPHP + Slim 4 + Apache 2.48.4 / 4.xBusiness logic, REST API, auth middleware, CORS
Database primarioMySQL 8.0 InnoDB8.07 database separati (1 per servizio), utf8mb4, prepared statements
CacheRedis7.xSessioni, rate limiting, cache query (predisposto)
Message brokerRabbitMQ3.13Event bus asincrono (predisposto per integrazione ERP/webhook)
AI engineAnthropic Claude APIclaude-sonnet-4-6Function calling, SSE streaming, tool execution WMS
Frontend runtimeVanilla JSES2022SPA no-framework, IIFE modules, fetch API, EventSource

10 Microservizi Indipendenti

Ogni microservizio è un container Docker autonomo con il suo database, dipendenze Composer e processo Apache dedicato. Comunicazione intra-servizi via HTTP interno (rete Docker).

SERVIZIOPORTA HOSTRESPONSABILITÀ
wms-gateway3080Nginx: SPA frontend + reverse proxy API. SSL termination, gzip, routing regex, SSE passthrough
wms-auth-ms3081Autenticazione, utenti, ruoli, JWT, sessioni, multi-tenancy. DB: wms_auth_db
wms-inventory-ms3082Prodotti SKU, giacenze, lotti FEFO, movimenti, parcel tracking. DB: wms_inventory_db
wms-warehouse-ms3083Struttura magazzino, zone, bin, missioni, equipment, operatori, TMS, NCR, SSE realtime. DB: wms_warehouse_db
wms-inbound-ms3084Ordini in entrata, ricevimento fisico, quality check, put-away task. DB: wms_inbound_db
wms-outbound-ms3085Ordini uscita, wave planning, picking, packing, spedizione. DB: wms_outbound_db
wms-report-ms3086Dashboard KPI, snapshot storici, analytics, report export. DB: wms_report_db
wms-ai-ms3087Claude API integration, function calling WMS, SSE streaming chat. Accede a wms_warehouse_db + wms_inventory_db
wms-calendar-ms3088Festività, turni, orari operativi, chiusure programmate. DB: wms_warehouse_db
wms-wcs-ms3089Warehouse Control System, comunicazione MQTT AGV/robot, automazioni. DB: wms_warehouse_db
wms-network-ms3090Rete Filiera, Procure-to-Pay, partner network, EDI multi-partner. DB: wms_agile_db
Struttura di ogni servizio
services/{name}-ms/
├── Dockerfile
├── composer.json
├── env.production.example
├── public/
│   └── index.php      # Routes Slim 4 + middleware
└── src/
    └── Services/
        └── {Name}Service.php  # Business logic
Shared Libraries

Librerie condivise montate come volume read-only in ogni container:

shared/
├── auth-lib/           # AuthMiddleware, JwtHelper
├── database-lib/       # Database::getConnection()
└── event-bus-lib/      # RabbitMQ EventBus

Namespace: Wms\Shared\Auth\Wms\Shared\Database\

Schema Multi-Database

Ogni microservizio ha il suo database MySQL dedicato. L'isolamento previene query cross-service accidentali e permette backup/restore indipendenti.

DatabaseServizio OwnerTabelle Principali
wms_auth_dbauth-msusers, tenants, roles, user_roles, sessions, api_keys
wms_inventory_dbinventory-msproducts, stock_levels, lots, stock_movements, parcel_tracking, parcel_scan_events
wms_warehouse_dbwarehouse-mswarehouses, zones, bins, missions, equipment, operators, alerts, companies, drivers, delivery_runs, delivery_manifest_items, non_conformities, ncr_events, stock_adjustments
wms_inbound_dbinbound-msinbound_orders, inbound_order_items, quality_checks, putaway_tasks
wms_outbound_dboutbound-msoutbound_orders, order_items, picking_tasks, packing_tasks, waves
wms_report_dbreport-msdaily_kpi_snapshots, reports
wms_agile_dbmeta/sharedcross-service references (opzionale)
Convenzioni di sicurezza DB
  • PDO Prepared Statements su tutte le query — zero SQL injection
  • tenant_id su ogni tabella — isolamento dati multi-cliente
  • Soft deletedeleted_at TIMESTAMP NULL + filtro IS NULL
  • utf8mb4 — charset completo con emoji e caratteri CJK
  • InnoDB — transazioni ACID su tutte le tabelle
  • Indici su tenant_id, status, created_at per ogni tabella
Struttura risposta API standard
// Successo
{
  "success": true,
  "data": { ... } | [ ... ],
  "meta": { "total": 42, "page": 1 }
}

// Errore
{
  "success": false,
  "error": "Descrizione errore",
  "code": 404
}

Endpoint Principali

Tutte le route autenticate richiedono header Authorization: Bearer {JWT}. Le route pubbliche sono indicate con [PUBLIC].

Auth — /api/auth
POST/api/auth/login[PUBLIC]
POST/api/auth/logout
GET/api/auth/me
GET/api/users
POST/api/users
GET/api/tenants
Inventory — /api/products, /api/stock
GET/api/products
POST/api/products
PUT/api/products/{id}
GET/api/stock
GET/api/lots
GET/api/movements
GET/api/tracking/public/{num}[PUBLIC]
GET/api/tracking
POST/api/tracking/{num}/scan
Warehouse — /api/warehouses, /api/zones, /api/bins
GET/api/warehouses
GET/api/zones
GET/api/bins
GET/api/missions
POST/api/missions
GET/api/equipment
GET/api/dashboard-summary
GET/api/realtime/stream?token=JWT[SSE]
GET/api/drivers
GET/api/delivery-runs
GET/api/companies
GET/api/ncr
AI — /api/ai
POST/api/ai/chat[SSE stream]
GET/api/ai/tools
Chat body:
{
  "message": "Quanti ordini oggi?",
  "session_id": "uuid",
  "history": [...],
  "stream": true
}
AI Tools disponibili: list_missions, get_dashboard_summary, list_products, create_mission, configure_bin, configure_zone, list_non_conformities, create_ncr, get_tracking_summary, list_delivery_runs

Server-Sent Events (SSE)

WMS Agile usa SSE (non WebSocket) per aggiornamenti server→client. Più semplice, compatibile con tutti i proxy HTTP/2, e nativo nel browser.

SSE Dashboard — /api/realtime/stream
  • 🔑 Auth via ?token=JWT (EventSource non supporta headers)
  • ⏱ Tickrate: 4 secondi per evento
  • ⏰ Durata max: 120 secondi poi riconnessione automatica
  • 📡 4 event type: heartbeat, missions, alerts, equipment
  • 🔄 Client riconnette con backoff esponenziale (max 30s)
// nginx: proxy_buffering off
// PHP: header('X-Accel-Buffering: no')
// PHP: flush() dopo ogni evento

event: missions
data: {"count":3,"items":[...]}

event: alerts
data: {"critical":1,"high":2,"total":3}
SSE AI Chat — /api/ai/chat
  • 📡 Streaming Anthropic Claude claude-sonnet-4-6
  • 🔧 Function calling per 10 WMS tools
  • 🔒 Auth via Authorization: Bearer JWT
  • ⏱ Timeout proxy: 120 secondi
  • 📝 Formato: chunk di testo progressivi
// Client JS riceve:
data: {"type":"delta","text":"I KPI di "}
data: {"type":"delta","text":"oggi sono..."}
data: {"type":"tool_call","name":"get_dashboard"}
data: {"type":"tool_result","data":{...}}
data: {"type":"done"}

Modello di Sicurezza Enterprise

Progettato per deployment enterprise con multi-tenancy completa, autenticazione JWT, HTTPS end-to-end e audit trail su tutti i dati.

Autenticazione JWT HS256
Login con email/password → JWT con scadenza 8h. Signature con secret a 256-bit hex. Il token include user_id, tenant_id, role. Ogni richiesta API valida il JWT nel AuthMiddleware.
Multi-tenancy completa
Il tenant_id estratto dal JWT viene applicato a TUTTE le query SQL come filtro WHERE obbligatorio. È impossibile accedere ai dati di un altro tenant anche con JWT valido.
SQL Injection Prevention
100% PDO Prepared Statements su tutte le query. Zero concatenazione di input utente in SQL. Input validato e tipizzato prima di ogni operazione DB.
HTTPS End-to-End
TLS 1.3 con Let's Encrypt su wms.agile.software. Certificato rinnovato automaticamente. HSTS header per impedire downgrade ad HTTP.
Network Isolation
I container comunicano solo sulla rete Docker interna wms-network. Le porte dei servizi PHP sono bound a 127.0.0.1 (non esposte pubblicamente). Solo il gateway :3080 è raggiungibile dall'esterno.
RBAC — Role Based Access
Tre ruoli: admin (pieno accesso), supervisor (senza admin utenti), operatore (solo operazioni, no configurazione). Il frontend mostra solo i portali e le funzioni autorizzate per il ruolo.
Audit Trail
Ogni NCR ha una timeline di eventi (ncr_events). I movimenti stock sono immutabili. Il log Apache registra ogni request con IP, timestamp e response code.
Secret Management
Secrets in .env.production (gitignored). JWT_SECRET generato con openssl rand -hex 32. API_KEY con openssl rand -hex 32. ANTHROPIC_API_KEY mai committata in repo.

Deploy e Operatività

Deployment su Hetzner Cloud con Docker Compose, 11 container in produzione, healthcheck su ogni servizio.

Server Hetzner CX22
  • 🖥 vCPU: 2 core AMD
  • 💾 RAM: 4 GB
  • 💿 Disco: 40 GB NVMe SSD
  • 🌐 IP: 135.181.149.254
  • 📍 Region: Falkenstein EU
  • 🔒 Accesso: SSH key-only, no password
  • 📦 OS: Ubuntu 24.04 LTS
Container in produzione
  • ✓ wms-gateway (nginx)
  • ✓ wms-auth-ms
  • ✓ wms-inventory-ms
  • ✓ wms-warehouse-ms
  • ✓ wms-inbound-ms
  • ✓ wms-outbound-ms
  • ✓ wms-report-ms
  • ✓ wms-ai-ms
  • ● wms-mysql (porta 3326)
  • ● wms-redis (porta 6399)
  • ● wms-rabbitmq (porta 5692)
Healthcheck

Ogni container ha healthcheck Docker:

HEALTHCHECK \
  --interval=30s \
  --timeout=3s \
  --start-period=10s \
  --retries=3 \
  CMD curl -f http://127.0.0.1/health

Tutti i servizi devono rispondere GET /health → 200 JSON

Comandi deploy
# Full deploy (build + start)
./infrastructure/deploy-hetzner.sh --full

# Solo rebuild immagini
./infrastructure/deploy-hetzner.sh --build

# Verifica stato
./infrastructure/deploy-hetzner.sh --verify

# Rebuild singolo servizio
docker build -t wms-agile-{svc} -f services/{svc}/Dockerfile .
docker stop wms-{svc} && docker rm wms-{svc}
docker run -d --name wms-{svc} --network wms-network \
  --env-file /var/www/wms-agile/services/{svc}/.env.production \
  -p 127.0.0.1:{PORT}:80 --restart unless-stopped --memory 256m \
  wms-agile-{svc}

Caratteristiche di Scalabilità

API Response
Target: <200ms per query semplici su dati operativi. I report storici con aggregazione possono arrivare a 500ms su volumi grandi.
SSE Latency
Aggiornamenti dashboard ogni 4 secondi. Latency evento dal DB al browser: tipicamente <500ms incluso tick sleep.
Memory Limits
Ogni container PHP ha limit 256 MB RAM. MySQL 512 MB. Limite totale stimato: ~2.5 GB per tutti i container.
Scalabilità
Architettura orizzontalmente scalabile. Ogni microservizio può essere replicato con load balancer Nginx upstream. MySQL sostituibile con cluster.

Claude AI — Function Calling WMS

L'AI ha accesso diretto ai dati del magazzino tramite 10 tools strutturati. Ogni tool è una query PHP sicura al DB con tenant isolation.

WMS Tools disponibili
ToolDescrizione
get_dashboard_summaryKPI operativi correnti
list_missionsLista missioni filtrate per stato/tipo
create_missionCrea nuova missione operativa
list_productsProdotti con stock e classe ABC
configure_binModifica attributi di una ubicazione
configure_zoneModifica attributi di una zona
list_non_conformitiesNCR filtrate per stato/gravità
create_ncrApre nuova non conformità
get_tracking_summaryStato spedizione per numero tracking
list_delivery_runsGiri consegna con manifesto
Flusso di elaborazione
Browser → POST /api/ai/chat
  ↓ (stream: true)
ai-ms → Anthropic API
  ↓ function_calling
Claude seleziona tool
  ↓
WmsTools::execute($tool, $input)
  ↓ PDO query su wms_*_db
  ↓ risultato → Claude
  ↓ risposta narrativa
SSE stream → Browser
  (chunk per chunk)

Il tenant_id viene estratto dal JWT nel middleware prima di passare il controllo all'AI. Claude non può mai accedere a dati di altri tenant.

RAG Pipeline — Voyage AI + Qdrant

Retrieval-Augmented Generation per-tenant: ogni azienda ha la propria knowledge base vettoriale isolata. L'AI risponde con contesto aziendale reale (SOP, manuali, procedure) invece di sole conoscenze generali.

Stack RAG
ComponenteTecnologiaDettaglio
Embedding modelvoyage-3-lite512 dim · cosine · Voyage AI API
Vector DBQdrantContainer Docker · port 6333 interno
Collection namingwms_kb_{tenant_id}Isolamento dati per tenant
Chunking250 parole · overlap 40~350 token/chunk · contesto preservato
Score threshold0.35 cosine similaritySolo chunk rilevanti al contesto
IndicizzazioneBatch · 8 testi/callRispetta 3 RPM free tier Voyage AI
Flusso RAG in chat
Utente → POST /api/ai/chat
  ↓ query dell'utente
IngestionService::search()
  ↓ embed query (voyage-3-lite)
Qdrant similarity search
  ↓ top-4 chunk con score ≥ 0.35
Contesto iniettato nel prompt Claude
  ↓ "Fonti aziendali: ..."
Claude Sonnet 4.6 risponde
  ↓ con contesto reale tenant
SSE stream → Browser

Se Qdrant non ha chunk per il tenant (knowledge base vuota), Claude risponde con conoscenza generale senza errori.

Endpoint indicizzazione
POST/api/ai/knowledge/ingest
Indicizza un singolo documento (id, title, content, source)
POST/api/ai/knowledge/seed-wms
Auto-indicizza 15 moduli WMS in batch (idempotente)
GET/api/ai/knowledge/stats
Statistiche collection: chunk totali, status Qdrant
Contenuto pre-indicizzato
15 moduli WMS · 36 chunk · ~12.600 token

Dashboard · Anagrafica Prodotti · Giacenze & Stock · Magazzino · Ricezione · Spedizioni · Missioni · Conformità · Risorse · 4PL/Billing · TMS · Tracking · Report & Analisi · AI Chat · AI Settings · Calendario · Simulatore

✅ 17 moduli indicizzati in produzione (Mar 2026)
Sicurezza
• Qdrant porta 6333 NON esposta all'host — solo rete Docker interna

• tenant_id estratto da JWT → collection isolata per cliente

• VOYAGE_API_KEY a livello sistema (infrastruttura), ANTHROPIC_API_KEY override per-tenant via admin panel

• doc_id deterministico (md5) → re-index idempotente