# Lifecycle

Follow the daemon from launch to shutdown, and see which goroutines run concurrently in between.

## Startup sequence

`runServer` launches the current executable with `--daemon` in a new session, redirects stdout and stderr to `daemon.log`, and polls for the `endpoint` file every 100 ms for up to 10 seconds. `runServerDaemon` then initializes in this order:

1. Write `runtime.uid` with a new UID, the PID, and the start time. A failure, including another live daemon, is logged as a warning and startup continues.
2. Initialize the keychain context for service `kuradb`.
3. Construct the OpenAI embedder; a missing credential exits the process.
4. Open `global.db`, preload query embeddings of the expected dimension, and register the hook that persists new query embeddings.
5. Initialize the gse tokenizer and the process-wide vector cache.
6. Load the database registry from `db.json`; a read error exits the process.
7. For each registered database: ensure its directories and `~/Kura_{db}` symlink, open `data.db`, create its vector bucket, and rebuild the bucket from embedded, non-dismissed rows.
8. Start one embedding scheduler and one watcher per loaded database.
9. Start the HTTP server on `127.0.0.1`, write `endpoint`, and wait for `SIGINT` or `SIGTERM`.

A per-database failure before `data.db` opens skips that database. A vector-bucket failure leaves the database queryable by keyword but starts no watcher or embedder for it.

## Concurrency model

| Work | Goroutines | Coordination |
|---|---|---|
| Watcher | One per loaded database, 10-second ticker | Snapshot writes to `record.json` run in a goroutine guarded by a mutex |
| Embedding scheduler | One per loaded database, 5-second ticker | Batches of up to 64 pending chunks |
| Search | Keyword and semantic branches run concurrently when no `target` is set | `sync.WaitGroup` in `search.Search`, shared by all transports |
| Query-cache persistence | One short goroutine per new query embedding | 5-second timeout per write to `global.db` |
| HTTP server | One listener goroutine plus a shutdown watcher | Context cancellation |

SQLite provides a read pool of eight connections and a separate write connection through `go-sqlkit`. The vector cache uses a cache-level and a per-bucket `RWMutex`; the query cache has its own `RWMutex`.

## Shutdown

A termination signal cancels the shared context. Each part reacts independently:

| Part | Action |
|---|---|
| HTTP server | Removes `endpoint`, then calls `Shutdown` with a 5-second timeout |
| Watchers and embedders | Leave their select loops |
| Main goroutine | Clears `runtime.uid`, closes every per-database connector, then closes `global.db` |

The main goroutine does not wait for the HTTP shutdown or the loops to finish, so the process can exit before in-flight requests complete. `kura stop` sends `SIGTERM` and escalates to `SIGKILL` if the process is still alive after 5 seconds.
