███████╗██████╗ ██████╗ ██╗ ██╗██╗███████╗██╗ ██╗
╚══███╔╝██╔══██╗██╔══██╗██║ ██║██║██╔════╝██║ ██║
███╔╝ ██║ ██║██████╔╝██║ ██║██║█████╗ ██║ █╗ ██║
███╔╝ ██║ ██║██╔══██╗╚██╗ ██╔╝██║██╔══╝ ██║███╗██║
███████╗██████╔╝██████╔╝ ╚████╔╝ ██║███████╗╚███╔███╔╝
╚══════╝╚═════╝ ╚═════╝ ╚═══╝ ╚═╝╚══════╝ ╚══╝╚══╝
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
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:
From source:
&&
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. The
overlay keys work here too: h help, c scheme chooser, C palette editor, and
a scheme picked here is the one the opened file uses.
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 withy).:— 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.
0Records — 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.1Info — file size, and (when recognized) the detected format name and decoded header fields.2Strings — every run of printable text embedded in the archive, with byte offsets.3Hex —xxd-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:
a— create a record (prompt for a key; inserted with an empty value).e— update the selected record's value in a hex editor (see below).r— rename a record's key (map-keyed formats).d— delete the record (confirm withy).
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 — on by default)
The script-cache value blobs are bincode-encoded fusevm::Chunk. The value
pane's disasm render mode (v cycles to it) 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. On by default (fusevm
is published on crates.io); disable with --no-default-features.
Keys
| Key | Action |
|---|---|
Tab |
switch focus (table list ↔ rows) |
j / k, arrows |
move; ← / → change column |
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) |
s |
sort by the cursor column (ascending → descending → off) |
< / > |
move the sort to the previous / next column |
a e r d |
create / hex-edit 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 |
c |
color-scheme chooser |
C |
palette editor |
h / ? |
help overlay |
| mouse | wheel scrolls, click selects, right-click selects + opens detail |
q |
quit |
Esc |
back out of a nested screen (quits on the main screen) |
h is the help key, not a motion — columns move with ← / →. The overlay keys
(h, c, C) work on every screen, including the recent-files picker.
Color schemes
c opens the scheme chooser (ported from iftoprs): every scheme with a swatch
of its six palette colors, j/k or the wheel to cycle with live preview,
Enter to save, Esc to cancel and restore the previous scheme. C opens the
palette editor — ←/→ pick a slot, ↑/↓ adjust it by one, PgUp/PgDn
by sixteen, Enter saves the custom palette. Both persist to
$XDG_CONFIG_HOME/zdbview/prefs (or ~/.config/zdbview/prefs) and load on
startup; the editor uses C rather than e, which edits data everywhere else.
Non-interactively, --list-themes prints every scheme with its token and swatch,
and --theme <token> overrides the saved scheme for one run:
Help overlay and toasts
h (or ?) opens the keyboard-shortcut overlay: a themed box listing every
binding in three columns, with the section matching what is open — SQLite, rkyv,
or the recent-files picker. Any key or click closes it.
Action results (copy, export, edit, delete, SQL, scheme saved) appear as a
transient toast centered above the status bar and dismiss themselves after
three seconds — a port of iftoprs's StatusMsg / draw_status. The same text
stays in the status bar so it remains readable after the toast fades.
Input prompts (search, SQL, cell/value edit, add/rename) have a movable text
cursor: ←/→, Home/End, and the readline chords Ctrl-a/Ctrl-e
(line start/end), Ctrl-w (delete word), Ctrl-u/Ctrl-k (kill to
start/end). Mouse support and the cursor model are ported from iftoprs.
Hex editor
e on a record opens a full-screen hex editor over that record's value —
ported from zmax's
zmax-term/src/ui/hex.rs. It opens pre-filled with the current bytes, so there
is nothing to retype:
plugins/foo.zsh [+] — 40 bytes · cursor 0x00000004 · -- EDIT (hex) --
00000000 40 07 0e 15 1c 23 2a 31 38 3f 46 4d 54 5b 62 69 |@....#*18?FMT[bi|
00000010 70 77 7e 85 8c 93 9a a1 a8 af b6 bd c4 cb d2 d9 |pw~.............|
| Key | Action |
|---|---|
h l j k, arrows |
move a byte / a row; 0 $ row start / end; g G first / last |
Ctrl-f / Ctrl-b |
page; the wheel scrolls, a click places the cursor |
i / R |
enter EDIT mode (the editor opens read-only) |
Tab |
switch the focused column between hex and ASCII |
0-9 a-f |
in EDIT + hex: set the high nibble, then the low one, then advance |
| any printable | in EDIT + ASCII: overwrite the byte and advance |
o / O |
insert a 00 byte after / before the cursor |
x |
delete the byte under the cursor |
Ctrl-s |
write the value back into the archive |
Esc |
leave EDIT mode; q leaves the editor (twice when there are unsaved edits) |
The editor is modal — h and c are its own motions and hex digits there, not
the help and scheme keys. Saving goes through the same write-back as every other
record edit: deserialize, mutate, re-serialize byte-identically, atomic rename.
o/O/x are a zdbview addition: zmax edits a fixed-length file, while a
record's value can legitimately change length.
Sorting
s sorts the row grid by the column under the cursor: ascending, then descending
on a second press, then back to the table's natural rowid order on a third.
< / > move the sort to the previous / next column, keeping the direction. The
sorted column is marked in its header (qty ▲) and in the pane title; selecting
another table clears the sort.
Sorting is done in SQL (ORDER BY "col" ASC|DESC, rowid), so it orders the
whole table, not just the loaded page — numeric columns sort numerically, and
the rowid tiebreaker keeps paging stable when the sort column has duplicates.
Search and the (row N of M) readout follow the displayed order, so n / N
step to the next match on screen rather than the next by rowid.
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:
Man pages
Installed by the Homebrew formula — man zdbview works straight after
brew install. From a source checkout:
# install
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 )
&&
Build
cargo build
cargo test
License
MIT — see LICENSE.