package cli import ( "fmt" "os" "path/filepath" "github.com/spf13/cobra" ) const defaultConfigTemplate = `# vaultik configuration # Documentation: https://git.eeqj.de/sneak/vaultik # ─── REQUIRED ──────────────────────────────────────────────────────────────── # Age recipient public keys for encryption. # Backups are encrypted to ALL listed recipients. Any one of the corresponding # private keys can decrypt. Generate a keypair with: # age-keygen -o key.txt && grep 'public key' key.txt age_recipients: - age1REPLACE_WITH_YOUR_PUBLIC_KEY # Named snapshots. Each snapshot backs up one or more paths and can have its # own exclude patterns in addition to the global excludes below. snapshots: home: paths: - ~/Documents - ~/Pictures # exclude: # - "*.cache" # Storage backend (pick ONE of the three forms below). # # S3-compatible: # storage_url: "s3://mybucket/backups?endpoint=s3.example.com®ion=us-east-1" # (also set s3.access_key_id and s3.secret_access_key below) # # Local filesystem: # storage_url: "file:///mnt/backups/vaultik" # # Rclone (requires rclone configured separately): # storage_url: "rclone://myremote/path/to/backups" storage_url: "" # ─── S3 CREDENTIALS (required for s3:// storage_url) ──────────────────────── # s3: # access_key_id: YOUR_ACCESS_KEY # secret_access_key: YOUR_SECRET_KEY # # region: us-east-1 # Default: us-east-1 # # use_ssl: true # Default: true # # part_size: 5MB # Multipart upload part size. Default: 5MB # ─── OPTIONAL ──────────────────────────────────────────────────────────────── # Global exclude patterns applied to ALL snapshots. # Snapshot-specific excludes are additive. # exclude: # - "*.log" # - "*.tmp" # - ".git" # - "node_modules" # Average chunk size for content-defined chunking (FastCDC). # Smaller = better deduplication but more metadata overhead. # Accepts: 1MB, 10M, 64KB, etc. # Default: 10MB # chunk_size: 10MB # Maximum blob size before splitting into a new blob. # Accepts: 1GB, 10G, 500MB, etc. # Default: 10GB # blob_size_limit: 10GB # Zstd compression level (1-19). Higher = better ratio but slower. # Default: 3 # compression_level: 3 # Hostname used in snapshot IDs. Default: system hostname. # hostname: myserver # Path to the local SQLite index database. # Default: ~/.local/share/berlin.sneak.app.vaultik/index.sqlite # index_path: /path/to/index.sqlite ` // NewInitCommand creates the init command that writes a default config file. func NewInitCommand() *cobra.Command { cmd := &cobra.Command{ Use: "init", Short: "Write a default config file", Long: `Creates a default configuration file with commented explanations for every setting. If a config file already exists at the target path, the command refuses to overwrite it. The config is written to the path from --config, $VAULTIK_CONFIG, or the platform default config directory (e.g. ~/Library/Application Support/ on macOS, ~/.config/ on Linux, /etc/vaultik/ as root).`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { path := configPathForInit() if _, err := os.Stat(path); err == nil { return fmt.Errorf("config file already exists: %s", path) } dir := filepath.Dir(path) if err := os.MkdirAll(dir, 0o755); err != nil { return fmt.Errorf("creating config directory %s: %w", dir, err) } if err := os.WriteFile(path, []byte(defaultConfigTemplate), 0o600); err != nil { return fmt.Errorf("writing config file: %w", err) } fmt.Printf("Config written to %s\n", path) fmt.Println("Edit it to set your age_recipients, snapshots, and storage_url.") return nil }, } return cmd } // configPathForInit returns the config path to write, checking --config flag, // VAULTIK_CONFIG env, and the platform default. func configPathForInit() string { if rootFlags.ConfigPath != "" { return rootFlags.ConfigPath } if envPath := os.Getenv("VAULTIK_CONFIG"); envPath != "" { return envPath } return DefaultConfigPath() }