A xovi extension that provides a programmatic interface to xochitl's document library without the need to restart xochitl.
- xovi - Extension framework
- xovi-message-broker - Required for shell and QML communication
- Ensure dependencies are installed
- Download the
.sofile for your architecture from the latest release and place it in/home/root/xovi/extensions.d/on your reMarkable tablet- reMarkable 1 & 2:
librarian-armv7.so - reMarkable Paper Pro and Paper Pro Move:
librarian-aarch64.so
- reMarkable 1 & 2:
- Restart xovi
All commands use the xovi-message-broker pipe interface:
echo '>e<signal>:<params>' > /run/xovi-mb; cat /run/xovi-mb-outResolve a document or folder name to its UUID. Returns all matches (newline-separated) if multiple entries share the same name.
echo '>elookupEntry:My Document' > /run/xovi-mb; cat /run/xovi-mb-out
echo '>elookupEntry:My Folder/My Document' > /run/xovi-mb; cat /run/xovi-mb-outImport a PDF, EPUB, or .rmdoc file from the local filesystem. For PDF and EPUB, the file extension is stripped from the display name (e.g., report.pdf becomes report). For .rmdoc files, the archive is extracted directly — the embedded UUID, metadata, and page data are preserved as-is. Returns the document's UUID.
# Import to root
echo '>eimportDocument:/path/to/file.pdf' > /run/xovi-mb; cat /run/xovi-mb-out
# Import into a folder (by name or UUID)
echo '>eimportDocument:/path/to/file.pdf,My Folder' > /run/xovi-mb; cat /run/xovi-mb-out
# Import an rmdoc archive
echo '>eimportDocument:/path/to/backup.rmdoc' > /run/xovi-mb; cat /run/xovi-mb-out
echo '>eimportDocument:/path/to/backup.rmdoc,My Folder' > /run/xovi-mb; cat /run/xovi-mb-outImport a PNG or JPEG image, converting it to a single-page PDF for display in xochitl. Returns the new document's UUID.
echo '>eimportImage:/path/to/screenshot.png' > /run/xovi-mb; cat /run/xovi-mb-out
echo '>eimportImage:/path/to/photo.jpg,My Folder' > /run/xovi-mb; cat /run/xovi-mb-outecho '>erenameEntry:Old Name,New Name' > /run/xovi-mb; cat /run/xovi-mb-out# Move to a folder
echo '>emoveEntry:My Document,Destination Folder' > /run/xovi-mb; cat /run/xovi-mb-out
# Move to root (empty parent)
echo '>emoveEntry:My Document,' > /run/xovi-mb; cat /run/xovi-mb-outecho '>etrashEntry:My Document' > /run/xovi-mb; cat /run/xovi-mb-out
echo '>erestoreEntry:My Document' > /run/xovi-mb; cat /run/xovi-mb-outecho '>edeleteEntry:My Document' > /run/xovi-mb; cat /run/xovi-mb-out# Clone into same location
echo '>ecloneEntry:My Document,' > /run/xovi-mb; cat /run/xovi-mb-out
# Clone into a different folder
echo '>ecloneEntry:My Document,Other Folder' > /run/xovi-mb; cat /run/xovi-mb-out# Create notebook
echo '>ecreateNotebook:New Notebook' > /run/xovi-mb; cat /run/xovi-mb-out
echo '>ecreateNotebook:New Notebook,My Folder' > /run/xovi-mb; cat /run/xovi-mb-out
# Create folder
echo '>ecreateFolder:New Folder' > /run/xovi-mb; cat /run/xovi-mb-out
echo '>ecreateFolder:Subfolder,Parent Folder' > /run/xovi-mb; cat /run/xovi-mb-out
# Ensure folder exists (creates if missing, returns UUID)
echo '>eensureFolder:My Folder' > /run/xovi-mb; cat /run/xovi-mb-out
echo '>eensureFolder:Projects/2026/March' > /run/xovi-mb; cat /run/xovi-mb-out# Pin/unpin
echo '>esetPinned:My Document,true' > /run/xovi-mb; cat /run/xovi-mb-out
# Set cover page to last visited
echo '>esetCover:My Document,-1' > /run/xovi-mb; cat /run/xovi-mb-out
# Set cover page to first page
echo '>esetCover:My Document,0' > /run/xovi-mb; cat /run/xovi-mb-out
# Set orientation
echo '>esetOrientation:My Document,landscape' > /run/xovi-mb; cat /run/xovi-mb-out
echo '>esetOrientation:My Document,portrait' > /run/xovi-mb; cat /run/xovi-mb-out
# Set tags
echo '>esetTags:My Document,tag1;tag2;tag3' > /run/xovi-mb; cat /run/xovi-mb-outScan the xochitl data directory and load any documents not yet known to the running library. Returns the number of new entries loaded.
echo '>erescanLibrary:' > /run/xovi-mb; cat /run/xovi-mb-outAll signals are accessible from QML via the xovi-message-broker:
import net.asivery.XoviMessageBroker 2.0XoviMessageBroker { id: librarian }librarian.sendSimpleSignal("lookupEntry", "My Document")
librarian.sendSimpleSignal("importDocument", "/tmp/report.pdf,My Folder")
librarian.sendSimpleSignal("importDocument", "/tmp/backup.rmdoc")
librarian.sendSimpleSignal("importImage", "/tmp/screenshot.png,Screenshots")
librarian.sendSimpleSignal("renameEntry", "My Document,New Name")
librarian.sendSimpleSignal("moveEntry", "My Document,Destination Folder")
librarian.sendSimpleSignal("trashEntry", "My Document")
librarian.sendSimpleSignal("restoreEntry", "My Document")
librarian.sendSimpleSignal("deleteEntry", "My Document")
librarian.sendSimpleSignal("cloneEntry", "My Document,Other Folder")
librarian.sendSimpleSignal("createNotebook", "New Notebook,My Folder")
librarian.sendSimpleSignal("createFolder", "New Folder,Parent Folder")
librarian.sendSimpleSignal("ensureFolder", "Projects/2026/March")
librarian.sendSimpleSignal("rescanLibrary", "")
librarian.sendSimpleSignal("setPinned", "My Document,true")
librarian.sendSimpleSignal("setCover", "My Document,-1")
librarian.sendSimpleSignal("setOrientation", "My Document,landscape")
librarian.sendSimpleSignal("setTags", "My Document,tag1;tag2;tag3")| Signal | Parameter | Returns |
|---|---|---|
lookupEntry |
name or path |
UUID(s), newline-separated |
importDocument |
filepath or filepath,parent |
UUID |
importImage |
filepath or filepath,parent |
UUID |
renameEntry |
entry,newName |
ok |
moveEntry |
entry,parent |
ok |
trashEntry |
entry |
ok |
restoreEntry |
entry |
ok |
deleteEntry |
entry |
ok |
cloneEntry |
entry,parent |
ok |
createNotebook |
name or name,parent |
ok |
createFolder |
name or name,parent |
ok |
ensureFolder |
name or path |
UUID |
rescanLibrary |
(none) | count of new entries |
setPinned |
entry,true/false |
ok |
setCover |
entry,pageNumber |
ok |
setOrientation |
entry,portrait/landscape |
ok |
setTags |
entry,tag1;tag2;tag3 |
ok |
All commands accept document names, folder paths, or UUIDs:
| Form | Example | Behavior |
|---|---|---|
| UUID | 9155cd95-147f-4bb5-93f1-a1519dac1021 |
Direct lookup |
| Name | My Document |
Searches root first, then all folders. Errors if ambiguous. |
| Anchored name | /My Document |
Searches root only, no fallback. |
| Path | My Folder/My Document |
Walks the folder hierarchy from root |
restoreEntry searches specifically within trashed entries.
All commands return ERROR: <message> on failure (not found, ambiguous name, etc.).
Commands that take two arguments use , as the separator (e.g., entry,newName), splitting on the last comma. The first argument (entry name) can contain commas freely. Name and path resolution with / is best-effort convenience as reMarkables doesn't prevent / from being used in document or folder names. If a folder name contains , or a name contains /, use UUIDs instead:
# Get UUIDs first
echo '>elookupEntry:My Document' > /run/xovi-mb; cat /run/xovi-mb-out
echo '>elookupEntry:Destination Folder' > /run/xovi-mb; cat /run/xovi-mb-out
# Then use UUIDs directly
echo '>emoveEntry:<doc-uuid>,<folder-uuid>' > /run/xovi-mb; cat /run/xovi-mb-out./build.shBuilds for both architectures using Docker:
librarian-aarch64.so- reMarkable Paper Prolibrarian-armv7.so- reMarkable 2
Copyright (C) 2026 Mitchell Scott
Licensed under the GNU General Public License v3.0.