# How AgentsView Daemon Mode Works for Background Serving

> Discover how AgentsView daemon mode operates for background serving. Learn about its long-running HTTP server, exclusive SQLite archive locking, and automatic shutdown features.

- Repository: [Kenn Software/agentsview](https://github.com/kenn-io/agentsview)
- Tags: how-to-guide
- Published: 2026-07-04

---

**AgentsView daemon mode launches a long-running HTTP server that exclusively locks the SQLite archive, writes a runtime discovery record, and optionally shuts down automatically after idle periods.**

AgentsView implements a robust **daemon mode for background serving** that transforms the CLI into a persistent HTTP API server. When you execute `agentsview serve`, the application transitions from a short-lived command to a background process that maintains exclusive ownership of the database while exposing REST endpoints for other CLI commands and external integrations.

## Startup Lock and Exclusive Database Ownership

The daemon initialization begins with an exclusive advisory lock to prevent database corruption. In [`cmd/agentsview/daemon_runtime.go`](https://github.com/kenn-io/agentsview/blob/main/cmd/agentsview/daemon_runtime.go), the function `MarkDaemonStarting` (also referenced as `markDaemonStarting`) creates an advisory lock file named `runtime.lock` in the data directory immediately after the CLI parses the configuration.

This lock guarantees that only a single process may start the server at any given time. If another process attempts to start while the lock is held, the operation fails. The daemon also implements `rejectLiveWritableDaemonBeforeDirectWrite` to block other processes from opening the SQLite archive for writes while the daemon is active, ensuring data consistency.

## Runtime Record and Service Discovery

After the HTTP listener is created via `daemon.Listen`, the daemon writes a **Kit daemon runtime record** using `WriteDaemonRuntime` in [`cmd/agentsview/daemon_runtime.go`](https://github.com/kenn-io/agentsview/blob/main/cmd/agentsview/daemon_runtime.go). This record contains critical metadata:

- **Host** and **port** of the listening interface
- **Version** string for compatibility checking
- **Read-only** flag indicating if mutating operations are allowed
- Optional **Caddy PID** for reverse proxy management

Other CLI commands locate the daemon by calling `FindDaemonRuntime`, which scans the data directory for this record and probes the stored endpoint. This mechanism enables commands like `agentsview stats` and `agentsview sync` to discover and communicate with the background server without hardcoded network addresses.

## HTTP Server and Listener Architecture

The server initialization flow in [`internal/server/server.go`](https://github.com/kenn-io/agentsview/blob/main/internal/server/server.go) coordinates the network stack. The `Server.ListenAndServe` method builds a TCP address from the configuration and delegates to `daemon.Listen` from the `go.kenn.io/kit/daemon` package, which supplies the actual listener and manages low-level socket options.

Once running, the daemon serves the Single Page Application (SPA) and API endpoints at `/api/v1/...`. The server maintains a mutex-protected reference to the HTTP server instance (`s.httpSrv`) to enable safe shutdown operations.

## Idle Timeout and Automatic Shutdown

When the daemon launches as a background child process (`runningAsBackgroundChild`), the system creates an `IdleTracker` configured via `daemonIdleTimeout`. Implemented in [`internal/server/idle.go`](https://github.com/kenn-io/agentsview/blob/main/internal/server/idle.go), this tracker monitors HTTP request activity.

If no requests occur for the configured duration, the tracker invokes `stop()`, which triggers a graceful shutdown. This feature prevents resource exhaustion from forgotten background processes while allowing the daemon to persist during active use periods.

## Graceful Shutdown Process

The shutdown sequence in [`internal/server/server.go`](https://github.com/kenn-io/agentsview/blob/main/internal/server/server.go) ensures clean resource release. The `Server.Shutdown` method:

1. Stops the HTTP server using `http.Server.Shutdown(ctx)`, which stops accepting new connections while allowing existing requests to complete
2. Closes any on-demand sync engine instances
3. Releases the startup lock

After the server returns, the runtime record is removed via `RemoveDaemonRuntime` in [`cmd/agentsview/daemon_runtime.go`](https://github.com/kenn-io/agentsview/blob/main/cmd/agentsview/daemon_runtime.go), signaling to other processes that the daemon is no longer available.

## Read-Only Mode and Safety Features

The daemon supports a **read-only mode** activated via `--no-sync` or when running as a PG "push" server. In this configuration, the runtime record's `read_only` flag is set to true, and the HTTP backend in [`internal/service/http.go`](https://github.com/kenn-io/agentsview/blob/main/internal/service/http.go) returns `501 Not Implemented` for mutating endpoints.

This safety feature makes the daemon suitable for low-privilege clients that should only query data without modifying the SQLite archive. The system also implements **automatic restart detection**: when a daemon crashes, its lock disappears, allowing subsequent CLI invocations to start a fresh daemon and write a new runtime record.

## Practical Usage Examples

Start the daemon in the foreground, replacing any existing instance:

```bash
agentsview serve --replace

```

Start the daemon as a background process:

```bash
agentsview serve --replace &

```

Check daemon status using the runtime record:

```bash
agentsview serve status

# → "agentsview daemon is active at http://127.0.0.1:8080"

```

Stop a running daemon gracefully:

```bash
agentsview serve stop

```

Discover the daemon endpoint from Go code:

```go
import "go.kenn.io/agentsview/internal/daemon"

func getDaemonURL(dataDir string) (string, error) {
    rt := daemon.FindDaemonRuntime(dataDir)
    if rt == nil {
        return "", fmt.Errorf("no daemon found")
    }
    return fmt.Sprintf("http://%s:%d", rt.Host, rt.Port), nil
}

```

Implement graceful shutdown in extensions:

```go
func (s *Server) Shutdown(ctx context.Context) error {
    s.mu.RLock()
    srv := s.httpSrv
    s.mu.RUnlock()
    if srv != nil {
        _ = srv.Shutdown(ctx) // stops accepting new connections
    }
    // Runtime record removal handled by caller
    return nil
}

```

## Summary

- **Exclusive locking** via `MarkDaemonStarting` and `runtime.lock` prevents multiple daemon instances from corrupting the SQLite archive.
- **Runtime records** written by `WriteDaemonRuntime` enable automatic discovery via `FindDaemonRuntime`, allowing other CLI commands to locate the HTTP API.
- **Background operation** supports detached execution with automatic idle timeout reaping configured through `newDaemonIdleTracker`.
- **Graceful shutdown** in `Server.Shutdown` closes the HTTP server, sync engines, and removes the runtime record without dropping active connections.
- **Read-only safety** returns HTTP 501 for write operations when the daemon starts with `--no-sync`, protecting data from unauthorized modifications.

## Frequently Asked Questions

### How does AgentsView prevent multiple daemon instances from running simultaneously?

The daemon uses an advisory lock file mechanism implemented in [`cmd/agentsview/daemon_runtime.go`](https://github.com/kenn-io/agentsview/blob/main/cmd/agentsview/daemon_runtime.go). When starting, `MarkDaemonStarting` creates a `runtime.lock` file in the data directory. If another process attempts to start while this lock exists, the operation fails. Additionally, the `rejectLiveWritableDaemonBeforeDirectWrite` check prevents other processes from opening the SQLite database for writes while the daemon holds the lock.

### What triggers the automatic shutdown of a background daemon?

When running as a background child process, the daemon creates an `IdleTracker` (defined in [`internal/server/idle.go`](https://github.com/kenn-io/agentsview/blob/main/internal/server/idle.go)) that monitors HTTP request activity. If no requests arrive for the duration specified by `daemonIdleTimeout`, the tracker calls `stop()`, initiating a graceful shutdown via `Server.Shutdown`. This prevents orphaned processes from consuming system resources indefinitely.

### How do other CLI commands communicate with the running daemon?

Commands like `agentsview stats` and `agentsview sync` call `FindDaemonRuntime` from [`cmd/agentsview/daemon_runtime.go`](https://github.com/kenn-io/agentsview/blob/main/cmd/agentsview/daemon_runtime.go) to scan the data directory for the runtime record. This JSON file contains the host, port, and version information needed to construct the API endpoint URL. The command then issues HTTP requests to `/api/v1/...` endpoints, with the client wrapper in [`internal/service/http.go`](https://github.com/kenn-io/agentsview/blob/main/internal/service/http.go) handling the transport layer.

### What is the difference between regular and read-only daemon mode?

In read-only mode (activated by `--no-sync` or PG push server configuration), the daemon sets the `read_only` flag in its runtime record. The HTTP handler in [`internal/service/http.go`](https://github.com/kenn-io/agentsview/blob/main/internal/service/http.go) checks this flag and returns HTTP 501 for any mutating requests. This allows the daemon to safely serve data to low-privilege clients while preventing modifications to the SQLite archive, whereas regular mode permits full CRUD operations through the API.