./run kochab-infrastruttura-ai-guardian
Kochab: costruire fiducia in codice — la stella che non cancella mai
Diario tecnico di Kochab: agente outbound-only in Go, audit di sicurezza mappato CIS/NIST, task firmati Ed25519 e una regola — non cancella mai — garantita in quattro modi indipendenti.
- status
- draft
- project
- kochab
- updated
- 2026-07-07
- tags
Kochab è la stella in punta al Piccolo Carro — quella che indica il nord, ferma mentre tutto ruota. Il nome è una promessa: un punto fermo che veglia sui tuoi server. Ma un tool che gira con privilegi su tutte le tue macchine è utile solo se ti fidi. Questo diario parla di come la fiducia si scrive in codice.
Il problema
Chi gestisce una manciata di server vive in una zona scomoda: troppo piccolo per i tool enterprise, troppo grande per controllare a mano. La sicurezza è la prima a scivolare — chiavi SSH permissive, firewall lasciato aperto “temporaneamente” due anni fa, DKIM/SPF/DMARC mai sistemati.
Architettura: tre pezzi, una separazione netta
- Agente (Go, un binario statico da ~9.7 MB,
.debda 4.6 MB): gira su ogni host sotto systemd. Solo in uscita — nessun DB locale, nessuna UI, nessuna porta in ascolto. Ilgo.modha una sola dipendenza diretta. Superficie d’attacco minima. - Platform (Go): API + worker fuori banda. Possiede l’inventario, il motore di audit, la firma dei task. PostgreSQL è l’unica fonte di verità.
- App: PWA React 19 / Vite 8 / Tailwind 4 che disegna la fleet come costellazione.
Perché l’agente chiama casa, e non il contrario
Ho scartato SSH-exec e agenti in ascolto. L’agente fa long-polling HTTP, ispirato ai tunnel Cloudflare — “il demone chiama casa, mai il contrario”:
GET /v1/tasks:204= niente da fare, riconnetti;200= un task;410 Gone= nodo dismesso, l’agente smette di pollare per sempre.- Timeout client 90s, apposta più lungo dei 60s di attesa lato server.
- TLS minimo 1.3, autenticazione richiesta via HMAC-SHA256 sul body.
- Loop
poll → verifica firma → esegui → riporta, backoff esponenziale 2s→60s.
I task sono firmati Ed25519 dalla platform e verificati dall’agente prima di eseguire. Il messaggio canonico è identico ai due lati:
// "taskID|taskType|hex(sha256(payload))|RFC3339(timestamp)"
msg := fmt.Sprintf("%s|%s|%x|%s", taskID, taskType,
sha256.Sum256(payload), timestamp.UTC().Format(time.RFC3339))
sig := ed25519.Sign(privKey, msg)
Sottigliezza che mi è costata un bug: il timestamp firmato deve coincidere al secondo con il created_at salvato, o la verifica salta.
Il motore di audit
Dieci controlli nel seed iniziale, ognuno mappato a CIS e NIST: SSH solo a chiave (CIS-5.3.4 / AC-7), firewall default-deny, TLS ≥1.2, permessi su shadow, fail2ban, servizi non esposti, DKIM/SPF/DMARC, backup presente, Docker senza host-network. Ogni regola è un file Go che si auto-registra: una regola nuova = un file nuovo, nessuno switch cablato.
type CheckFunc func(ctx context.Context) (passed bool, context map[string]any, err error)
Le precondizioni (binario assente) sono modellate come pass-con-contesto, non errori, così il report resta sensato su host eterogenei. Il check SSH è il più raffinato: concatena sshd_config + i .d/*.conf nell’ordine effettivo, sonda ogni porta configurata per capire se è esposta pubblicamente, e passa quei segnali al calcolo di severità.
Onestà da diario: nel codice spedito non c’è ancora un LLM. È tutto deterministico. L’AI è progettata per stare fuori dal loop di esecuzione — genererà nuove regole di audit che vengono poi “cristallizzate” nella stessa forma deterministica e approvate da un umano. Il ragionamento: “90% di accuratezza per passo = 59% di successo su 5 passi”. Quindi la logica sta nel codice, l’LLM al massimo scrive regole, mai tocca un host vivo.
”Non cancella mai” — garantito in quattro modi
Questa è la parte di cui vado più fiero, perché è fiducia resa meccanica:
- I nodi sono solo soft-deleted.
DELETE /v1/nodes/{id}valida la ragione contro un’allowlist e fa unUPDATE removed_at = now(), mai unDELETESQL. - Una guardia CI impone il filtro. Uno script fallisce la build se una query sui nodi non ha
removed_at IS NULL. Esiste perché quattro veri bug “nodo zombie” (un nodo rimosso che si rianimava) erano sfuggiti. - L’audit trail è fisicamente immutabile, in Postgres, non nel codice:
CREATE FUNCTION prevent_audit_trail_mutation() RETURNS trigger AS $$
BEGIN
RAISE EXCEPTION 'audit_trail is append-only: % operations are forbidden', TG_OP;
END; $$ LANGUAGE plpgsql;
CREATE TRIGGER audit_trail_immutable_delete BEFORE DELETE ON audit_trail
FOR EACH ROW EXECUTE FUNCTION prevent_audit_trail_mutation();
- I finding si risolvono, non si cancellano. E i fix non vengono mai eseguiti dal sistema: sono resi come shell copia-incolla per un umano.
C’è anche un’asimmetria che mi piace: il fix “disabilita password SSH” è fail-closed — resta nascosto se un preflight non prova che esiste già una chiave funzionante (per non chiuderti fuori). Ogni altra regola è fail-open, così una sonda ballerina non nasconde mai un fix legittimo.
La costellazione
La fleet è un campo di stelle. Due funzioni pure, testate: una piazza i nodi in modo radiale deterministico, l’altra mappa (stato, severità, ultimo battito) → stato visivo, dove la raggiungibilità vince sulla severità (un nodo irraggiungibile è offline, punto). Accessibilità di serie: target da 44×44px, aria-label in italiano, attivazione con Enter/Spazio, animazione disattivata con prefers-reduced-motion. E una vista di trasparenza che mostra all’operatore esattamente cosa l’agente ha trasmesso — perché raccogliamo solo metadati, mai contenuti di log.
Gotcha onesti
- Drift README/realtà: la doc dice “Watermill per gli eventi”, il codice fa Postgres
LISTEN/NOTIFY+ polling TanStack. Watermill è rimandato post-MVP. Meglio dirlo. - Regole in doppia rappresentazione (Go + seed SQL) create un rischio di disallineamento, mitigato solo da un grep in CI. Tassa di manutenzione accettata per tenere i metadati CIS nel DB.
Come va
Corro verso l’MVP. I tre pezzi compilano, le immagini sono a release candidate, e lo testo sulle mie macchine reali — il modo migliore per capire se una stella-guardiana funziona è farla vegliare su qualcosa che, se salta, mi rovina la giornata.
Codice: github.com/vcnngr/kochab-agent · install via get.kochab.ai. Agente Apache-2.0, platform BSL.