List remote snapshots without requiring the private key (closes #64)
check / check (push) Successful in 6s
check / check (push) Successful in 6s
ListSnapshots built its table entirely from the local SQLite index. The only remote access, reportRemoteDrift, was gated on AgeSecretKey != "", so on a correctly configured host - which by design holds no private key - snapshot list never contacted the destination store at all. A user who lost their local index could not see their own backups, and the "<remote only>" cell the README documents was unreachable dead code. The listing is now the union of the local index and the destination store, with no age_secret_key gate. Remote-only snapshots cannot have their hostname or name recovered - RemoteSnapshotKey is one-way and the manifest stores the hash - so they are listed by abbreviated remote key with the real timestamp and compressed size from the manifest, and "<remote only>" in the two columns that require the local index. Nothing new is written to remote storage and the human ID is never fabricated. An unreachable destination degrades to local-only with a warning and a zero exit code. remote_present is null rather than false in that case, so "absent" and "unknown" stay distinguishable and no drift is claimed from a listing that never happened. Also: - Snapshot timestamps are normalised to UTC in scanSnapshotRows, the single point where they enter the domain. Previously one of three scanners omitted .UTC(), so on a non-UTC host the same snapshot rendered a different time depending on whether it was locally tracked. - The 1000-row cap and the unreadable-manifest count are reported in --json mode as well as table mode, so machine consumers cannot be silently truncated. The JSON shape is unchanged. - Warnings raised while listing are routed to stderr rather than the logger, which writes to stdout and would corrupt the JSON document. This is a local workaround for the logger bug tracked in #82 and should be removed when that lands. - downloadManifestByKey is now the only remote manifest reader, so the manifest privacy question in #81 has a single call site to change. - The orphaned "vaultik snapshot cleanup" hint now names vaultik prune; that command was folded into prune by the 2026-07-02 consolidation.
This commit was merged in pull request #83.
This commit is contained in:
@@ -53,18 +53,36 @@ const (
|
||||
)
|
||||
|
||||
// SnapshotInfo contains information about a snapshot.
|
||||
// UncompressedSize and NewChunkSize are populated only when the snapshot
|
||||
// is present in the local database; LocallyTracked indicates whether
|
||||
// those values are meaningful.
|
||||
//
|
||||
// LocallyTracked says which of the two sources this row came from, and
|
||||
// therefore which fields are meaningful:
|
||||
//
|
||||
// - true: the snapshot is in the local index. ID is its human
|
||||
// snapshot ID and UncompressedSize/NewChunkSize are populated.
|
||||
// - false: the snapshot was found only on the destination store. ID
|
||||
// is empty, because the human ID cannot be recovered from remote
|
||||
// storage without the age secret key (see RemoteKey), and
|
||||
// UncompressedSize/NewChunkSize are zero because they are computed
|
||||
// from local index rows that do not exist.
|
||||
//
|
||||
// RemoteKey is always populated: for a locally tracked snapshot it is
|
||||
// the key the snapshot would occupy on the destination store, and for a
|
||||
// remote-only snapshot it is the only identifier available.
|
||||
//
|
||||
// RemotePresent reports whether the snapshot's metadata was seen on the
|
||||
// destination store. It is nil when the destination could not be
|
||||
// listed, so "absent" and "unknown" stay distinguishable.
|
||||
//
|
||||
//nolint:tagliatelle // snake_case is the established output format
|
||||
type SnapshotInfo struct {
|
||||
ID types.SnapshotID `json:"id"`
|
||||
RemoteKey string `json:"remote_key"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
CompressedSize int64 `json:"compressed_size"`
|
||||
UncompressedSize int64 `json:"uncompressed_size,omitempty"`
|
||||
NewChunkSize int64 `json:"new_chunk_size,omitempty"`
|
||||
LocallyTracked bool `json:"locally_tracked"`
|
||||
RemotePresent *bool `json:"remote_present"`
|
||||
}
|
||||
|
||||
// formatBytes formats bytes in a human-readable format
|
||||
|
||||
Reference in New Issue
Block a user