Skip to content

multi platform support#2

Open
anywayzz wants to merge 68 commits into
night-agent-cli:stagingfrom
anywayzz:main
Open

multi platform support#2
anywayzz wants to merge 68 commits into
night-agent-cli:stagingfrom
anywayzz:main

Conversation

@anywayzz
Copy link
Copy Markdown

Linux 6.17.0-22-generic and Windows 10 Pro

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
pietroperona and others added 29 commits April 13, 2026 17:02
- 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
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.
Copy link
Copy Markdown
Collaborator

@pietroperona pietroperona left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 = clang

Su 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants