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

> Learn how the no-mistakes daemon singleton lock uses OS-level file locking to prevent multiple daemon instances, ensuring kernel cleanup on exit or crash.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: internals
- Published: 2026-07-19

---

**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`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/lock.go).

## Platform-Specific Lock Implementations

### Unix Advisory Lock with flock

On Unix systems, the implementation in [`internal/daemon/lock_unix.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/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`:

```go
// 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:

```go
// 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`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/lock.go).
- **Unix** systems use `syscall.Flock` with `LOCK_EX|LOCK_NB` inside [`internal/daemon/lock_unix.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/lock_unix.go), while **Windows** uses `LockFileEx` inside [`internal/daemon/lock_windows.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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.