diff --git a/README.md b/README.md index 32a5a37..4a5fd80 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ WIP: pre-1.0, some functions may not be fully implemented yet -`vaultik` is an incremental backup daemon written in Go. It encrypts data +`vaultik` is an incremental backup tool written in Go. It encrypts data using an `age` public key and uploads each encrypted blob directly to a remote S3-compatible object store. It requires no private keys, secrets, or credentials (other than those required to PUT to encrypted object storage, @@ -120,9 +120,6 @@ passphrase is needed or stored locally. access_key_id: ... secret_access_key: ... region: us-east-1 - backup_interval: 1h - full_scan_interval: 24h - min_time_between_run: 15m chunk_size: 10MB blob_size_limit: 1GB ``` @@ -147,16 +144,21 @@ passphrase is needed or stored locally. ### commands ```sh -vaultik [--config ] snapshot create [snapshot-names...] [--cron] [--daemon] [--prune] +vaultik [--config ] snapshot create [snapshot-names...] [--cron] [--prune] [--skip-errors] vaultik [--config ] snapshot list [--json] -vaultik [--config ] snapshot verify [--deep] +vaultik [--config ] snapshot verify [--deep] [--json] vaultik [--config ] snapshot purge [--keep-latest | --older-than ] [--force] -vaultik [--config ] snapshot remove [--dry-run] [--force] +vaultik [--config ] snapshot remove [--dry-run] [--force] [--remote] [--json] vaultik [--config ] snapshot prune -vaultik [--config ] restore [paths...] -vaultik [--config ] prune [--dry-run] [--force] +vaultik [--config ] restore [paths...] [--verify] +vaultik [--config ] prune [--force] [--json] +vaultik [--config ] purge [--keep-latest | --older-than ] [--force] +vaultik [--config ] verify [--deep] [--json] vaultik [--config ] info +vaultik [--config ] remote info [--json] vaultik [--config ] store info +vaultik [--config ] database purge [--force] +vaultik version ``` ### environment @@ -170,8 +172,8 @@ vaultik [--config ] store info * Config is located at `/etc/vaultik/config.yml` by default * Optional snapshot names argument to create specific snapshots (default: all) * `--cron`: Silent unless error (for crontab) -* `--daemon`: Run continuously with inotify monitoring and periodic scans * `--prune`: Delete old snapshots and orphaned blobs after backup +* `--skip-errors`: Skip file read errors (log them loudly but continue) **snapshot list**: List all snapshots with their timestamps and sizes * `--json`: Output in JSON format diff --git a/TODO.md b/TODO.md index 22ca067..4cc2a5c 100644 --- a/TODO.md +++ b/TODO.md @@ -103,26 +103,3 @@ User must have rclone configured separately (via `rclone config`). - Ensure consistent code style 1. Tag and release v1.0.0 - ---- - -## Post-1.0 (Daemon Mode) - -1. Implement inotify file watcher for Linux - - Watch source directories for changes - - Track dirty paths in memory - -1. Implement FSEvents watcher for macOS - - Watch source directories for changes - - Track dirty paths in memory - -1. Implement backup scheduler in daemon mode - - Respect backup_interval config - - Trigger backup when dirty paths exist and interval elapsed - - Implement full_scan_interval for periodic full scans - -1. Add proper signal handling for daemon - - Graceful shutdown on SIGTERM/SIGINT - - Complete in-progress backup before exit - -1. Write tests for daemon mode diff --git a/config.example.yml b/config.example.yml index 93d53b5..433d844 100644 --- a/config.example.yml +++ b/config.example.yml @@ -291,21 +291,6 @@ storage_url: "rclone://las1stor1//srv/pool.2024.04/backups/heraklion" # # Default: 5MB # #part_size: 5MB -# How often to run backups in daemon mode -# Format: 1h, 30m, 24h, etc -# Default: 1h -#backup_interval: 1h - -# How often to do a full filesystem scan in daemon mode -# Between full scans, inotify is used to detect changes -# Default: 24h -#full_scan_interval: 24h - -# Minimum time between backup runs in daemon mode -# Prevents backups from running too frequently -# Default: 15m -#min_time_between_run: 15m - # Path to local SQLite index database # This database tracks file state for incremental backups # Default: /var/lib/vaultik/index.sqlite diff --git a/internal/cli/root.go b/internal/cli/root.go index 4b633f4..f17d780 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -25,7 +25,7 @@ func NewRootCommand() *cobra.Command { cmd := &cobra.Command{ Use: "vaultik", Short: "Secure incremental backup tool with asymmetric encryption", - Long: `vaultik is a secure incremental backup daemon that encrypts data using age + Long: `vaultik is a secure incremental backup tool that encrypts data using age public keys and uploads to S3-compatible storage. No private keys are needed on the source system.`, SilenceUsage: true, diff --git a/internal/cli/snapshot.go b/internal/cli/snapshot.go index 5ff8d7a..4699e71 100644 --- a/internal/cli/snapshot.go +++ b/internal/cli/snapshot.go @@ -98,7 +98,6 @@ specifying a path using --config or by setting VAULTIK_CONFIG to a path.`, }, } - cmd.Flags().BoolVar(&opts.Daemon, "daemon", false, "Run in daemon mode with inotify monitoring") cmd.Flags().BoolVar(&opts.Cron, "cron", false, "Run in cron mode (silent unless error)") cmd.Flags().BoolVar(&opts.Prune, "prune", false, "Delete all previous snapshots and unreferenced blobs after backup") cmd.Flags().BoolVar(&opts.SkipErrors, "skip-errors", false, "Skip file read errors (log them loudly but continue)") diff --git a/internal/config/config.go b/internal/config/config.go index 66019b2..05602ae 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -6,7 +6,6 @@ import ( "path/filepath" "sort" "strings" - "time" "filippo.io/age" "git.eeqj.de/sneak/smartconfig" @@ -83,19 +82,16 @@ func (c *Config) SnapshotNames() []string { // encryption recipients, storage configuration, and performance tuning parameters. // Configuration is typically loaded from a YAML file. type Config struct { - AgeRecipients []string `yaml:"age_recipients"` - AgeSecretKey string `yaml:"age_secret_key"` - BackupInterval time.Duration `yaml:"backup_interval"` - BlobSizeLimit Size `yaml:"blob_size_limit"` - ChunkSize Size `yaml:"chunk_size"` - Exclude []string `yaml:"exclude"` // Global excludes applied to all snapshots - FullScanInterval time.Duration `yaml:"full_scan_interval"` - Hostname string `yaml:"hostname"` - IndexPath string `yaml:"index_path"` - MinTimeBetweenRun time.Duration `yaml:"min_time_between_run"` - S3 S3Config `yaml:"s3"` - Snapshots map[string]SnapshotConfig `yaml:"snapshots"` - CompressionLevel int `yaml:"compression_level"` + AgeRecipients []string `yaml:"age_recipients"` + AgeSecretKey string `yaml:"age_secret_key"` + BlobSizeLimit Size `yaml:"blob_size_limit"` + ChunkSize Size `yaml:"chunk_size"` + Exclude []string `yaml:"exclude"` // Global excludes applied to all snapshots + Hostname string `yaml:"hostname"` + IndexPath string `yaml:"index_path"` + S3 S3Config `yaml:"s3"` + Snapshots map[string]SnapshotConfig `yaml:"snapshots"` + CompressionLevel int `yaml:"compression_level"` // StorageURL specifies the storage backend using a URL format. // Takes precedence over S3Config if set. @@ -155,13 +151,10 @@ func Load(path string) (*Config, error) { cfg := &Config{ // Set defaults - BlobSizeLimit: Size(10 * 1024 * 1024 * 1024), // 10GB - ChunkSize: Size(10 * 1024 * 1024), // 10MB - BackupInterval: 1 * time.Hour, - FullScanInterval: 24 * time.Hour, - MinTimeBetweenRun: 15 * time.Minute, - IndexPath: filepath.Join(xdg.DataHome, appName, "index.sqlite"), - CompressionLevel: 3, + BlobSizeLimit: Size(10 * 1024 * 1024 * 1024), // 10GB + ChunkSize: Size(10 * 1024 * 1024), // 10MB + IndexPath: filepath.Join(xdg.DataHome, appName, "index.sqlite"), + CompressionLevel: 3, } // Convert smartconfig data to YAML then unmarshal diff --git a/internal/models/models.go b/internal/models/models.go index 2541d6a..f16e17a 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -63,10 +63,3 @@ type Chunk struct { Offset int64 Length int64 } - -// DirtyPath represents a path marked for backup by inotify -type DirtyPath struct { - Path string - MarkedAt time.Time - EventType string // "create", "modify", "delete" -} diff --git a/internal/vaultik/info.go b/internal/vaultik/info.go index ff28859..7640ad1 100644 --- a/internal/vaultik/info.go +++ b/internal/vaultik/info.go @@ -66,18 +66,6 @@ func (v *Vaultik) ShowInfo() error { } fmt.Println() - // Daemon Settings (if applicable) - if v.Config.BackupInterval > 0 || v.Config.MinTimeBetweenRun > 0 { - fmt.Printf("=== Daemon Settings ===\n") - if v.Config.BackupInterval > 0 { - fmt.Printf("Backup Interval: %s\n", v.Config.BackupInterval) - } - if v.Config.MinTimeBetweenRun > 0 { - fmt.Printf("Minimum Time: %s\n", v.Config.MinTimeBetweenRun) - } - fmt.Println() - } - // Local Database fmt.Printf("=== Local Database ===\n") fmt.Printf("Index Path: %s\n", v.Config.IndexPath) diff --git a/internal/vaultik/snapshot.go b/internal/vaultik/snapshot.go index a77818c..943aace 100644 --- a/internal/vaultik/snapshot.go +++ b/internal/vaultik/snapshot.go @@ -19,7 +19,6 @@ import ( // SnapshotCreateOptions contains options for the snapshot create command type SnapshotCreateOptions struct { - Daemon bool Cron bool Prune bool SkipErrors bool // Skip file read errors (log them loudly but continue) @@ -54,12 +53,6 @@ func (v *Vaultik) CreateSnapshot(opts *SnapshotCreateOptions) error { return fmt.Errorf("prune database: %w", err) } - if opts.Daemon { - log.Info("Running in daemon mode") - // TODO: Implement daemon mode with inotify - return fmt.Errorf("daemon mode not yet implemented") - } - // Determine which snapshots to process snapshotNames := opts.Snapshots if len(snapshotNames) == 0 { diff --git a/test/config.yaml b/test/config.yaml index 97e5c9d..07804ae 100644 --- a/test/config.yaml +++ b/test/config.yaml @@ -20,9 +20,6 @@ s3: region: us-east-1 use_ssl: true part_size: 5242880 # 5MB -backup_interval: 1h -full_scan_interval: 24h -min_time_between_run: 15m index_path: /tmp/vaultik-test.sqlite chunk_size: 10MB blob_size_limit: 10GB diff --git a/test/integration-config.yml b/test/integration-config.yml index ce6e5d7..a716482 100644 --- a/test/integration-config.yml +++ b/test/integration-config.yml @@ -17,9 +17,6 @@ s3: region: us-east-1 use_ssl: false part_size: 5242880 # 5MB -backup_interval: 1h -full_scan_interval: 24h -min_time_between_run: 15m index_path: /tmp/vaultik-integration-test.sqlite chunk_size: 10MB blob_size_limit: 10GB