docs: add project README

This commit is contained in:
dualfroz committed 2026-08-05 19:46:07 +02:00
commit c263a13641
2 files changed
+268

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
__pycache__/
*.py[cod]
+266
View File
@@ -0,0 +1,266 @@
# clip-vault
Two small background daemons for Linux desktops that keep a searchable local
history of things that normally vanish the moment they happen, and can forward
them to a webhook if you ask them to.
| Tool | Keeps a history of | Interface |
| --- | --- | --- |
| [`clip-vault`](#clip-vault-1) | Everything you copy to the clipboard | Tray icon + CLI |
| [`notif-vault`](#notif-vault) | Every desktop notification you receive | CLI only |
They live in one repository because they are the same idea applied twice: watch
a stream of short-lived desktop events, write them to a local SQLite database,
make them searchable afterwards, and optionally fan them out to HTTP endpoints
with per-rule filters. They share no code and can be installed independently.
Both store everything locally. Nothing is sent anywhere unless you write a
webhook into the config yourself, and every webhook ships disabled by default.
---
## clip-vault
A clipboard manager. It watches the X11 clipboard, stores every distinct thing
you copy, and gives you the history back through a tray menu and a CLI.
### Features
- **History with de-duplication.** Content is hashed with SHA-256; copying the
same thing twice moves the existing entry to the top instead of duplicating
it. The history is trimmed to `maxHistory` entries (500 by default).
- **Pinning.** Pinned clips survive both trimming and `clip-vault clear`.
- **Source window.** Each clip records the title of the window that was focused
when you copied it, so you can tell the password you copied from your browser
from the one you copied from your terminal.
- **Password-manager blindness.** Clips are dropped, not stored, when the source
window matches `ignoreWindowPatterns` (KeePassXC, Bitwarden and 1Password out
of the box) or when the content matches `ignoreContentPatterns` (PEM private
key headers out of the box).
- **Tray menu.** Pause capture, re-copy any of the last 10 clips with one click,
reload the config, quit.
- **Webhook fan-out.** Any clip matching a regex can be POSTed to an HTTP
endpoint in a background thread. Useful for pushing snippets to a note-taking
service or a chat channel.
### Requirements
- Python 3, X11 (this will not work on a Wayland session)
- PyGObject with GTK 3 and an app-indicator library — either
`AyatanaAppIndicator3` or the older `AppIndicator3`
- `xdotool` — reads the focused window title
- `xclip` — used by `clip-vault copy` to put a stored clip back on the clipboard
On a Debian or Ubuntu derivative:
```bash
sudo apt install python3-gi gir1.2-gtk-3.0 gir1.2-ayatanaappindicator3-0.1 xdotool xclip
```
### Installing
```bash
cd clip-vault
./install.sh
```
This copies the package into `~/.local/share/clip-vault`, drops `clip-vault`
and `clip-vault-daemon` wrappers into `~/.local/bin`, and adds an autostart
entry so the daemon starts with your session.
One rough edge worth knowing: the two wrapper scripts do **not** set
`PYTHONPATH` themselves, so running them by hand from a fresh shell needs it
exported first. The autostart entry sets it for you, so this only affects
manual runs.
```bash
export PYTHONPATH="$HOME/.local/share/clip-vault:$PYTHONPATH"
clip-vault-daemon &
```
### Using it
```bash
clip-vault list # 30 most recent clips
clip-vault list --search ssh # filter by content
clip-vault show 42 # print a clip in full
clip-vault copy 42 # put it back on the clipboard
clip-vault pin 42 # protect it from trimming and clear
clip-vault delete 42
clip-vault clear # wipe history, keeping pinned clips
clip-vault clear --all # wipe everything
clip-vault config # print config and log file paths
clip-vault test-webhook my-hook # send a test payload through a webhook
```
### Configuration
`~/.config/clip-vault/config.json`, created from the bundled defaults on first
run. The daemon re-reads it when you pick "Reload config" from the tray menu.
| Key | Default | Meaning |
| --- | --- | --- |
| `maxHistory` | `500` | Unpinned clips to keep |
| `minContentLength` | `2` | Ignore anything shorter |
| `maxContentLength` | `200000` | Ignore anything longer |
| `ignoreWindowPatterns` | KeePassXC, Bitwarden, 1Password | Case-insensitive regexes matched against the source window title |
| `ignoreContentPatterns` | PEM private key header | Regexes matched against the clip content |
| `webhooks` | one disabled example | See below |
`pollFallbackSeconds` also appears in the default config but is not currently
read by any code.
A webhook entry:
```json
{
"name": "notes",
"enabled": true,
"url": "https://example.com/hook",
"method": "POST",
"matchPattern": "^TODO:",
"headers": { "Authorization": "Bearer ..." },
"timeoutSeconds": 4,
"includeSourceWindow": true
}
```
`matchPattern` is a regex tested against the clip content — `.*` forwards
everything. The request body is JSON:
```json
{ "content": "...", "timestamp": 1234567890.0, "sourceWindow": "Firefox" }
```
`sourceWindow` is omitted when `includeSourceWindow` is false.
### Files it writes
- `~/.config/clip-vault/config.json` — configuration
- `~/.config/clip-vault/history.sqlite3` — the clipboard history
- `~/.config/clip-vault/webhook.log` — one line per webhook attempt
**The history database is plain, unencrypted SQLite.** The ignore patterns
reduce what ends up in it, but anything you copy that is not filtered out is
readable by anything running as your user. Treat it accordingly.
---
## notif-vault
A searchable archive of your desktop notifications. It listens on the session
D-Bus for `org.freedesktop.Notifications.Notify` calls, records every one, and
lets you search them afterwards from the command line.
This is a *passive recorder*: it does not display, suppress or replace your
notifications, and your normal notification daemon keeps working untouched.
### Features
- **Full history** of app name, summary, body, urgency and icon, with a
configurable cap (5000 by default).
- **Search and filter** by app, by text, by age.
- **Live tail** of incoming notifications in a terminal.
- **App ignore list** using shell-style globs.
- **Do-not-disturb window** that suppresses *webhook forwarding* during set
hours, optionally letting critical notifications through. It does not
suppress the notifications themselves — they are still recorded and still
appear on screen.
- **Webhook fan-out** filtered by app glob and minimum urgency.
### Requirements
- Python 3
- `python3-dbus` and PyGObject (`GLib`)
- A session D-Bus that permits eavesdropping. The daemon adds a match rule with
`eavesdrop=true`; on distributions that have tightened D-Bus policy this may
need to be allowed explicitly before any notifications are captured.
On a Debian or Ubuntu derivative:
```bash
sudo apt install python3-dbus python3-gi
```
### Installing
```bash
cd notif-vault
./install.sh
```
Copies the package into `~/.local/share/notif-vault-src`, installs
`notif-vault` and `notif-vault-daemon` into `~/.local/bin` (these wrappers do
set `PYTHONPATH` correctly), and adds an autostart entry.
```bash
notif-vault-daemon &
```
### Using it
```bash
notif-vault list # 50 most recent
notif-vault list --app discord # filter by app name substring
notif-vault list --search "build failed" # filter by summary or body text
notif-vault list --since 2h # 30m, 2h and 1d are all valid
notif-vault tail # follow new notifications live
```
### Configuration
`~/.config/notif-vault/config.json`, created from the bundled defaults the
first time the daemon starts.
| Key | Default | Meaning |
| --- | --- | --- |
| `maxHistory` | `5000` | Notifications to keep |
| `ignoreApps` | `[]` | Glob patterns; matching apps are never recorded |
| `doNotDisturb.enabled` | `false` | Turn the quiet window on |
| `doNotDisturb.start` / `.end` | `23:00` / `08:00` | Quiet window, may wrap past midnight |
| `doNotDisturb.allowUrgent` | `true` | Let critical notifications through the quiet window |
| `webhooks` | one disabled example | See below |
A webhook entry:
```json
{
"name": "phone",
"url": "https://example.com/hook",
"enabled": true,
"appPatterns": ["Thunderbird", "*Calendar*"],
"minUrgency": 2,
"headers": {},
"timeoutSeconds": 4
}
```
Urgency is the freedesktop scale: `0` low, `1` normal, `2` critical. The
request body is JSON:
```json
{ "appName": "Thunderbird", "summary": "...", "body": "...", "timestamp": 1234567890.0 }
```
### Files it writes
- `~/.config/notif-vault/config.json` — configuration
- `~/.local/share/notif-vault/notifications.db` — the notification history
- `~/.local/share/notif-vault/webhook.log` — one line per webhook attempt
Notification bodies routinely contain message previews, one-time codes and
other things you would not want lying around. The database is unencrypted
SQLite — use `ignoreApps` for anything sensitive.
---
## Tested on
Linux Mint 22.2 with the Cinnamon desktop, on an X11 session. `clip-vault`
depends on X11 outright. `notif-vault` only needs a session D-Bus and should
work on any freedesktop-compliant desktop, but Cinnamon on X11 is the only
place it has actually been run.
## Licence
No licence has been chosen yet.