Summary
nixpush is a NixOS notification-dispatch mechanism that separates what sends an
alert from where the alert goes. Modules and scripts call
nixpush send --channel alerts with a message, and one NixOS-level channel
table decides which provider delivers it.
The core is stateless by default: exactly one synchronous delivery attempt per
send, reported through a three-class exit code, with no daemon and no queue. Two
per-channel opt-ins exist without changing that default: a crash-safe on-disk
spool (durable = true) and a fallback destination for unseal failures or
provider hard rejections.
What it is
- A NixOS option schema (
nixosModules.core) defining namedchannelsbound to pluggableproviders, rendered to/etc/nixpush/channels.json. - A synchronous
nixpush sendCLI (packagenixpush) performing one delivery attempt per invocation. - A first-party
ntfyprovider (nixosModules.ntfy-provider, packagenixpush-provider-ntfy) implemented withcurlandjq. - A Nix-level
mkSendCommandhelper exposed both as flakeliband asconfig.nixpush.lib.mkSendCommand. - Declared checks (
checks/assertions.nix,checks/behavior.nix) and supporting material (docs/faq.md,docs/rationale.md,CONTRIBUTING.mdprovider contract).
It is not a notification service itself, nor a queue, scheduler, or dashboard. Providers are small executables owned outside the core.
How it works
A channel names a provider and its parameters; a provider is any executable
satisfying the stdin, environment, and exit-code contract documented in
CONTRIBUTING.md. The referenced revision’s README states the contract as
JSON-in and exit-code-out, so community providers can be written in any
language.
Evaluation renders the channel table to JSON on the target host. At runtime
nixpush send reads that file, resolves the named channel (or
nixpush.defaultChannel), executes the provider once, and exits with one of
three classes. Nothing listens, retries, or persists unless the channel sets
durable = true, which adds the file-backed spool, or sets a fallback
destination used when a secret fails to unseal or the primary provider
hard-rejects.
The flake exposes nixosModules.default as core plus the ntfy provider for
single-provider consumers; multi-provider setups import core and add provider
modules explicitly.
How to configure it
Import the module bundle for the wanted provider set, then declare channels
under the nixpush.* option surface observed in modules/default.nix:
nixpush.channels.<name>— provider binding plus per-channel parameters.nixpush.providers.<name>— provider registration surface.nixpush.defaultChannel— channel used when send omits--channel.nixpush.spoolDir— spool location for durable channels.
The ntfy provider adds its own surface (see modules/providers/ntfy.nix),
including server, topic, and token-file settings. Callers then use
nixpush send --channel <name> or the mkSendCommand helper; repointing
channels.<name>.provider changes delivery without touching callers. Review
docs/faq.md and docs/rationale.md for the numbered design decisions
referenced from code comments.
Tutorial
This example uses invented values against the real option interface from the
referenced revision. The interface shape below was evaluated with the actual
nixosModules.default bundle (core plus ntfy provider) at the pinned revision
using synthetic values only; no message was delivered.
# Synthetic example: invented server, topic, and channel choices.
{
imports = [ inputs.nixpush.nixosModules.default ];
nixpush = {
enable = true;
defaultChannel = "alerts";
channels.alerts.provider = "ntfy";
ntfy = {
enable = true;
serverUrl = "https://ntfy.example";
topic = "demo-alerts";
};
};
}Importing the bundle alone registers nothing: delivery only exists because
ntfy.enable is set and the channel names the registered "ntfy" provider.
Sending from a script uses the same channel name regardless of backend:
# Synthetic example: public placeholder only.
nixpush send --channel alerts "demo message"Treat the exit code as the delivery verdict: the referenced revision documents three outcome classes rather than a boolean.
Evidence and limits
This page is bound to public revision
fe83da882b39aa8099441e4435a69ab451e6d0b0. That tree contains the flake
outputs, modules/default.nix, modules/providers/ntfy.nix,
pkgs/nixpush.nix, pkgs/nixpush-provider-ntfy.nix, lib/default.nix,
lib/nixpush.sh, checks/assertions.nix, checks/behavior.nix, and the
docs/ plus CONTRIBUTING.md material discussed above.
CNIX has not independently established a successful evaluation, build, delivery, or runtime result for that revision. The tutorial interface above was evaluated against the real modules at that revision with synthetic values: the channel binding, default channel, and ntfy settings pass type checking and the default-channel assertion holds. No message was delivered. Durability behavior under crash conditions and fallback semantics are described from source structure, not from reproduced runs.
Related
nixpush addresses notification dispatch only. Host-memory policy, remote access, compositor configuration, shell environment, file sharing, and tiny -VM server policy are covered by sibling Corbet Nix projects, each with its own revision-addressed concept. Its relationship to other Corbet Nix projects is currently a shared design direction, not evidence of runtime integration.