slipql
A query language for Slipcase metadata. Point it at a directory of .slpc containers and ask questions in select, from, and where: each container is a row, each metadata key a column.
$ slipql ./contracts --recursive
Connected to: ./contracts (recursive). Type "help" for commands, "quit" to exit.
slipql> select @path, title, governance.owner where status = "draft" or tags contains "legal"
| @path | title | governance.owner |
| ---------------------- | ------------------------- | ---------------- |
| 2026/renewal.docx.slpc | Renewal, 2026 | |
| msa.pdf.slpc | Master services agreement | Kim |
| q3.xlsx.slpc | Q3 report | Lee |
(3 rows)
Why
A Slipcase container carries a TOML document describing its payload, and that description travels with the file. Once a directory holds a few hundred of them, the question stops being "what is in this file" and becomes "which files say this". Unpacking every container to find out, or keeping an index that goes stale the moment someone copies a file in, both defeat the point of metadata that lives with the document.
slipql reads each container's metadata in place, through the same library the slipcase command uses, and never unpacks a payload. It keeps no index and no state: every query is a fresh scan, so the answer is what is on disk now. It changes nothing.
The language borrows SQL's clause shape and TOML's literals, so anyone who can write a metadata document can write a query against one without learning a second date syntax or a second way to quote a string.
Install
Debian and Ubuntu
Add the Excelano apt repository once:
|
Then install it, so apt upgrade keeps it current:
Homebrew
crates.io
Windows
winget install Excelano.slipql
The command
slipql <dir> opens a prompt bound to a directory, so queries can leave out from. Add --recursive to descend into subdirectories. --exec runs one query and exits, and a query on standard input runs without a prompt, one per line, which is how a script uses it:
Output is a table at a terminal and tab-separated in a pipe; --mode picks table, tsv, csv, or json explicitly. CSV output is meant as a feed for the rest of the tabular family, xled among them. JSON keeps TOML's types where JSON has them. A container that cannot be read is skipped and reported on standard error after the rows, as is any comparison the query made across types, so a pipe stays clean and nothing is silently ignored.
slipql --help states the flags and the exit status contract.
The language
select <columns> from '<dir>' [recursive] [where <condition>] [limit <n>]
Columns are metadata keys, with dots into nested tables and [n] into arrays: governance.privacy_flag, tags[0]. @path is the container's path under the from directory. select * gives @path and then every leaf value across the rows returned, as dotted columns. A column a row does not have renders empty rather than failing the query, because metadata keys are ad hoc by design.
Conditions compare a column with a literal using =, !=, <, >, <=, and >=, test membership with in (...), match strings with like and ilike, look inside arrays with contains, and test for a key with exists. Combine with and, or, not, and parentheses. Literals are TOML's: "strings" or 'literal strings', 42, 1.5, true, and datetimes as TOML writes them, 2026-01-01 or 2026-01-01T09:00:00+02:00.
Keywords are lowercase. TOML has no null, so there is no is null; a key is present or it is absent, and exists is the one test for that. Comparing a column that is absent, or one whose type does not match the literal, is neither true nor false and does not select the row. Values compare within their type: integers with floats, strings with strings, each of TOML's four datetime forms with itself, and offset datetimes as instants. There is no coercion between strings and numbers, on purpose, since a "3" where a 3 was meant is a mistake worth seeing rather than one worth hiding.
GRAMMAR.md is the exact grammar and the semantics of every operator.
The library
The crate is the query engine, and the command is a thin shell over it. A program that embeds it takes the crate with default-features = false, which leaves out the command's own dependencies.
use ;
Rows come out of an iterator as the scan reaches them, so a query over a large tree starts answering at once, limit stops the scan early, and dropping the iterator cancels it. Once the rows are out, the tally says what was skipped and which comparisons crossed types. docs.rs/slipql is the library's own page.
License
MIT. See LICENSE.