zdbview 0.3.0

Terminal inspector and CRUD editor for rkyv archives and SQLite databases
zdbview-0.3.0 is not a library.
███████╗██████╗ ██████╗ ██╗   ██╗██╗███████╗██╗    ██╗
╚══███╔╝██╔══██╗██╔══██╗██║   ██║██║██╔════╝██║    ██║
  ███╔╝ ██║  ██║██████╔╝██║   ██║██║█████╗  ██║ █╗ ██║
 ███╔╝  ██║  ██║██╔══██╗╚██╗ ██╔╝██║██╔══╝  ██║███╗██║
███████╗██████╔╝██████╔╝ ╚████╔╝ ██║███████╗╚███╔███╔╝
╚══════╝╚═════╝ ╚═════╝   ╚═══╝  ╚═╝╚══════╝ ╚══╝╚══╝

Read the Docs · Engineering Report


zdbview

Terminal inspector and CRUD editor for rkyv archives and SQLite databases.

One binary opens either kind of file. The file type is detected from the SQLite header magic (authoritative — a .db name whose bytes are not a SQLite header is treated as binary), falling back to the extension only for files too short to carry a header.

zdbview                        # no args → pick from recently opened files
zdbview path/to/file.db        # SQLite → full CRUD
zdbview path/to/archive.rkyv   # rkyv   → full CRUD if recognized, else structural
zdbview --sqlite file          # force SQLite
zdbview --rkyv   file          # force rkyv/binary

Install

brew tap MenkeTechnologies/menketech
brew install zdbview

The formula installs the binary, both man pages and the zsh completion, and is bumped automatically by this repo's Release workflow on every v* tag.

From crates.io — binary only, no man pages or completion:

cargo install zdbview

From source:

git clone https://github.com/MenkeTechnologies/zdbview
cd zdbview && cargo build --release
install -m 755 target/release/zdbview /usr/local/bin/

