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

The no-mistakes daemon uses an OS-level exclusive file lock acquired at startup to guarantee only one live instance per NM_HOME, with the kernel automatically cleaning up the lock on process exit or crash.

The no-mistakes project from kunchenguid/no-mistakes implements a robust cross-platform singleton mechanism to prevent duplicate daemon processes. The daemon singleton lock is acquired before any destructive initialization work, ensuring no two instances can race to manage the same work tree. This design relies on kernel-managed file locks rather than fragile PID files, making it inherently resilient against crashes and power failures.

How the Singleton Lock Is Acquired

The entry point acquireSingletonLock in internal/daemon/lock.go opens the lock file path via p.LockFile() and attempts to claim it through the platform-specific tryLockFile helper. If another process already holds the lock, the function immediately returns ErrSingletonLockHeld and includes diagnostic details about the current holder. This core logic spans lines 33 through 52 of internal/daemon/lock.go.

Platform-Specific Lock Implementations

Unix Advisory Lock with flock

On Unix systems, the implementation in internal/daemon/lock_unix.go calls syscall.Flock with the LOCK_EX|LOCK_NB flags to request a non-blocking exclusive advisory lock. Because the lock is bound directly to the open file descriptor, the kernel releases it automatically when the process exits, crashes, or closes the descriptor.

Windows Mandatory Lock with LockFileEx

On Windows, internal/daemon/lock_windows.go uses the LockFileEx syscall on a far-offset byte range at 0xFFFFFFFF to create a mandatory lock. Like the Unix variant, this lock is tied to the owning process and is released automatically upon termination.

Self-Cleaning Lock Lifecycle

A key strength of the daemon singleton lock is its self-cleaning property. Because the OS kernel manages the lock, a stale lock cannot survive a crashed or force-killed daemon process.

The lock file at p.LockFile() stores a small JSON record with the holder's PID and StartedAt fields for diagnostic purposes. As implemented in kunchenguid/no-mistakes, the safety guarantee rests entirely on the OS-level lock. The diagnostic write occurs at lines 53 through 62 of internal/daemon/lock.go.

Daemon Startup Integration

The singleton lock is obtained before the daemon performs any destructive startup work such as stale-run recovery, work-tree cleanup, or binding the IPC socket. This ordering ensures that no two daemon processes can race to perform initialization actions.

The following pattern demonstrates typical acquisition and error handling in kunchenguid/no-mistakes:

// 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 the lock is already held, callers can inspect the specific error:

// 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
}

Summary

  • The no-mistakes daemon enforces a single instance per NM_HOME through an OS-level exclusive file lock managed in internal/daemon/lock.go.
  • Unix systems use syscall.Flock with LOCK_EX|LOCK_NB inside internal/daemon/lock_unix.go, while Windows uses LockFileEx inside internal/daemon/lock_windows.go.
  • The lock is self-cleaning: the kernel releases it automatically if the daemon crashes or exits unexpectedly.
  • A JSON diagnostic record containing PID and StartedAt is written for troubleshooting, but the safety guarantee depends solely on the OS lock.
  • Acquisition happens before destructive startup tasks and IPC socket binding, preventing race conditions between multiple daemon instances.

Frequently Asked Questions

What happens when a second daemon instance tries to start?

The second instance fails at acquireSingletonLock with ErrSingletonLockHeld. The error includes diagnostic information about the running holder, and the caller typically logs the conflict and exits before performing any startup work.

How does the daemon singleton lock survive unexpected crashes?

It does not need to survive them. Because the lock is an OS-level kernel construct, it is released automatically when the process terminates, crashes, or closes its file descriptor. This eliminates the risk of stale locks that plague traditional PID-file approaches.

Why does the lock file contain JSON data if the OS lock provides the guarantee?

The JSON record written to the lock file stores the holder's PID and StartedAt timestamp purely for diagnostics. It helps operators identify which process currently holds the lock, but the synchronization safety is provided entirely by the kernel-managed lock, not by the contents of the file.

When exactly is the singleton lock acquired during daemon startup?

According to the no-mistakes source code, the lock is claimed before any destructive operations such as stale-run recovery, work-tree cleanup, or IPC socket binding. This strict ordering prevents two daemons from racing to modify shared state or bind to the same socket.

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 →