Omahub
← All plugins
W

Ripcord

by weedwhitesandwine

Snatch-and-run defence for risky places: your tethered USB drive gets yanked and the session locks.

Security review

Review recommended · 1 finding

Deterministic scan — not a security guarantee

Low
Risk level
Low
Analyzed commit
f4a6945
Scanned
1 month ago
  • low obfuscation ripcord-watch.py:115

    Augments a command with octal/hex escape sequences.

    \x20amd64". Showing that raw makes the pairing list

Automated analysis only — not a security guarantee.

AI advisory review

No obvious issues detected

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

Low
AI risk level
Low
Recommendation
install
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
f4a6945
Reviewed
1 month ago

The plugin is a well-documented USB-drive removal watcher that locks or sleeps the session when the paired drive is yanked. The code is clear, uses standard system facilities, and writes only a settings file. The deterministic 'obfuscation' finding is a false positive: it refers to a comment explaining udev's \xNN escape sequences, not actual obfuscation.

  • The plugin executes system commands (hyprlock, systemctl suspend, etc.) when triggered, but these are user-configured and clearly documented.
  • The watcher runs as the user with no elevation, and the README explicitly lists all processes and files it touches.
How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/weedwhitesandwine/ripcord --enable
System #system #security

Ripcord

<p align="center"> <img src="preview.png" width="420" alt="Ripcord armed, showing the hazard bar and STAND DOWN"/> </p>

Built for the field, not the desk: your USB drive is the key. It gets yanked and the machine locks.

About this project

The intent is a failsafe for anyone working somewhere their laptop might be taken off the table in front of them — a café, a border queue, a press pit, a protest. It works by making a "key" drive that is tethered to you like a ripcord. If the machine is carried away, the drive comes out with you, and the session locks the moment it goes. Optionally it sleeps as well, so the memory is cold before the machine is out of the room.

The drive does not have to be plugged in all the time — only when you think you might be at risk. And it only fires on the first removal after being inserted: unlock after a trigger and it will not fire again until the drive has been put back and pulled out once more.

It is a deterrent against a snatch-and-run. It is not a defence against someone who has your machine and time — see What it cannot do.

How it works

Pair a drive, arm the trap, and pull the drive to fire it.

  • The key is the physical drive, identified by the serial in its USB descriptor. Relabelling the drive does not break the pairing, and neither does reformatting it. A drive named to match yours cannot stand in for it. Drives that report no serial fall back to the SCSI serial, then the wwid, then a partition identifier — the last of which does not survive a reformat.
  • One row per drive, not per partition. A single installer stick can carry three filesystems; you plug in one thing, so the list shows one thing, named by its volume with the hardware name and size underneath.
  • Only drives you can physically pull out are offered. Anything reached over the USB bus, plus anything the kernel reports as removable. The disk your system runs from is never offered, so the trap cannot be set on something that can only be removed by accident.
  • Arming binds to the drive you armed on, not merely to "something with the right serial is still attached". This matters because cheap flash drives are routinely shipped with duplicate or absent serials: if a second drive answering to the same identity were enough to satisfy the trap, pulling your real key would go unnoticed.
  • It watches for events, not on a timer. The kernel tells it when the set of attached drives changes and it re-reads to confirm. A slow re-read runs as a backstop so a missed event cannot leave the trap blind.

What it does when the drive is pulled

Whatever you have enabled, in this order:

Response Default What happens
Lock the session on The lock screen appears; the machine stays awake
Put the machine to sleep off Power is cut to everything but memory; the lock screen is waiting when it wakes

Sleeping is off until you turn it on. Locking your own session is unremarkable; putting the machine to sleep is worth a deliberate decision rather than arriving switched on.

Rehearsal mode is on by default. While it is on, pulling the drive sends a notification saying what would have happened and does nothing else. Leave it on until you have watched the trap fire once — nobody should have to get locked out of their own laptop to discover whether they configured it correctly.

After it fires, it does not re-arm on its own. The drive has to be plugged back in first, so unlocking after a trigger does not walk straight into the next one.

Arming is never remembered. It is not written to disk and does not survive a restart, a shell reload or a crash. Every armed session is one you asked for.

What it tells you

In the bar, a padlock: closed when armed, open when not. Red when arming would really lock the machine, amber while it is only rehearsing, and the ordinary bar colour when the trap is off.

In the panel, a status block that changes colour and grows an animated hazard bar while the trap is set, above a readout of which drive is paired, whether it is connected, and exactly what a pull will do.

When something is wrong, it says so rather than appearing to be on guard:

  • the drive watcher stopping while armed
  • a lock that fails, or that reports success without the session actually locking
  • two attached drives answering to the same identity — arming is refused while that is true, because the trap could not tell them apart
  • a drive list too large to read, which means it is working from stale information

Appearance

Ripcord paints its own surface rather than following the active theme: plain dark blue, or off-white in light mode. The moon in the panel header switches between them and the choice is remembered.

This is a deliberate break from the rest of the shell. The panel's job is to be unmistakable at a glance, and it cannot promise that when the ground under it changes with every theme. Every colour in both modes is at least 4.5:1 against its own background, which is only checkable because the background is known.

