multi platform support#2
Conversation
Aggiunge guardian-shim — un binario C installato come symlink nella shim directory (~/.guardian/shims/) — per intercettare i comandi shell eseguiti da qualsiasi agente AI, incluso Claude Code (Hardened Runtime). - internal/shim/: package Go con ShimDir, PrependPath, CreateSymlinks, Install - internal/shim/csrc/guardian_shim.c: binario shim con safe failure = block - guardian run: prepende shim dir al PATH e imposta GUARDIAN_SHIM_DIR - guardian init: installa shims se guardian-shim è già compilato - Makefile: aggiunge target shim, integra in all e integration-test
Aggiunge il package internal/launchagent per registrare guardian come LaunchAgent macOS (~/Library/LaunchAgents/com.guardian.daemon.plist). - guardian init: installa il LaunchAgent dopo init (avvio al login, KeepAlive) - guardian uninstall: ferma il daemon, rimuove plist e hook shell - GeneratePlist: stdout/stderr su ~/.guardian/daemon.log e daemon-error.log
Quando la policy dice ask, il daemon mostra un dialog macOS con 4 opzioni: - Blocca: blocca questa volta - Consenti: consenti solo questa volta - Sessione: consenti per tutta la sessione (in-memory) - Sempre: scrive regola allow permanente nella policy.yaml Aggiunge policy.AppendAllowRule per inserire regole allow al volo. Il daemon non restituisce mai "ask" grezzo al client (safe failure = block).
Sostituisce il dialog runtime (osascript) con un wizard al momento dell'init. L'utente sceglie una volta per ogni regola pericolosa — la scelta viene scritta nella policy.yaml. A runtime il daemon è deterministico: ask=block. - internal/wizard: wizard con domande, ParseAnswer, Run() - internal/policy: aggiunge policy.Save() - guardian init: esegue wizard, aggiorna policy, flag --yes per skip - internal/prompt: rimosso osascript, mantenuta solo SessionAllowlist - internal/daemon: ask a runtime = block (deterministico)
Logo Fortify in cyan/bold, progress bar per ogni step, icone per regola, dettaglio del rischio, feedback colorato inline (rosso=bloccato/verde=consentito) e riepilogo finale. Aggiunge StripANSI per test.
- guardian policy list: tabella colorata con tutte le regole e decisioni - guardian policy toggle <id>: inverte block↔allow con feedback inline - guardian policy add: wizard interattivo per nuova regola - guardian policy remove <id>: rimozione con conferma Chiude il Cycle 1 MVP.
README spiega architettura, installazione, utilizzo e tutti i comandi. guardian help mostra logo Fortify con tutti i comandi organizzati per sezione.
Merge develop → main. Chiude il Cycle 1. Funzionalità incluse: - Policy engine YAML con glob/regex e first-match-wins - Command normalizer (shell/git/file classification) - Audit logger JSONL con filtri - Unix socket daemon con policy evaluation e live logging - Shell hook injector (zshrc/bashrc, idempotente) - DYLD_INSERT_LIBRARIES injection via C library (INTERPOSE) - PATH shims agent-agnostic (funziona con Claude Code Hardened Runtime) - LaunchAgent macOS (avvio automatico daemon al login) - Wizard di configurazione policy con UI colorata (ASCII art, progress bar) - CLI completa: init, start, run, logs, doctor, policy, uninstall, help - guardian policy list/toggle/add/remove - README
- Aggiunge internal/sandbox con SandboxManager (docker run wrapper, resolveDockerBinary per path fissi macOS, BuildDockerArgs) - Routing automatico: decision sandbox nel daemon avvia Docker e restituisce output + exit code al shim - Aggiunge guardian sandbox run con flag --image e --network - SandboxConfig per regola nel YAML (image, network) - Path rewriting: path host riscritti in /workspace nel container - Shim C: gestisce risposta sandbox con header [⬡ sandbox] e motivo - Shim C: invia work_dir reale via getcwd(), mostra reason nei blocchi - python e python3 aggiunti a ShimmedCommands - Audit Event: campi sandboxed, sandbox_image, sandbox_exit_code - doctor: check Docker installato + daemon in esecuzione - guardian init: messaggio source .zshrc solo alla prima installazione - default_policy.yaml: regole sandbox_python_scripts, sandbox_shell_scripts - README e CLAUDE.md aggiornati con stato Ciclo 2
- Aggiunge .guardian.yaml per configurazione sandbox per-progetto (default_image, default_network, mounts, env) - MergeConfig: regola > profilo progetto > default globale - ExtraMounts e Env nel Config per mount aggiuntivi e variabili - Mount read-only supportati (:ro) via ProfileMount.Readonly - Label guardian.sandbox=true su tutti i container - Manager.Reset: ferma container sandbox attivi via label filter - Aggiunge guardian sandbox reset al CLI
- Repo GitHub rinominata: agent-guardian → night-agent - Modulo Go: github.com/pietroperona/night-agent - Binario di output: night-agent (Makefile) - Aggiornati tutti gli import path Go - README e CLAUDE.md aggiornati con nuovo nome - .gitignore aggiunto /night-agent
- Tutti i path aggiornati: ~/.guardian → ~/.night-agent - Socket: guardian.sock → night-agent.sock - LaunchAgent label: com.guardian.daemon → com.night-agent.daemon - Profilo progetto: .guardian.yaml → .night-agent.yaml - Migrazione automatica in guardian init: sposta ~/.guardian → ~/.night-agent se esiste e la nuova directory non è ancora presente - README e CLAUDE.md aggiornati
…n usa variabili d'ambiente nei hook
- Makefile: target build/build-staging/build-dev con endpoint hardcodato - cloudconfig: defaultEndpoint da const a var, sempre dal valore compilato - cloud.go: registerSigningKey invia chiave firma al backend dopo connect - test: aggiornato per nuovo comportamento endpoint
- server.go: ActionType ora settato da action.Type nel normalizer - guardian_shim.c: agent_name legge NIGHTAGENT_AGENT env var (fallback "shim") - run.go: setta NIGHTAGENT_AGENT=<agente> nell'env del processo figlio
- start.go: goroutine sync cloud ogni 30s se cloud.yaml esiste (fail-open) - sandbox/manager.go: monta /tmp host in read-only nel container Docker - server.go: ActionType popolato negli eventi audit dal normalizer - guardian_shim.c: agent_name legge NIGHTAGENT_AGENT env (fallback "shim") - run.go: setta NIGHTAGENT_AGENT=<agente> nell'env del processo figlio - .gitignore: binari ignorati con /prefix per non matchare directory
- internal/configdir: risoluzione config dir (.nightagent/ locale → ~/.night-agent/ globale) - cmd/guardian/configdir.go: helper resolveConfigDir() condiviso, rispetta --global - main.go: aggiunto flag persistente --global - cloud.go: connect crea .nightagent/ locale, genera signing key dedicata, aggiorna .gitignore - start.go: priorità policy (cloud → locale → globale → errore), config dir risolta dalla cwd - test: 6 unit test per Resolve/CreateLocal/IsLocal con fallback e casi edge
…licy-only - internal/policy/loader.go: Load() con priorità cloud→locale(walk-up)→globale→none Watch() con fsnotify per hot-reload di nightagent-policy.yaml HTTPCloudClient, CloudClient interface, FormatSource() - internal/policy/loader_test.go: 7 test (cloud, fallback, local, parent, global, none, priority) - internal/daemon/server.go: UpdatePolicy() thread-safe con sync.RWMutex - cmd/guardian/start.go: usa policy.Load(), Watch(), hot-reload, --local-policy-only - internal/policy/policy.go: Load → LoadFile per evitare conflitto di nome - dipendenza: github.com/fsnotify/fsnotify v1.9.0
…cale/globale IngestRequest ora porta il campo policy_yaml (omitempty) con il contenuto YAML della policy attiva. Il campo viene incluso solo nel primo batch di ogni sync e solo quando la sorgente è SourceLocal o SourceGlobal. Aggiunto WithPolicyPath() e WithEndpoint() su Agent per configurazione fluent.
- Hardcoded immutable rules in Evaluate() bloccano write su policy files prima di qualsiasi regola YAML (non bypassabili) - Watch() accetta isTrustedFile callback: rifiuta reload se hash non corrisponde all'ultimo write del daemon (blocca tamper esterno) - Daemon: SetInitialHash, IsTrustedFileContent, WritePolicyFile, handlePolicyWrite (socket type="policy_write") - CLI: nightagent policy edit — apre $EDITOR, valida YAML, invia via socket al daemon (fallback scrivi diretto se daemon offline) - default_policy.yaml: regola protect_policy_write - shim: aggiunto tee alla lista comandi intercettati
- LockFile/UnlockFile/RelockFile in internal/policy (chflags uchg/nouchg) - init: imposta user-immutable su policy.yaml dopo setup - WritePolicyFile: unlock → write → relock attorno a ogni scrittura daemon - Hardcoded rules: blocca chflags nouchg *nightagent* - Shim: aggiunto chflags alla lista comandi intercettati
- install.sh: scarica binari pre-compilati da GitHub Releases, verifica SHA256, installa i 3 artifact (nightagent, guardian-shim, dylib) - .github/workflows/release.yml: build arm64 su macos-latest, pacchetta tar.gz + sha256, pubblica release automatica su tag v* - README: aggiorna sezione installazione con comando curl + nota Gatekeeper
…enti al modulo e repo
…imozione sezioni non pronte
Updated README to clarify platform support and CLI designation.
Removed unnecessary line break in the description.
QueryDaemon spostato in internal/mcphook (testabile). Se il daemon non è raggiungibile, restituisce block + exit code 2 invece di allow. Comportamento precedente permetteva ogni tool call con daemon spento.
QueryDaemon spostato in internal/mcphook (testabile). Se il daemon non è raggiungibile, restituisce block + exit code 2 invece di allow. Comportamento precedente permetteva ogni tool call con daemon spento.
Aggiunge SignFunc iniettabile nel Logger per separare la logica di firma
dall'implementazione. Quando il cloud è connesso, RemoteSigner chiama
POST /api/sign (timeout 3s) e fa fallback al signer locale in caso di errore.
Il campo sig_source ("local"/"remote") nel log è informativo e non fa parte
del payload firmato, garantendo retrocompatibilità con VerifyAll.
… CI release su tag esistente
pietroperona
left a comment
There was a problem hiding this comment.
Grazie per la PR — abbiamo fatto un merge test locale e il risultato è pulito. La Go codebase è identica, le modifiche reali sono 5 file (Makefile + 2 file C + exec-helper).
Il lavoro è solido. Alcune osservazioni prima del merge:
guardian_intercept.c su Windows
L'implementazione Windows è essenzialmente uno stub: posix_spawn e dlfcn non esistono su Windows, quindi l'interception DYLD non funziona davvero. Va bene per ora, ma andrebbe documentato esplicitamente (es. commento nel codice o nota nel README) che su Windows l'intercept layer non è operativo in questa versione.
afunix.h su Windows
AF_UNIX su Windows richiede Windows 10 build 1903+ / Windows Server 2019+. Vale la pena aggiungere un check a runtime o almeno documentarlo nei requisiti.
Makefile — clang hardcoded su Linux
ifeq ($(UNAME), Linux)
CC = clangSu molte distro Linux headless clang non è installato di default, solo gcc. Suggerisco:
CC ?= $(shell command -v clang 2>/dev/null || echo gcc)oppure lasciare CC sovrascrivibile da chi builda (make CC=gcc).
Se risolvi questi tre punti siamo pronti al merge.
Linux 6.17.0-22-generic and Windows 10 Pro