(\__/)
(='.'=) ~ a gentle nudge to keep your system fresh ~
(")_(")
A gentle nudge to keep your system fresh.
Lightweight Linux desktop update manager that checks for available packages after login and asks if you'd like to update. Supports multiple distributions and package managers (apt, dnf, pacman, zypper), 5 notification backends, Flatpak and Snap stores, scheduling with deferral, pre-upgrade snapshots, and a friendly bunny mascot. Pure bash. Zero compiled dependencies. 332 tests.
nudge is in active development and publicly available for early adopters. If you're running a Linux desktop and want to help improve it, install it, use it, and report what breaks. Bug reports, edge cases, quirky distro behaviour, notification backends not cooperating — all of it is useful.
How to help:
- Install nudge on your daily driver (see Quick Start)
- Use it normally — let it run at login, try the different notification backends, test scheduling and deferral
- File issues at github.com/otmof-ops/nudge/issues with your distro, desktop environment, and what happened vs what you expected
The more variety of setups it gets tested on, the faster it stabilises. Every bug report helps.
Most Linux update tools are either silent and automatic, locked to one desktop environment, or require heavyweight runtimes. nudge is a lightweight system update notification tool that asks before acting — a consent-first Linux update manager built for every desktop.
| Feature | nudge | unattended-upgrades | GNOME Software | topgrade |
|---|---|---|---|---|
| Asks before updating | Yes | No (auto) | Yes | No (runs all) |
| Multi-distro | 4 pkg mgrs | Debian only | GNOME only | Multi |
| Flatpak + Snap | Yes | No | Partial | Yes |
| Lightweight (pure bash) | Yes | Yes | No | No (Rust) |
| Notification backends | 5 | 0 | 1 | 0 |
| Scheduling + deferral | Yes | Yes | No | No |
| Pre-upgrade snapshots | Yes | No | No | No |
| Test suite | 332 | — | — | — |
- Consent-first updates — never updates without your explicit approval; Update Now, Remind Later, or Not Now
- Multi-distro support — auto-detects apt, dnf, pacman, and zypper across Ubuntu, Fedora, Arch, and openSUSE
- 5 notification backends — dunstify, kdialog, zenity, gdbus, and notify-send with automatic detection
- Flatpak + Snap — checks universal package stores alongside your system package manager
- Scheduling and deferral — login, daily, or weekly checks with customizable "remind me later" intervals
- Pre-upgrade snapshots — optional timeshift, snapper, or btrfs snapshots before every update
- Security priority classification — highlights critical and security updates (kernel, openssl, glibc, sudo)
- Friendly mascot — the Nudge Bunny with 10 ASCII poses and 100+ rotating dialogue lines
- JSON output — structured output for scripting, waybar/polybar integration, and automation
- JSONL history — searchable update history with date filtering
- Zero telemetry — no data collection, no phone-home, fully auditable source
- User-space install — everything lives in
~/.local/, no root persistence, no daemon
One-liner install:
bash <(curl -sL https://raw.githubusercontent.com/otmof-ops/nudge/main/setup.sh) --install --defaultsOr clone and run the setup TUI:
git clone https://github.com/otmof-ops/nudge.git
cd nudge
./setup.shThat's it. nudge will notify you at next login when updates are available.
Login/Timer → nudge.sh → load config → acquire lock → schedule guard → delay →
network probe (curl/wget/ping) → detect package manager → lock check →
count updates (system + flatpak + snap) → if 0: exit → build preview with
priority classification → show dialog → "Update Now": snapshot → upgrade →
reboot check | "Remind Later": defer → write duration | "Not Now": exit →
write history → exit with named code
nudge v2.0.0 uses a modular library design — the main script is a ~480-line dispatcher that sources 15 modules from lib/:
| Module | Purpose |
|---|---|
lib/output.sh |
Exit codes, logging, JSON output, content rendering |
lib/config.sh |
Safe config parser, validation, migration |
lib/lock.sh |
flock-based instance locking |
lib/network.sh |
Multi-method network probe (curl/wget/ping) |
lib/pkgmgr.sh |
apt/dnf/pacman/zypper + flatpak + snap |
lib/notify.sh |
dunstify/kdialog/zenity/gdbus/notify-send backends |
lib/schedule.sh |
Scheduling, interval guards, deferral |
lib/history.sh |
JSONL history log and viewer |
lib/safety.sh |
Pre-upgrade snapshots, reboot detection |
lib/selfupdate.sh |
GitHub release self-update check |
lib/errorreport.sh |
Crash reports and automated GitHub issue filing |
lib/tui.sh |
TUI rendering primitives — bunny, menus, colors |
lib/bunny-poses.sh |
10 ASCII art pose functions |
lib/bunny-dialogue.sh |
100+ rotating dialogue messages, random picker |
lib/bunny.sh |
Bunny orchestrator — render, season, context, state |
| Distribution | Package Manager | Status |
|---|---|---|
| Ubuntu / Debian | apt |
Full support |
| Fedora / RHEL | dnf |
Full support |
| Arch Linux | pacman |
Full support |
| openSUSE | zypper |
Full support |
| Any (Flatpak) | flatpak |
Auto-detected |
| Any (Snap) | snap |
Auto-detected |
| Backend | Interactive | Defer | Auto-Dismiss | Detection Order |
|---|---|---|---|---|
dunstify |
Yes (actions) | Yes | Yes | 1st |
kdialog |
Yes (dialog) | Yes | Yes (timeout) | 2nd |
zenity |
Yes (dialog) | Yes | Yes (native) | 3rd |
gdbus |
Passive | No | Yes | 4th |
notify-send |
Passive | No | N/A | 5th |
- Linux desktop with one of: apt, dnf, pacman, or zypper
- One of: dunstify, kdialog, zenity, gdbus, or notify-send
- A terminal emulator (konsole, gnome-terminal, xfce4-terminal, or xterm)
- Optional: flatpak, snap, timeshift/snapper (for snapshots)
git clone git@github.com:otmof-ops/nudge.git
cd nudge
./setup.shThe unified TUI walks you through install, configure, update, and uninstall — all guided by the Nudge Bunny.
./setup.sh --install --defaults./setup.sh --install --unattendedbash <(curl -sL https://raw.githubusercontent.com/otmof-ops/nudge/main/setup.sh) --install --defaults./setup.sh --install --upgradePreserves your existing config, runs migration to add new keys with defaults.
| Flag | Description |
|---|---|
--install |
Install nudge |
--uninstall |
Uninstall nudge |
--update |
Check and install updates |
--config-only |
Open configure flow only |
--defaults |
Skip prompts, use default settings |
--unattended |
Non-interactive install (implies --defaults) |
--upgrade |
In-place upgrade, preserve config |
--systemd |
Use systemd user timer for autostart |
--xdg |
Use XDG autostart entry |
--no-color |
Disable colored output |
--prefix=PATH |
Custom install prefix (default: $HOME) |
--dry-run |
Show what would happen, change nothing |
--keep-config |
Uninstall: preserve config directory |
--check |
Update: just check, print version, exit |
Legacy install.sh and uninstall.sh wrappers are still supported for backward compatibility.
| File | Purpose |
|---|---|
~/.local/bin/nudge.sh |
Main dispatcher script |
~/.local/lib/nudge/*.sh |
Library modules (14) |
~/.config/nudge/nudge.conf |
Configuration (31 keys) |
~/.config/autostart/nudge.desktop |
XDG autostart entry |
~/.config/systemd/user/nudge.timer |
systemd timer (if selected) |
~/.config/systemd/user/nudge.service |
systemd service (if selected) |
~/.local/share/bash-completion/completions/nudge |
Bash completion |
~/.local/share/man/man1/nudge.1 |
Man page |
~/.local/share/nudge/ |
History, state, deferral files |
./setup.sh --uninstallOr via the TUI: run ./setup.sh and choose option 2.
Shows what will be removed and asks for confirmation. Cleans up systemd timer, bash completion, man page, history, and state files.
| Flag | Description |
|---|---|
--yes, -y |
Skip confirmation prompts |
--keep-config |
Preserve ~/.config/nudge/ |
--no-color |
Disable colored output |
Edit ~/.config/nudge/nudge.conf:
| Option | Type | Default | Description |
|---|---|---|---|
ENABLED |
bool | true |
Enable or disable nudge |
DELAY |
int | 45 |
Seconds to wait after login |
CHECK_SECURITY |
bool | true |
Highlight security updates |
AUTO_DISMISS |
int | 0 |
Auto-dismiss dialog (seconds, 0 = never) |
UPDATE_COMMAND |
string | sudo apt update && sudo apt full-upgrade |
Update command |
| Option | Type | Default | Description |
|---|---|---|---|
NETWORK_HOST |
string | archive.ubuntu.com |
Connectivity check host |
NETWORK_TIMEOUT |
int | 5 |
Check timeout (seconds) |
NETWORK_RETRIES |
int | 2 |
Retry count |
OFFLINE_MODE |
enum | skip |
skip / notify / queue |
| Option | Type | Default | Description |
|---|---|---|---|
NOTIFICATION_BACKEND |
enum | auto |
auto/kdialog/zenity/dunstify/gdbus/notify-send/none |
DUNST_APPNAME |
string | nudge |
App name for dunst |
PREVIEW_UPDATES |
bool | true |
Show package list before prompting |
SECURITY_PRIORITY |
bool | true |
Show critical/security packages first |
| Option | Type | Default | Description |
|---|---|---|---|
SCHEDULE_MODE |
enum | login |
login / daily / weekly |
SCHEDULE_INTERVAL_HOURS |
int | 24 |
Hours between checks |
DEFERRAL_OPTIONS |
string | 1h,4h,1d |
"Remind me later" choices |
| Option | Type | Default | Description |
|---|---|---|---|
PKGMGR_OVERRIDE |
string | (empty) | Force specific package manager |
FLATPAK_ENABLED |
enum | auto |
true / false / auto |
SNAP_ENABLED |
enum | auto |
true / false / auto |
| Option | Type | Default | Description |
|---|---|---|---|
HISTORY_ENABLED |
bool | true |
Write history records |
HISTORY_MAX_LINES |
int | 500 |
Rotate history at this count |
LOG_FILE |
string | (empty) | Log file path (empty = none) |
LOG_LEVEL |
enum | info |
debug / info / warn / error |
JSON_OUTPUT |
bool | false |
Default to JSON output |
| Option | Type | Default | Description |
|---|---|---|---|
REBOOT_CHECK |
bool | true |
Detect reboot needed post-upgrade |
SNAPSHOT_ENABLED |
bool | false |
Snapshot before upgrade |
SNAPSHOT_TOOL |
enum | auto |
auto / timeshift / snapper / btrfs |
| Option | Type | Default | Description |
|---|---|---|---|
SELF_UPDATE_CHECK |
bool | true |
Check for newer nudge version |
SELF_UPDATE_CHANNEL |
enum | stable |
stable / beta |
| Option | Type | Default | Description |
|---|---|---|---|
BUNNY_PERSONALITY |
enum | disney |
classic (neutral) / disney (Thumper baby voice) |
nudge --version # Print version
nudge --help # Show help
nudge --dry-run # Run checks, no dialogs
nudge --check-only # Print update count
nudge --check-only --json # JSON output
nudge --verbose # Verbose logging
nudge --history # Show last 20 history records
nudge --history 50 # Show last 50 records
nudge --history --json # Raw JSONL dump
nudge --history --since DATE # Filter by date
nudge --defer 4h # Defer next check
nudge --self-update # Download latest version
nudge --config # Print resolved config
nudge --validate # Validate config
nudge --report # Show crash reports
nudge --report --file # File latest crash as GitHub issue
nudge --report --clear # Clear all crash reports
nudge --migrate # Run config migration| Code | Constant | Meaning |
|---|---|---|
| 0 | EXIT_OK |
No updates / completed |
| 1 | EXIT_UPDATES_DECLINED |
User said "Not Now" |
| 2 | EXIT_UPDATES_APPLIED |
Updates ran successfully |
| 3 | EXIT_UPDATES_FAILED |
Update command failed |
| 4 | EXIT_DISABLED |
ENABLED=false |
| 5 | EXIT_NETWORK_FAIL |
Network check failed |
| 6 | EXIT_PKG_LOCK |
Package manager locked |
| 7 | EXIT_ALREADY_RUNNING |
Another instance running |
| 8 | EXIT_NO_BACKEND |
No notification backend |
| 9 | EXIT_DEFERRED |
User chose "Remind later" |
| 10 | EXIT_CONFIG_ERROR |
Config validation failed |
| 11 | EXIT_INTERRUPTED |
SIGINT/SIGTERM/SIGHUP |
| 12 | EXIT_SNAPSHOT_FAILED |
Snapshot failed, aborted |
| 13 | EXIT_REBOOT_PENDING |
Reboot required |
With --json, nudge emits a single JSON object:
{
"nudge_version": "2.0.0",
"timestamp": "2026-03-19T09:15:00+08:00",
"exit_code": 2,
"exit_reason": "UPDATES_APPLIED",
"pkg_manager": "apt",
"updates": {"total": 14, "security": 3, "critical": 1, "flatpak": 2, "snap": 0},
"packages": [{"name": "openssl", "from": "3.0.10", "to": "3.0.11", "priority": "CRITICAL"}],
"reboot_required": false,
"snapshot_id": null,
"deferred": false,
"duration_seconds": 4
}| Mode | Behavior |
|---|---|
login (default) |
Check every login |
daily |
Check once per SCHEDULE_INTERVAL_HOURS |
weekly |
Check once per SCHEDULE_INTERVAL_HOURS × 7 |
Use XDG autostart (default) or systemd user timer:
./install.sh --systemd # Install with systemd timer
./install.sh --xdg # Install with XDG autostartScreenshots coming soon.
make test # Requires bats-coremake lint # Requires shellcheckman ./share/man/nudge.1 # Preview locallynudge doesn't run at login:
- Check autostart:
ls ~/.config/autostart/nudge.desktoporsystemctl --user status nudge.timer - Verify
ENABLED=truein~/.config/nudge/nudge.conf
"No supported notification backend found":
- Install a dialog tool:
sudo apt install kdialog(KDE),sudo apt install zenity(GNOME/XFCE), orsudo apt install dunst(tiling WMs)
Network check always fails:
- Try a different
NETWORK_HOST(e.g.,1.1.1.1) - Increase
NETWORK_TIMEOUTandNETWORK_RETRIES - Set
OFFLINE_MODE="notify"to see when network is down
Config errors:
- Run
nudge --validateto check config - Run
nudge --configto see resolved values - Run
nudge --migrateif upgrading from v1.x
Package manager not detected:
- Set
PKGMGR_OVERRIDE="apt"(or dnf/pacman/zypper) in config
nudge runs your configured update command when you accept. Please read SAFETY.md — it covers the risks of system updates, PPA concerns, and shared-account considerations.
To report a security vulnerability, do not open a public issue. See SECURITY.md for the responsible disclosure process.
Contributions are welcome. See CONTRIBUTING.md for the Contributor License Agreement, code conventions, and PR process.
| Metric | Value |
|---|---|
| Test suite | 332+ tests across 16 files |
| Library modules | 15 modular .sh files |
| Named exit codes | 13 scriptable exit codes |
| Config keys | 33 validated keys |
| Notification backends | 5 with auto-detection |
| Package managers | 4 + Flatpak + Snap |
| Bunny poses | 10 ASCII art poses |
| Bunny dialogue lines | 100+ rotating messages |
| CI pipeline | ShellCheck + BATS on every PR |
- ROADMAP.md — Feature roadmap and deliberately out-of-scope items
- CHANGELOG.md — Release history
- CREDITS-AND-COMMUNITY.md — Attribution and IP framework
- CODE_OF_CONDUCT.md — Community standards
- STANDARDS.md — Repository standards and conventions
Proprietary source-available. Copyright (c) 2026 OFFTRACKMEDIA Studios (ABN: 84 290 819 896). All rights reserved. Free for personal, non-commercial use. Commercial use requires written consent. See LICENSE and EULA/OFFTRACKMEDIA_EULA_2025.txt.
OFFTRACKMEDIA Studios — Building Empires, Not Just Brands.