Choosing a drive

Use a drive with nothing on it you care about. The whole point is that it gets yanked out without warning, and pulling a mounted filesystem mid-write can corrupt it. A cheap empty stick costs nothing to replace and nothing to lose — the drive is a key, not storage.

If you must use one that carries data, unmount it before arming. Ripcord watches for the device disappearing, not for it being mounted, so an unmounted-but-attached drive still arms and still fires when it is physically removed.

Requirements

Python 3, which Omarchy already has. Nothing else — the drive watching uses kernel facilities reached through the C library, so there is no watcher package to install.

Install

omarchy plugin add https://github.com/weedwhitesandwine/ripcord.git --enable

Then add Ripcord to your bar from the shell's bar settings, open it, plug your drive in, and click the drive to pair it.

Update

omarchy plugin update io.github.weedwhitesandwine.ripcord

Remove

omarchy plugin remove io.github.weedwhitesandwine.ripcord

Removing the plugin leaves your settings behind at ~/.local/state/omarchy/ripcord/. Delete that directory to clear them.

What it writes, and when

Files it writes

Path When What
~/.local/state/omarchy/ripcord/settings.json When you pair, unpair, or change a setting The paired drive's identifier and name, the lock and sleep toggles, rehearsal mode, and the light/dark choice

That is the only file it writes. The state directory is created with owner-only permissions, and writes go through the shell's atomic-write path. Whether the trap is armed is deliberately not written anywhere.

Ripcord does not touch your Hyprland configuration, your bar layout, or any other file belonging to you or to the shell. It offers no hotkey, so it has nothing to add to your keybindings.

Files it reads

Path Why
/dev/disk/by-uuid, /dev/disk/by-label Which filesystems are attached, and what they are called
/sys/block/…, and the USB device nodes above them To identify each drive — serial, model and size — and to tell one you can unplug from one that is bolted in
its own settings.json To restore your pairing and settings

Its settings file is opened without following symlinks, checked to be a regular file, and read to a fixed ceiling, so a file that has been replaced with something else yields nothing rather than something. Measured: a 191 MB file planted at that path is refused with the reading process peaking at 14 MB, a FIFO is refused rather than hanging, and a symlink is refused.

Processes it runs

Command When
python3 ripcord-watch.py Once, for the life of the shell — the drive watcher
python3 -c … To read the settings file to a ceiling, at startup and whenever it changes on disk
mkdir -p -m 700 Once at startup, for the state directory
omarchy-system-lock, or hyprlock, or loginctl lock-session When the trap fires and locking is enabled — the first of those that exists
omarchy-hyprland-session-locked About a second after a lock, to confirm the session really locked
systemctl suspend When the trap fires and sleeping is enabled (off by default)
notify-send Seven situations: a rehearsal trigger; a trigger with no response enabled; a lock command that fails; a lock that reports success while the session stays unlocked; the watcher stopping while armed; a second drive claiming the paired identity while armed; and a drive list that cannot be read while armed

Every one of these runs as you, with your own session's permissions. Nothing here needs or requests elevation. The watcher is started with setpriv --pdeathsig TERM so it cannot outlive the shell.

Why the lock order is what it is. On Omarchy Quattro, loginctl lock-session does nothing at all and exits 0 — so it is neither a working lock nor a detectable failure, and a plugin trusting it would report success while leaving the session open. omarchy-system-lock drives the shell's own lock service and also locks 1Password, which is the right behaviour for the situation this plugin exists for, so it goes first. Because a zero exit proves nothing here, Ripcord asks the compositor a moment later whether the session actually locked, and tells you if it did not.

Network

None. Ripcord opens no sockets and makes no requests.

What it cannot do

It runs inside the desktop shell. If the shell stops, the watching stops with it. The bar padlock exists so that the armed state is something you can see rather than assume, but nothing can report a shell that is not running.

A drive can be unplugged and plugged back in faster than any watcher can respond. This is a tripwire, not a lock.

It cannot tell two drives apart if they report the same serial. It detects that case and refuses to arm rather than setting a trap that cannot spring, but it cannot resolve it — unplug the other drive.

Locking is not encryption. Anyone who takes the machine still has your disk. If that matters, use full-disk encryption and sleep rather than lock, so the keys leave memory.

Development

python3 test-classify.py     # which devices count as unpluggable
python3 test-trap-logic.py   # when the trap fires, and when it must not

test-classify.py checks the unpluggable/bolted-in decision against synthetic device trees, including cases real hardware cannot produce on demand — a USB SSD that does not set the removable flag, and an internal disk on a path that merely contains the letters "usb".

test-trap-logic.py models the arming and triggering rules, including the case that matters most: with a second drive attached reporting the same identity as your key, pulling the real one still fires. It models the logic rather than executing the QML, so the two are kept in step by hand.

Credits

The concept comes from RipCord by KClose. All credit for the original idea and its macOS implementation goes to them.

This is an independent implementation for Omarchy and shares no code with it.

Licence

MIT. See LICENSE.

Built with Claude Code.