A package management system and repository tailored specifically for reMarkable tablets.
See the codeA package management system and repository tailored specifically for reMarkable tablets.
Vellum provides an opinionated wrapper and static build of Alpine Package Keeper (apk), adapted for the constraints of the reMarkable platform.
Please refer to the vellum-cli documentation for instructions on installation and usage.
The reMarkable tablet’s operating system is called Codex, meaning "bound book" in Latin.
Historically, vellum was the fine parchment used for illuminated manuscripts. Today, it refers to the smooth, durable paper favored by artists and designers.
A fitting name for a package manager on a paper tablet.
url field.Vellum stores everything in /home/root/.vellum/ to work around the space-constrained and read-only root filesystem:
/home/root/.vellum/
├── bin/
│ ├── vellum # wrapper binary
│ └── apk.vellum # static apk binary
├── etc/apk/
│ ├── repositories # package sources
│ └── keys/ # signing keys (packages.rsa.pub, local.rsa*)
├── lib/apk/db/ # package database
├── share/bash-completion/ # shell completions
├── local-repo/ # local package repository
├── cache/ # download cache
└── state/ # runtime state
Vellum automatically detects the device model and creates an appropriate virtual package:
The remarkable-os virtual package is generated dynamically to match the installed OS version, enabling version-specific package dependencies.
These packages are auto-installed on every vellum command and allows extensions to declare device compatibility.
Pull requests and issues must be opened by a person, not by an agent acting on its own. If a pull request description is written by an LLM, we will assume that the pull request was opened by an agent, and it will be closed. This also applies to any followup comments made to any issue or PR.
Commits must be authored and co-authored by people. Do not add Co-Authored-By: trailers that name an assistant, agent, or bot, and do not commit from a bot account.
packages/ with the package nameVELBUILD file following Alpine's format. See VELBUILD Reference, and aports coding stylemaintainer="Your Name <your@email.com>"
pkgname=myextension
pkgver=1.0.0
pkgrel=0
upstream_author="your-github-username"
category="ui"
pkgdesc="Description of your extension"
url="https://github.com/you/repo"
arch="noarch"
license="SPDX License Identifier for your license"
depends="qt-resource-rebuilder remarkable-os>=3.24 remarkable-os<3.25"
_commit="1161639c9435372db1fb05024137407d20f6bb5e"
source="
https://raw.githubusercontent.com/you/repo/$_commit/myextension.qmd
https://raw.githubusercontent.com/you/repo/$_commit/LICENSE
"
options="!check !fhs"
package() {
install -Dm644 "$srcdir"/myextension.qmd \
"$pkgdir"/home/root/xovi/exthome/qt-resource-rebuilder/myextension.qmd
install -Dm644 "$srcdir"/LICENSE \
"$pkgdir"/home/root/.vellum/licenses/$pkgname/LICENSE
echo "https://github.com/you/repo" > \
"$pkgdir"/home/root/.vellum/licenses/$pkgname/SOURCES
}
sha512sums="
<checksum> myextension.qmd
<checksum> LICENSE
"
Use !package-name in depends= to declare that your package cannot be installed alongside another:
depends="xovi !other-extension" # conflicts with other-extension
depends="xovi !other-extension>1.0" # conflicts with other-extension versions >1.0
Use semantic versioning (MAJOR.MINOR.PATCH) for pkgver:
Use pkgrel for packaging changes that don't affect the upstream version (e.g., fixing the VELBUILD, changing dependencies). Reset pkgrel to 0 when bumping pkgver.
pkgver=1.2.0
pkgrel=0 # initial release of 1.2.0
pkgrel=1 # fixed a dependency, same upstream version
APK supports pre-release suffixes (underscore-prefixed):
| Suffix | Meaning | Sorts |
|---|---|---|
_alpha | Alpha release | before base |
_beta | Beta release | before base |
_pre | Pre-release | before base |
_rc | Release candidate | before base |
pkgver=1.2.0_rc1 # sorts before 1.2.0
pkgver=1.2.0 # final release
OS compatibility is declared in depends=. apk automatically picks the highest compatible version:
mypackage-1.0.0 depends="remarkable-os>=3.22 remarkable-os<3.24"
mypackage-1.0.1 depends="remarkable-os>=3.24" # ported to 3.24
mypackage-2.0.0 depends="remarkable-os>=3.24" # breaking change
Device compatibility is also declared in depends=. apk ANDs these, so these should be exclusions:
mypackage-1.0.0 depends="!rm1 !rm2" # Compatible with Paper Pro and Move only
Packages can include lifecycle scripts. These are added as functions in the VELBUILD:
postinstall(){
# Make changes
}
postupgrade(){
postinstall
}
predeinstall(){
# Undo changes from postinstall
}
| Script | When it runs |
|---|---|
preinstall | Before fresh install |
postinstall | After fresh install |
preupgrade | Before upgrading to a new version |
postupgrade | After upgrading to a new version |
predeinstall | Before package removal |
postdeinstall | After package removal |
postosupgrade | After reMarkable OS update (via vellum reenable) |
The post-os-upgrade hook runs when users execute vellum reenable after an OS update. Use this for packages that need to restore system files wiped by OS updates.
If your package installs files outside /home/root/ (e.g., systemd units to /etc/systemd/system/), you'll likely need lifecycle scripts to handle installation, upgrades, OS updates, and removal.
[!IMPORTANT] System files require careful handling:
- Paper Pro family root filesystems are read-only;
/etcuses an overlay that resets on reboot- Files created by scripts aren't tracked by apk, clean them up in
pre-deinstallvellum reenablewrapspost-os-upgradewithmount-rw/mount-restoreautomatically, but other hooks must call these themselves
Use mount-utils (a dependency) to handle the read-only filesystem:
mount-rw # Remount root read-write and unmount /etc overlay
mount-restore # Restore /etc overlay and remount root read-only
It automatically detects if mount changes are required by the device.
Example:
postinstall() {
/home/root/.vellum/bin/mount-rw
echo "tun" > /etc/modules-load.d/tun.conf
/home/root/.vellum/bin/mount-restore
}
When users run vellum purge (vs vellum del), they expect all data to be removed. Vellum sets VELLUM_PURGE=1 in the environment so your scripts can detect this:
predeinstall(){
# Only remove user data on purge
if [ "$VELLUM_PURGE" = "1" ]; then
rm -rf /home/root/.config/mypackage
rm -rf /home/root/.local/share/mypackage
fi
}
| Command | Behavior |
|---|---|
vellum del mypackage | Removes package, keeps user config/data |
vellum purge mypackage | Removes package and all associated data |
Requires Docker or Podman.
# Lint all packages
./scripts/lint-packages.sh --apkbuild-lint
# Lint specific packages
./scripts/lint-packages.sh mypackage --apkbuild-lint
# Lint only packages changed since main
./scripts/lint-packages.sh --changed --apkbuild-lint
When you modify source URLs or update package versions:
./scripts/update-checksums.sh mypackage
# Build a single package
./scripts/build-package.sh mypackage aarch64
# Build a noarch package (auto-detected from VELBUILD)
./scripts/build-package.sh mypackage
Built packages are output to dist/<arch>/.
Generated development keypair is output to keys/vellum-dev.rsa(.pub)
Copy the vellum-dev.rsa.pub key to the reMarkable: /home/root/.vellum/etc/apk/keys/
vellum add mypackage-1.0.0-r0.apk
Vellum maintains a separate testing repository for validation before release. This is useful for testing package changes on real devices before merging to main.
main branch. Generally, one package per PR is preferredvellum testing enablevellum update && vellum add <package>@testing/ready-for-review on the PRvellum testing disablevellum testing status # Check if testing repo is enabled
vellum testing enable # Enable testing repository
vellum testing disable # Disable testing repository
| Feed | Contents |
|---|---|
| https://vellum.delivery/feeds/all.rss | All packages |
| https://vellum.delivery/feeds/rm1.rss | reMarkable 1 |
| https://vellum.delivery/feeds/rm2.rss | reMarkable 2 |
| https://vellum.delivery/feeds/rmpp.rss | reMarkable Paper Pro |
| https://vellum.delivery/feeds/rmppmove.rss | reMarkable Paper Pro Move |
| https://vellum.delivery/feeds/rmppure.rss | reMarkable Paper Pure |
Individual packages retain their own licenses. Vellum infrastructure is MIT.
Shell
98.7%
CMake
1.3%
A package management system and repository tailored specifically for reMarkable tablets.
See the codeA package management system and repository tailored specifically for reMarkable tablets.
Vellum provides an opinionated wrapper and static build of Alpine Package Keeper (apk), adapted for the constraints of the reMarkable platform.
Please refer to the vellum-cli documentation for instructions on installation and usage.
The reMarkable tablet’s operating system is called Codex, meaning "bound book" in Latin.
Historically, vellum was the fine parchment used for illuminated manuscripts. Today, it refers to the smooth, durable paper favored by artists and designers.
A fitting name for a package manager on a paper tablet.
url field.Vellum stores everything in /home/root/.vellum/ to work around the space-constrained and read-only root filesystem:
/home/root/.vellum/
├── bin/
│ ├── vellum # wrapper binary
│ └── apk.vellum # static apk binary
├── etc/apk/
│ ├── repositories # package sources
│ └── keys/ # signing keys (packages.rsa.pub, local.rsa*)
├── lib/apk/db/ # package database
├── share/bash-completion/ # shell completions
├── local-repo/ # local package repository
├── cache/ # download cache
└── state/ # runtime state
Vellum automatically detects the device model and creates an appropriate virtual package:
The remarkable-os virtual package is generated dynamically to match the installed OS version, enabling version-specific package dependencies.
These packages are auto-installed on every vellum command and allows extensions to declare device compatibility.
Pull requests and issues must be opened by a person, not by an agent acting on its own. If a pull request description is written by an LLM, we will assume that the pull request was opened by an agent, and it will be closed. This also applies to any followup comments made to any issue or PR.
Commits must be authored and co-authored by people. Do not add Co-Authored-By: trailers that name an assistant, agent, or bot, and do not commit from a bot account.
packages/ with the package nameVELBUILD file following Alpine's format. See VELBUILD Reference, and aports coding stylemaintainer="Your Name <your@email.com>"
pkgname=myextension
pkgver=1.0.0
pkgrel=0
upstream_author="your-github-username"
category="ui"
pkgdesc="Description of your extension"
url="https://github.com/you/repo"
arch="noarch"
license="SPDX License Identifier for your license"
depends="qt-resource-rebuilder remarkable-os>=3.24 remarkable-os<3.25"
_commit="1161639c9435372db1fb05024137407d20f6bb5e"
source="
https://raw.githubusercontent.com/you/repo/$_commit/myextension.qmd
https://raw.githubusercontent.com/you/repo/$_commit/LICENSE
"
options="!check !fhs"
package() {
install -Dm644 "$srcdir"/myextension.qmd \
"$pkgdir"/home/root/xovi/exthome/qt-resource-rebuilder/myextension.qmd
install -Dm644 "$srcdir"/LICENSE \
"$pkgdir"/home/root/.vellum/licenses/$pkgname/LICENSE
echo "https://github.com/you/repo" > \
"$pkgdir"/home/root/.vellum/licenses/$pkgname/SOURCES
}
sha512sums="
<checksum> myextension.qmd
<checksum> LICENSE
"
Use !package-name in depends= to declare that your package cannot be installed alongside another:
depends="xovi !other-extension" # conflicts with other-extension
depends="xovi !other-extension>1.0" # conflicts with other-extension versions >1.0
Use semantic versioning (MAJOR.MINOR.PATCH) for pkgver:
Use pkgrel for packaging changes that don't affect the upstream version (e.g., fixing the VELBUILD, changing dependencies). Reset pkgrel to 0 when bumping pkgver.
pkgver=1.2.0
pkgrel=0 # initial release of 1.2.0
pkgrel=1 # fixed a dependency, same upstream version
APK supports pre-release suffixes (underscore-prefixed):
| Suffix | Meaning | Sorts |
|---|---|---|
_alpha | Alpha release | before base |
_beta | Beta release | before base |
_pre | Pre-release | before base |
_rc | Release candidate | before base |
pkgver=1.2.0_rc1 # sorts before 1.2.0
pkgver=1.2.0 # final release
OS compatibility is declared in depends=. apk automatically picks the highest compatible version:
mypackage-1.0.0 depends="remarkable-os>=3.22 remarkable-os<3.24"
mypackage-1.0.1 depends="remarkable-os>=3.24" # ported to 3.24
mypackage-2.0.0 depends="remarkable-os>=3.24" # breaking change
Device compatibility is also declared in depends=. apk ANDs these, so these should be exclusions:
mypackage-1.0.0 depends="!rm1 !rm2" # Compatible with Paper Pro and Move only
Packages can include lifecycle scripts. These are added as functions in the VELBUILD:
postinstall(){
# Make changes
}
postupgrade(){
postinstall
}
predeinstall(){
# Undo changes from postinstall
}
| Script | When it runs |
|---|---|
preinstall | Before fresh install |
postinstall | After fresh install |
preupgrade | Before upgrading to a new version |
postupgrade | After upgrading to a new version |
predeinstall | Before package removal |
postdeinstall | After package removal |
postosupgrade | After reMarkable OS update (via vellum reenable) |
The post-os-upgrade hook runs when users execute vellum reenable after an OS update. Use this for packages that need to restore system files wiped by OS updates.
If your package installs files outside /home/root/ (e.g., systemd units to /etc/systemd/system/), you'll likely need lifecycle scripts to handle installation, upgrades, OS updates, and removal.
[!IMPORTANT] System files require careful handling:
- Paper Pro family root filesystems are read-only;
/etcuses an overlay that resets on reboot- Files created by scripts aren't tracked by apk, clean them up in
pre-deinstallvellum reenablewrapspost-os-upgradewithmount-rw/mount-restoreautomatically, but other hooks must call these themselves
Use mount-utils (a dependency) to handle the read-only filesystem:
mount-rw # Remount root read-write and unmount /etc overlay
mount-restore # Restore /etc overlay and remount root read-only
It automatically detects if mount changes are required by the device.
Example:
postinstall() {
/home/root/.vellum/bin/mount-rw
echo "tun" > /etc/modules-load.d/tun.conf
/home/root/.vellum/bin/mount-restore
}
When users run vellum purge (vs vellum del), they expect all data to be removed. Vellum sets VELLUM_PURGE=1 in the environment so your scripts can detect this:
predeinstall(){
# Only remove user data on purge
if [ "$VELLUM_PURGE" = "1" ]; then
rm -rf /home/root/.config/mypackage
rm -rf /home/root/.local/share/mypackage
fi
}
| Command | Behavior |
|---|---|
vellum del mypackage | Removes package, keeps user config/data |
vellum purge mypackage | Removes package and all associated data |
Requires Docker or Podman.
# Lint all packages
./scripts/lint-packages.sh --apkbuild-lint
# Lint specific packages
./scripts/lint-packages.sh mypackage --apkbuild-lint
# Lint only packages changed since main
./scripts/lint-packages.sh --changed --apkbuild-lint
When you modify source URLs or update package versions:
./scripts/update-checksums.sh mypackage
# Build a single package
./scripts/build-package.sh mypackage aarch64
# Build a noarch package (auto-detected from VELBUILD)
./scripts/build-package.sh mypackage
Built packages are output to dist/<arch>/.
Generated development keypair is output to keys/vellum-dev.rsa(.pub)
Copy the vellum-dev.rsa.pub key to the reMarkable: /home/root/.vellum/etc/apk/keys/
vellum add mypackage-1.0.0-r0.apk
Vellum maintains a separate testing repository for validation before release. This is useful for testing package changes on real devices before merging to main.
main branch. Generally, one package per PR is preferredvellum testing enablevellum update && vellum add <package>@testing/ready-for-review on the PRvellum testing disablevellum testing status # Check if testing repo is enabled
vellum testing enable # Enable testing repository
vellum testing disable # Disable testing repository
| Feed | Contents |
|---|---|
| https://vellum.delivery/feeds/all.rss | All packages |
| https://vellum.delivery/feeds/rm1.rss | reMarkable 1 |
| https://vellum.delivery/feeds/rm2.rss | reMarkable 2 |
| https://vellum.delivery/feeds/rmpp.rss | reMarkable Paper Pro |
| https://vellum.delivery/feeds/rmppmove.rss | reMarkable Paper Pro Move |
| https://vellum.delivery/feeds/rmppure.rss | reMarkable Paper Pure |
Individual packages retain their own licenses. Vellum infrastructure is MIT.
Shell
98.7%
CMake
1.3%