SQLite is compiled in (rusqlite's bundled feature), so there is no system library to install. Tagged releases publish prebuilt binaries for macOS (arm64 + x86_64) and Linux (glibc + static musl, arm64 + x86_64). Homebrew covers the glibc targets; the static musl tarballs — for Alpine, distroless and other non-glibc hosts — are attached to the same GitHub release, with their sha256 sums listed at the bottom of the formula.

Recent files (no args)

Every opened file is recorded to $XDG_CACHE_HOME/zdbview/recent (or ~/.cache/zdbview/recent), most-recent-first. Running zdbview with no argument shows that list as a picker — j/k to move, Enter to open, q to quit.

Paths are canonicalized, so the same file reached through different relative paths or symlinks dedupes to one entry that moves back to the front on re-open. The list is capped at 50 entries and written via temp-file-plus-rename, so a concurrent reader never sees a half-written list.

SQLite — full generic CRUD

SQLite files are self-describing, so every operation works on any database:

  • Browse tables (with row counts) and paginated rows.
  • e — edit the selected cell in place.
  • a — insert a row using column defaults.
  • d — delete the selected row (confirm with y).
  • : — run an arbitrary SQL statement.

Rows are addressed by rowid; WITHOUT ROWID tables are listed read-only. Identifiers are double-quoted with internal quotes doubled, and edited values are bound as parameters, so schemas with spaces, keywords or quotes in their names work unmodified.

rkyv — auto-detected key/value CRUD + structural inspection

rkyv archives are not self-describing: the format stores no field names or type tags (https://rkyv.org/format.html), so the schema cannot be recovered from an unknown archive. zdbview handles this with a format registry: known formats are detected by their magic header and decoded to real key/value with a faithfully-copied, byte-compatible archive type; anything unrecognized falls back to a raw structural view.

  • 0 Records — key/value table for a recognized archive: keys on the left, the selected value's decoded scalar fields plus a hex dump on the right. Searchable by key. This view is the default whenever a format is recognized.
  • 1 Info — file size, and (when recognized) the detected format name and decoded header fields.
  • 2 Strings — every run of printable text embedded in the archive, with byte offsets.
  • 3 Hexxxd-style hex/ascii dump of the raw bytes.

Recognized formats (all fusevm-host script/heap caches, rkyv 0.7):

Format Magic Detect Key
zshrs script cache ZRSC magic script path
zshrs autoload cache ZRAL magic function name
strykelang script cache STRY magic (native v4 + compat) script path
awkrs script cache AWKR magic script path
vimlrs script cache VIML magic script path
elisprs heap-image cache ELSP magic script path
pythonrs bytecode cache (none) validated try-decode source path
rubylang / arb script cache (none) validated try-decode u64 content hash

Magic-bearing formats are matched by their header; the header-less hash-keyed shards (pythonrs, rubylang, arb) are attempted last and gated by rkyv validation, so an unrelated archive falls through to the structural view rather than mis-decoding. A format mismatch always surfaces as failed validation → structural fallback, never silent corruption. Adding a format is one registry entry: copy its archive type (same rkyv version and features as the producer) and map its magic (or add a validated try-decode for header-less formats).

Full CRUD (write-back)

Recognized archives are editable in place, not just readable. In the Records view:

  • acreate a record (prompt for a key; inserted with an empty value).
  • eupdate the selected record's value (type text, or 0x<hex> for binary).
  • rrename a record's key (map-keyed formats).
  • ddelete the record (confirm with y).

Every edit deserializes the shard, mutates it, and re-serializes it, then writes the file back atomically (temp + rename). Re-serialization is byte-identical to what the producing host writes, so the host reads the edited shard normally — verified by round-tripping every real cache. Edits target a record's stable identity (map key, or the u64 content hash for the header-less formats), so an update or delete touches exactly one entry even when several share a display key (pythonrs stores many records under <string>/<stdin>). Rename is offered only for the map-keyed formats; the header-less formats key by a content hash and have no renameable key.

Bytecode disassembly (disasm feature)

The script-cache value blobs are bincode-encoded fusevm::Chunk. Built with

cargo build --features disasm

the value pane gains a disasm render mode (v cycles to it) that decodes the blob and lists ops, constants, and names using the real fusevm types — no vendored copy of the 267-variant opcode enum, so no risk of silently wrong output. bincode is version-sensitive: disassembly is correct only when the linked fusevm version matches the one that encoded the cache; a mismatch fails loudly (invalid variant / unexpected EOF) and you fall back to hex. The feature is off by default so the crate stays self-contained and publishable.

Keys

Key Action
Tab switch focus (table list ↔ rows)
arrows / hjkl move
gg / G jump to top / bottom
Enter open detail (row / record); focus rows (table list)
/ search; n / N next / previous match
Ctrl-f / Ctrl-b page forward / back (SQLite)
e a d : edit / add / delete / SQL (SQLite)
a e r d create / update-value / rename / delete record (rkyv)
S schema view (SQLite)
0 1 2 3 Records / Info / Strings / Hex (rkyv)
v cycle value render (auto / hex / text / disasm) — detail screen
y copy cell / value / key to clipboard (OSC 52)
x export table (CSV) / records (JSON) to a file
? help overlay
q / Esc leave screen / quit

SQLite search is whole-table (SQL-backed across every column), not limited to the loaded page; rkyv search scans record keys, the string list, or raw bytes. Text searches are case-insensitive and wrap; the hex byte search is case-sensitive.

Detail view

Enter on a row or record opens a full-screen detail: every field untruncated on top, and a scrollable value pane below. v cycles how the value bytes render — auto (text if it looks textual, else hex), hex (xxd style), or text (UTF-8). y copies the value; the blob is shown raw, so this is how you read a cell or record value that's too wide for the grid.

Export

Interactive x writes the current table (CSV) or all records (JSON) to a file in the working directory. Non-interactively:

zdbview data.db --export json      # object of { table: [rows...] }, all tables
zdbview data.db --export csv       # first table as CSV
zdbview cache.rkyv --export json   # recognized records, value blob as hex

Man pages

Installed by the Homebrew formula — man zdbview works straight after brew install. From a source checkout:

man -l man/man1/zdbview.1        # the standard page
man -l man/man1/zdbviewall.1     # the all-in-one reference, zshall-style

# install
cp man/man1/zdbview*.1 /usr/local/share/man/man1/

Zsh completion

The Homebrew formula drops _zdbview into Homebrew's zsh_completion dir, which is already on fpath. From a source checkout:

fpath=(/path/to/zdbview/completions $fpath)
autoload -Uz compinit && compinit

Build

cargo build
cargo test

License

MIT — see LICENSE.