How the Daemon Singleton Lock Prevents Multiple Instances in no-mistakes

The no-mistakes daemon uses an OS-level exclusive file lock acquired at startup to guarantee that only one live daemon instance runs per NM_HOME, automatically releasing when the process exits or crashes.

The no-mistakes project implements a background daemon that requires exclusive control over its working environment to prevent data corruption and race conditions. To enforce single-instance semantics across platforms, the daemon employs a singleton lock mechanism that relies on kernel-managed file locking primitives rather than simple marker files.

How the Singleton Lock Works

The singleton lock guarantees exclusivity by acquiring a mandatory, non-blocking exclusive lock on a dedicated file before performing any destructive initialization work.

Lock Acquisition Process

The entry point acquireSingletonLock in [internal/daemon/lock.go](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/lock.go) (lines 33-52) orchestrates the locking sequence:

  1. Opens the lock file path provided by p.LockFile()
  2. Invokes the platform-specific tryLockFile function
  3. If another process holds the lock, returns ErrSingletonLockHeld with diagnostic information about the current holder
  4. Writes a JSON diagnostic record containing the PID and StartedAt timestamp to the lock file (lines 53-62) for debugging purposes

The safety guarantee relies entirely on the OS kernel's lock management, not the presence of the file itself.

Unix Implementation

On Linux and macOS, [internal/daemon/lock_unix.go](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/lock_unix.go) implements the lock using syscall.Flock with the flags LOCK_EX|LOCK_NB:

  • Exclusive (LOCK_EX): Prevents any other process from acquiring the lock
  • Non-blocking (LOCK_NB): Fails immediately if the lock is held rather than blocking indefinitely

The kernel automatically attaches the lock to the file descriptor and releases it when the process terminates, crashes, or closes the descriptor, eliminating the possibility of stale locks.

Windows Implementation

For Windows systems, [internal/daemon/lock_windows.go](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/lock_windows.go) uses the LockFileEx syscall on a specific byte range at offset 0xFFFFFFFF:

  • Creates a mandatory lock on the file range that other processes cannot override
  • The operating system releases the lock automatically when the owning process terminates
  • Uses a far-offset byte range to minimize collision with normal file operations

Self-Cleaning Safety Properties

Because both implementations rely on kernel-managed locking primitives rather than file existence checks, the daemon singleton lock is self-cleaning. If the daemon crashes or receives a SIGKILL, the operating system immediately releases the lock, ensuring that a subsequent restart can acquire it without manual intervention. The JSON metadata stored in the lock file (containing the holder's PID and start time) serves only diagnostic purposes and does not affect the locking semantics.

Implementation Details

The following pattern demonstrates acquiring the singleton lock during daemon initialization:

// Example: acquiring the singleton lock when starting the daemon
func startDaemon(p *paths.Paths) error {
    lock, err := acquireSingletonLock(p)
    if err != nil {
        // Another daemon is already running – report and exit
        return fmt.Errorf("cannot start daemon: %w", err)
    }
    // Ensure the lock is released on exit
    defer lock.Release()

    // … continue with daemon initialization …
    return runDaemon()
}

When handling startup failures, check for the specific singleton lock error to provide user-friendly diagnostics:

// Handling the specific ErrSingletonLockHeld error
if errors.Is(err, daemon.ErrSingletonLockHeld) {
    fmt.Println("A daemon is already running for this NM_HOME.")
    // Optionally log the holder information from the error message
}

Integration with Daemon Startup

According to the no-mistakes source code, the singleton lock is obtained before any destructive startup operations occur. Specifically, the acquisition happens prior to:

  • Stale-run recovery procedures
  • Work-tree cleanup operations
  • IPC socket binding

This ordering ensures that no two daemon instances can race to perform initialization actions that modify shared state. If acquireSingletonLock returns ErrSingletonLockHeld, the startup sequence aborts immediately, preventing the duplicate instance from interfering with the running daemon.

Summary

  • Kernel-level enforcement: The daemon uses syscall.Flock (Unix) and LockFileEx (Windows) to create OS-managed exclusive locks, not merely checking for file existence.
  • Automatic cleanup: The lock releases automatically when the process exits, crashes, or is killed, preventing stale lock scenarios common with PID files.
  • Diagnostic metadata: The lock file stores JSON data with the holder's PID and start time at [internal/daemon/lock.go](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/lock.go) lines 53-62, but safety relies on the OS lock mechanism.
  • Startup protection: The lock acquisition occurs in acquireSingletonLock before any destructive initialization work, ensuring exclusive access to NM_HOME resources.

Frequently Asked Questions

What happens if the daemon crashes while holding the singleton lock?

The operating system kernel automatically releases the file lock when the process terminates, regardless of whether the shutdown is clean or abrupt. Because the lock is attached to the process's file descriptor rather than managed by the application, a crashed daemon cannot leave a "stale" lock that would block future starts.

How does the daemon singleton lock differ from a simple PID file?

Traditional PID files rely on the existence of a file containing a process ID, which requires manual cleanup if the process crashes without deleting the file. The no-mistakes daemon singleton lock uses kernel-managed advisory (Unix) or mandatory (Windows) locks that evaporate when the process dies, providing stronger guarantees without risking stale lock files.

Can the singleton lock be bypassed or disabled?

No. The acquireSingletonLock function is a mandatory step in the daemon startup sequence hardcoded in internal/daemon/lock.go. There is no configuration option to disable it because allowing multiple daemon instances per NM_HOME would violate the architectural assumptions of the work-tree management and IPC systems.

Where is the lock file stored?

The lock file path is determined by the LockFile() method on the paths configuration object (typically internal/paths/paths.go), which returns the full path to daemon.lock under the NM_HOME directory. This ensures that separate no-mistakes installations in different home directories can run concurrently without interfering with each other.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →