Add snapshot files to list a snapshot's files without restoring it (closes #435)
check / check (push) In progress

`vaultik snapshot files <snapshot-id> [path] [--json]` downloads and
decrypts the snapshot database, as restore does, and prints mode, size,
modification time and path for every file at or under the path. A path
that matches nothing is an error. It needs the private key. The mode is
in Go's file-mode notation, which the README spells out. The table
quotes a path that holds a non-printable character.

Named `files` because `ls` is already the alias of `snapshot list`.
The missing-key error no longer says "for restore", since this command
and deep verify return it too. The README opening now states the
private-key rule first.

Model: opus-5-5
This commit is contained in:
sneak committed 2026-11-13 21:21:42 +00:00
1 parent 5ca0d46932
commit 88f2ea6f06
14 files changed
+552 -82

No files matched your search

+11 -10
View File
@@ -219,7 +219,8 @@ func retryOpen(ctx context.Context, path string) (*DB, error) {
// errUntrustedSnapshotSchema is returned when a downloaded snapshot
// database carries schema objects the real schema never defines, or is
// missing a table the restore and deep-verify queries read.
// missing a table the restore, deep-verify and `snapshot files` queries
// read.
var errUntrustedSnapshotSchema = errors.New(
"downloaded snapshot database has an untrusted schema")
@@ -241,12 +242,12 @@ func snapshotReadOnlyDSN(path string) string {
// OpenReadOnly opens an already-materialized SQLite file for read-only
// querying of a snapshot database downloaded from the store, used by
// restore and deep verify. Unlike New it never applies schema migrations
// and never writes: the connection is opened read-only with query_only
// and trusted_schema=OFF. It refuses any file whose schema carries a
// trigger, view or virtual table, or lacks an expected table, so a forged
// file cannot redefine what the restore queries return. The caller owns
// the file and must remove it.
// restore, deep verify and `snapshot files`. Unlike New it never applies
// schema migrations and never writes: the connection is opened read-only
// with query_only and trusted_schema=OFF. It refuses any file whose schema
// carries a trigger, view or virtual table, or lacks an expected table, so
// a forged file cannot redefine what the restore queries return. The
// caller owns the file and must remove it.
func OpenReadOnly(ctx context.Context, path string) (*DB, error) {
conn, err := sql.Open("sqlite", snapshotReadOnlyDSN(path))
if err != nil {
@@ -276,9 +277,9 @@ func OpenReadOnly(ctx context.Context, path string) (*DB, error) {
// the plain table set the real schema defines. Any trigger, view or
// virtual table, or a missing expected table, fails the open.
func verifySnapshotSchema(ctx context.Context, conn *sql.DB) error {
// expectedSnapshotTables are the tables the restore and deep-verify
// queries read. A downloaded database missing any of them is not a
// genuine snapshot database and is refused.
// expectedSnapshotTables are the tables the restore, deep-verify and
// `snapshot files` queries read. A downloaded database missing any of
// them is not a genuine snapshot database and is refused.
expectedSnapshotTables := []string{
"blob_chunks",
"blobs",