Library / SDK
rjeczalik/notify avatar
rjeczalik/notify

notify: filesystem event notifications in Go across four native backends

File system event notification library on steroids.

936 stars142 forksGoMIT

At a glance

What is it?
notify is a Go library that reports filesystem events through inotify on Linux, kqueue on the BSDs, FSEvents on macOS and ReadDirectoryChangesW on Windows, behind one API, with recursive and non-recursive watch trees. It fits Go services that react to files appearing on disk, and it does not fit a project that needs a maintained release stream, since the last tagged version is v0.9.3 from 2023.
Who is it for?
Adopt notify when you need one filesystem watching API that behaves the same on Linux, macOS, the BSDs and Windows, and you are willing to depend on a library whose tagged release is v0.9.3 from 2023-01-13 with commits continuing afterwards. Skip it if you need a maintained release cadence, if your target platform has no backend and you need more than a stub, or if you need semantic event classification beyond the raw create, write, remove and rename set the library reports.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 144 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One API, four operating system backends

notify is described in one line as a filesystem event notification library on steroids, and the repository layout explains what that means. The package root contains a set of paired files per operating system: event_inotify.go, event_kqueue.go, event_fsevents.go, event_readdcw.go, event_fen.go and event_stub.go, each with a matching watcher_ file such as watcher_inotify.go, watcher_kqueue.go, watcher_fsevents.go, watcher_readdcw.go, watcher_fen.go and watcher_notimplemented.go.

The build badge names the backends outright, listing inotify plus FSEvents plus kqueue for one platform and ReadDirectoryChangesW for another. So a Go program written against this library gets Linux inotify, macOS FSEvents, kqueue on the BSDs and the Windows directory change API through one interface, instead of writing four implementations.

That portability is the real product. The alternative is not one competitor but the platform APIs themselves, four different event models with different limitations, which means a project that watches files ends up with build-tagged files and a behavioural matrix to maintain. The shared parts of the package are small and predictable: node.go, notify.go, util.go, event.go and watcher.go hold the common types, and the per-platform files supply the mechanism.

The dependency list reflects the same economy. go.mod names the module github.com/rjeczalik/notify, requires Go 1.26, and lists a single external dependency, golang.org/x/sys at v0.45.0. Everything else is the standard library.

Recursive watching is a separate tree abstraction

The interesting design decision visible in the file list is that recursive watching is not a flag on a watcher but a different object. The repository contains tree.go alongside tree_recursive.go and tree_nonrecursive.go, with matching test files for each.

That separation exists because the platforms disagree. Native recursive watching is available on some systems and not others, so a portable library has to build the recursion itself when the platform will not. Splitting the tree into recursive and non-recursive implementations keeps the platform-specific work confined to the layer that can use the native facility and pushes the manual walk into the layer that cannot.

There is a matching split on the event side, with event_trigger.go standing apart from the per-platform event files. A trigger event is the synthetic kind that tells a watcher that something happened below a watched path rather than naming a specific file, and it is the event a recursive tree needs when the platform reports only that a directory changed and not which entry inside it changed.

The test files follow the same discipline. There are sync_unix_test.go and sync_readdcw_test.go for the synchronous APIs, notify_inotify_test.go and notify_readdcw_test.go for the asynchronous ones, util_darwin_test.go and util_unix_test.go for the platform helpers, and separate example files for FSEvents, inotify and ReadDirectoryChangesW. The API documentation lives on godoc at godoc.org/github.com/rjeczalik/notify, and the repository does not repeat it in the README, which points there instead.

Windows and macOS bring their own constraints

Two of the backends carry visible complications in the file names.

On macOS there are two variants of the FSEvents watcher, watcher_fsevents_cgo.go and watcher_fsevents.go, alongside the same pair for the event type, event_fsevents.go. The presence of a cgo and a non-cgo build for the same backend says that the implementation can be built two ways, and that one of them depends on cgo being available in the build environment. If you cross-compile for darwin in a static build pipeline, that is the constraint to check first, because a build that works on your laptop and fails in CI is a classic outcome of this kind of split.

On Windows the backend is ReadDirectoryChangesW, and there is a dedicated sync_readdcw_test.go alongside the watcher and event files, which points to the Windows path having both a synchronous and an asynchronous form worth testing separately. Windows also splits the watcher by architecture, with watcher_inotify.go and watcher_inotify_amd64.go, so the inotify implementation has an amd64-specific file rather than a single portable one.

The fallback path deserves attention too. watcher_notimplemented.go and event_stub.go exist for platforms with no backend. They mean the library compiles everywhere rather than failing to build, which is convenient for a cross-platform binary, and it also means that on an unsupported platform you get a stub instead of events. A program that silently receives nothing is a harder failure to diagnose than one that refuses to compile.

Adding it to a project

The installation instruction in the project documentation is a single command:

bash
~ $ go get -u github.com/rjeczalik/notify

