@@ -26,7 +26,7 @@ import (
2626)
2727
2828// version is injected at build time via -ldflags:
29- // -X main.version=YYYYMMDD+bN
29+ // -X main.version=YYYY.MM.DD+build.N
3030// Keep default empty so linker override works reliably.
3131var version string
3232
@@ -50,6 +50,8 @@ func main() {
5050 switch cmd {
5151 case "status" :
5252 os .Exit (cmdStatus (args ))
53+ case "config" :
54+ os .Exit (cmdConfig (args ))
5355 case "version" , "-v" , "--version" :
5456 cmdVersion ()
5557 case "check" :
@@ -351,6 +353,47 @@ func cmdBypassSave(wantBypass bool, cfgPath string) int {
351353 return 0
352354}
353355
356+ // cmdConfig opens config in nano and reloads the agent after editor exit.
357+ // Non-root users are transparently escalated with sudo when needed.
358+ func cmdConfig (args []string ) int {
359+ cfgPath := firstArgOrDefault (args , defaultConfigPath )
360+
361+ nanoPath , err := exec .LookPath ("nano" )
362+ if err != nil {
363+ fmt .Fprintln (os .Stderr , "nano not found; install nano first" )
364+ return 1
365+ }
366+
367+ editorArgs := []string {nanoPath , cfgPath }
368+ editorCmd := exec .Command (editorArgs [0 ], editorArgs [1 :]... )
369+ if os .Geteuid () != 0 && ! isWritableByCurrentUser (cfgPath ) {
370+ editorCmd = exec .Command ("sudo" , editorArgs ... )
371+ }
372+ if code := runCommand (editorCmd , "edit config with nano" ); code != 0 {
373+ return code
374+ }
375+
376+ // Reuse existing reload flow (includes config check).
377+ if os .Geteuid () == 0 {
378+ return cmdReload ([]string {cfgPath })
379+ }
380+ exe , err := os .Executable ()
381+ if err != nil {
382+ fmt .Fprintf (os .Stderr , "resolve executable for reload: %v\n " , err )
383+ return 1
384+ }
385+ return runCommand (exec .Command ("sudo" , exe , "reload" , cfgPath ), "sudo reload after config edit" )
386+ }
387+
388+ func isWritableByCurrentUser (path string ) bool {
389+ f , err := os .OpenFile (path , os .O_WRONLY | os .O_APPEND , 0 )
390+ if err != nil {
391+ return false
392+ }
393+ _ = f .Close ()
394+ return true
395+ }
396+
354397func writeFileAtomic (path string , data []byte , mode os.FileMode ) error {
355398 tmp := path + ".tmp"
356399 if err := os .WriteFile (tmp , data , mode ); err != nil {
@@ -550,6 +593,7 @@ Usage:
550593
551594Commands:
552595 status [config] launch the live TUI (default: /etc/kekkai/kekkai.yaml)
596+ config [config] open config in nano, then auto-reload kekkai-agent
553597 check [config] validate a config file (read-only; safe as non-root)
554598 ports [config] show colorized public/private port summary
555599 show [config] print the normalised config after migration
0 commit comments