In a modern module-based project this is not a step you run by hand, since the dependency lands in go.mod and go.sum through the module tooling. The published repository itself is a module, with the module path github.com/rjeczalik/notify, and its own go.sum checked in, so the version you resolve is reproducible.

What the README does not give you is a code sample. It points at the GoDoc page for the API and lists the projects that use the library, which is the intended path for learning the interface. The repository does carry working examples under the test files, example_fsevents_test.go, example_inotify_test.go and example_readdcw_test.go, so a per-platform example is available in the source even though the README leaves it out.

The one build flag worth knowing about is cgo, and it is not mentioned in the README at all. It is visible only in the file names, which means a user who hits a build failure on macOS has to deduce the cause from the tree rather than read it. That is a small documentation gap rather than a defect, but it is the kind of gap that costs an afternoon.

The release cadence is the weak point

Here the honest reading of the release history matters. The tagged releases are v0.9.3 from 2023-01-13, v0.9.2 from 2018-09-27 and v0.9.1 from 2018-08-30. That is a five-year gap between the two most recent tags, with a seven-year gap before them.

The repository is not archived, and the last push was on 2026-06-01, so work is happening on the default branch master. The module also requires Go 1.26, a modern toolchain, and the dependency on golang.org/x/sys is at v0.45.0, both of which indicate the current code is not what v0.9.3 shipped. In other words, the tagged release and the code on the branch have diverged, and the go.mod in the branch is the only accurate description of what you would actually get if you depended on master.

For an application that pins a version in go.mod, that split has a practical cost. A library dependency resolved to v0.9.3 gives you a 2023 snapshot that predates the current toolchain requirements, while taking master gives you untagged code with no release notes to diff against. There is a CHANGELOG in the tree, which helps, but the gap between tags means the commit history is the real changelog.

The continuous integration setup also looks dated, with .travis.yml and appveyor.yml at the root rather than a GitHub Actions workflow. That tells you the build verification for the tagged releases was set up on services that are no longer the norm, so do not assume a passing CI badge for a current release means a current toolchain was exercised.

Adopters, licence and where it fits

The project documentation lists the tools that use notify, and they describe the use case better than any feature list would: devd, modd, gow, TinyJPG and syncthing. These are developer tools and a file synchronisation daemon, which is to say software that watches a working directory and reacts when a file changes. That is the demand the library serves, hot reload for a web server, recompile on save, sync when a file lands.

The licence is MIT, so there is no copyleft obligation and no attribution requirement beyond keeping the notice, which is one fewer thing to review when you adopt a dependency that sits in the bottom of your stack.

On the question of when it is the wrong tool, the boundary is clearer than the documentation admits. The library reports filesystem events, and it reports the native events the platform gives, which means the set of things you can distinguish is the set the operating system distinguishes. You get create, write, remove and rename style notifications on files and directories, not an understanding of what a change means. If your application needs to know that a file was edited by a specific editor, or that a change came from a particular process, or that content changed rather than metadata, this library does not provide that and no configuration will add it.

There is also the matter of a Go environment that already has a plan for this. If you are on Linux and your whole application is Linux, the standard library plus a small amount of platform code may serve you better. The library earns its place when the same binary has to watch directories on Windows, macOS and Linux without three separate implementations, which is exactly the case for the tools listed as users.

Editorial conclusion

Adopt notify when you need one filesystem watching API that behaves the same on Linux, macOS, the BSDs and Windows, and you are willing to depend on a library whose tagged release is v0.9.3 from 2023-01-13 with commits continuing afterwards. Skip it if you need a maintained release cadence, if your target platform has no backend and you need more than a stub, or if you need semantic event classification beyond the raw create, write, remove and rename set the library reports. Verify first on each platform you ship, by running the examples under the package tests, because the behaviour differences sit in the per-platform files rather than in the shared API.

Frequently asked questions

Which platforms does the notify library support?

The build badges name the backends: inotify, FSEvents and kqueue for the Unix side, and ReadDirectoryChangesW for Windows. The package root carries a matching event file and watcher file for each, plus a stub and a not-implemented watcher for platforms with no backend.

How do I install the notify Go library?

The documented command is go get -u github.com/rjeczalik/notify. The library is a Go module with the module path github.com/rjeczalik/notify, requires Go 1.26, and depends only on golang.org/x/sys at v0.45.0. The API itself is documented on GoDoc.

Does notify support watching directories recursively?

Yes, and the implementation is split by platform capability. The repository contains tree.go with separate tree_recursive.go and tree_nonrecursive.go implementations, so recursion is handled natively where the platform allows it and manually where it does not.

Which projects use the notify library?

The documentation lists devd, modd, gow, TinyJPG and syncthing among the projects that use it, which are developer tools and a file synchronisation daemon that watch a working directory and react to file changes.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. rjeczalik/notify on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rjeczalik-notify.svg)](https://hysenlabs.com/projects/rjeczalik-notify)

Was this useful?