---
title: "Manage a server"
description: "Starting and stopping it, running it at login, where your data goes, the config file that decides which server a command talks to, updating it, and reaching it from another machine."
---

> Documentation Index
> Fetch the complete documentation index at: https://commentware.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage a server

Start a server, stop it, run it at login, keep it updated, and let a browser on
another machine reach it —
the pages for users, backups and settings all start from a running one. The
server is yours: it runs on your machine, holds one SQLite file and a
directory of screenshots, and answers the browser extension and any AI agents
you point at it. It makes no call that leaves your machine, and nobody else can
reach it unless you arrange for them to.

## Prerequisites

- The server installed. Setting one up from nothing, on this computer or on
  another machine, starts at [Install a server](/docs/install-server/).

## Install with a package manager

Take this way when you want the package manager to own the install and the
updates. It needs Node 24 or later and any of
the four package managers; npm comes with Node, so if you have Node you already
have one.

1. Point the `@commentware-private` scope at the package registry, `https://registry.commentware.com`,
   so that your package manager fetches the server from it rather than from
   npm. Run the command for your package manager, or for yarn
   and bun write the file it shows:

   With pnpm:

```sh
pnpm config set @commentware-private:registry=https://registry.commentware.com/
```

npm: `npm config set @commentware-private:registry=https://registry.commentware.com/`

pnpm and npm — It writes ~/.npmrc, the file the install below reads. No flag: that is where this command goes by default.

With yarn, in `~/.yarnrc.yml`:

```yaml
npmScopes:
  commentware-private:
    npmRegistryServer: "https://registry.commentware.com/"
```

yarn — Yarn 2 and later ignores .npmrc. `yarn config set … --home` writes this file, but only from yarn 2 and later: yarn 1 takes the same command, says it worked, and writes somewhere yarn 2 never reads.

With bun, in `~/.npmrc`:

```ini
@commentware-private:registry=https://registry.commentware.com/
```

bun — bun reads ~/.npmrc and has no command that writes it. npm's command above produces exactly this line.

   The command writes `~/.npmrc`, which is where this has to go. Yarn and bun
   get the file instead of a command, and it is the one in your home directory
   — not one in a project. Learn more about [why it is the home directory, not a
   project](/docs/concepts/install/#the-home-directory-not-a-project).

2. Install and start the server with your package manager:

   With pnpm:

```sh
pnpm add -g @commentware-private/cli-backend
commentware start
```

npm: `npm install -g @commentware-private/cli-backend && commentware start` · yarn: `npm install -g @commentware-private/cli-backend && commentware start` · bun: `bun add -g @commentware-private/cli-backend && commentware start`

pnpm — pnpm needs `pnpm setup` run once before it will install a global command. It refuses with that instruction if you have not. yarn — Yarn 2 and later has no global install. npm comes with Node, so it is already on this machine. bun — bun installs it and then tells you to put its global bin directory on PATH. Until you do, `commentware` is not found.

   It binds `7042` on `127.0.0.1` and then runs in the background until
   you stop it.

3. Press Enter at each of its three questions to take the default.
4. The start prints no connection string, so mint one for `admin` and copy it.
   Keys are stored hashed, so this is the only moment it is legible:

```sh
commentware users generate 1
```

The installer refuses a first install while this `commentware` is on
`PATH`, so use one way per machine. Learn more about [how the install
works](/docs/concepts/install/).

## Start the server

1. Run the start command:

```sh
commentware start
```

2. On a first start, answer its three questions, or press Enter for each
   default.

At a terminal, the server runs in the background: the command prints what the
server printed while it started, then its process id and its log, and gives
you the terminal back. If it cannot start — another server holds its database,
or the port is taken — the command says why and exits with an error.
`commentware logs` prints what a background server has written since, and `-f`
keeps printing.

If you want to watch it in the terminal instead, pass `--foreground`. A start
that is not at a terminal stays in the foreground on its own, so a service
manager, a container or a script that runs `commentware start` keeps the
process it started. A tool that gives its tasks a terminal of their own, such
as a task runner with a full-screen view, needs `--foreground` passed
explicitly.

## Stop the server

1. Run:

```sh
commentware stop
```

`stop` stops the server this directory addresses — the one [the directory's config
file](#the-config-file-a-command-uses) names, or the machine's own
server when there is none. It asks the server to stop and waits while it
finishes the requests it is already answering, then prints the address and
process id of the server it stopped. A server kept in the foreground also stops
on `Ctrl+C`, the same way.

On Windows the server stops at once, so a request it was answering is cut. The
database is not harmed.

## Run it at login

A background server keeps running until you stop it or the machine restarts.
To have it start at login and come back after a crash, hand it to your
system's service manager — systemd on Linux, launchd on macOS. Windows has no
`service install`; there, start the server with `commentware start` after each
login.

1. In the directory whose server should start at login, stop that server if it
   is running, because the service starts its own and a second server on one
   database is refused:

```sh
commentware stop
```

2. Install the service:

```sh
commentware service install
```

The service starts the server for the database this directory addresses, with
the config file in force named outright, and writes its output to the same log
`commentware logs` reads. After your next login, `commentware servers` lists
it. `commentware stop` still stops it, and it stays stopped until the next login
or until you run `commentware start`.

At a terminal without `--foreground`, `commentware start` starts the service
instead of a second server beside it. That start refuses `--port`, `--host`,
`--name` and `--public-origin`, because the service runs with the arguments it
was installed with. To change one, run `commentware service uninstall`, then
`commentware start` with the flag. `commentware service uninstall` stops the
service and removes it.

On Linux, systemd stops your services when you log out. If you want it running
while you are logged out, run `loginctl enable-linger` once.

## What the first start does

**It asks three questions**, once, on the run that brings a server into
existence: where your comments should live — this machine, or this directory —
which port to serve on, and what the server should call itself. Each has a
default, so three returns is a complete answer, and it writes what you said into
a config file.

It asks only when all three of these are true: nothing is configured anywhere
yet, there is no database at the path it resolved, and you are at a terminal.
Anything scripted therefore never sees a prompt. `--no-setup` turns the
questions off explicitly, for a terminal that is interactive but unattended.

**`-p` and `-n` are answers, not overrides.** `commentware start -p 8080` on a
first run does not ask about the port, and 8080 is what goes into the config
file — so the run you started and every run after it agree, and a connection
string you paste into the extension keeps working. `-H` is deliberately not one
of them: binding wider than loopback is a decision you make per run, not a
setting a first run writes down for you.

Then, in this order: opens the database, migrates it, takes its locks, creates
two users if there are none, runs the retention purge, and binds.

Then it prints where everything is — the address, the discovery URL, the
database, the screenshots, the config file in force, the MCP endpoint, and the
change channel. Those lines are the fastest answer to "which server am I
actually talking to".

The two users it creates are `admin` and `agent`, and **neither holds a key
yet**. A start prints no key at all: its output goes to a log, a service
manager's journal or an agent's shell, and a key written there is a disclosure
nobody chose. It names each user's id instead, and the command that mints
`admin`'s connection string:

```text
Created the default users: admin (id 1), agent (id 2)
  Get admin's connection string with "commentware users generate 1".
```

See [Manage users and access keys](/docs/server/users/).

## Where the data goes

Two paths, and the second follows the first:

- **The database**, a single SQLite file.
- **The screenshots and attachments**, a directory beside it.

**With nothing configured, they are yours rather than this directory's** — one
server that every `commentware` command on the machine reaches, wherever you are
standing when you run it.

| Your system | Database and screenshots                                        |
| ----------- | --------------------------------------------------------------- |
| Linux       | `$XDG_DATA_HOME/commentware/`, or `~/.local/share/commentware/` |
| macOS       | `~/Library/Application Support/Commentware/`                    |
| Windows     | `%LOCALAPPDATA%\Commentware\`                                   |

That is the default and not a rule. A directory can have a server of its own —
a client project whose comments should not mix with everything else, a checkout
a whole team shares — and that is what the config file is for.

## Give a directory its own server

If you want a directory's comments kept apart from everything else, such as a
client project or a checkout a team shares, give the directory a config file.
Every `commentware` command run in it, or below it, then addresses its server.
Learn more about [how a command finds its server](/docs/concepts/config-file/).

1. In the directory, write the config file. Give it a port no other server on
   this machine uses, since the machine's own server takes `7042`; the
   example uses 7100. `-n` sets the name, and `--force` replaces a file that is
   already there:

```sh
   commentware init -p 7100 -n "Design review"
```

   It writes `commentware.config.json`, with the database under
   `.tmp/comments/`, and starts nothing:

```text
   Wrote /home/alex/review/commentware.config.json
 database  /home/alex/review/.tmp/comments/data.db
 blobs     /home/alex/review/.tmp/comments/blobs
 port      7100
 name      Design review

   Start the server with "commentware start".
```

2. Start the server:

```sh
commentware start
```

3. Optional: commit `commentware.config.json`, so your teammates' and your
   agents' commands in the checkout reach the same server. Add `.tmp/` to the
   checkout's `.gitignore` first, or the database is committed with it.

To change what every directory without a file of its own reaches, run
`commentware init -g` instead. It writes your own config file.

Run `commentware where` in the directory, or any directory below it, to see
which file is in force and which database it names. Every key the file may hold
is in **[commentware.config.json](/docs/server/config/)**.

## Choose the port and the interface

The default is `7042` on `127.0.0.1`, which is reachable from this
machine and nowhere else.

1. Pass the port, and the interface if not loopback, to `start`:

```sh
commentware start --port 7100 --host 0.0.0.0
```

Binding anything but loopback prints a warning, and the warning is the point:
**everything this server accepts crosses the network in plaintext.** If it is
reachable from anywhere but your own machine, put TLS in front of it.

## Reach it from another browser

Two ways, and the first is usually right. Either way the server cannot know the
address you reach it at unless you tell it, and which of the two you did decides
how.

1. Choose a way in:
- **Forward a port**, over SSH or whatever tunnel you already use. The server
     stays on loopback and nothing is exposed.
- **Bind an interface and put a reverse proxy with TLS in front of it.** The
     same binary is meant to run this way.
2. Tell it the address: behind a proxy, set `publicOrigin` once; over a
   forwarded port, pass `--port` to the command that prints the address. Both
   are below.

Behind a reverse proxy, tell the server once: set `publicOrigin` in
`commentware.config.json`, or pass `--public-origin` to `start`. That is the
only route that reaches the addresses the server writes down rather than the
ones a command happens to print — every comment permalink an agent is handed,
and the OAuth issuer its tokens are bound to. It is also what the server
answers to: a request whose `Host` names an address it was never told is
refused, so a proxied server reached at a public name, or any server reached at
an mDNS `.local`, container or tailnet name, answers `403` to the extension's
first call until you set it. See the
[`publicOrigin`](/docs/server/config/#when-something-else-answers-for-your-server)
key.

Over a forwarded port or a tunnel there is nothing to tell it, because the
address is yours rather than the deployment's and the next person forwards a
different one. Tell the command instead: `--port`, on `users new` and on `mcp`.
Neither binds anything, and neither is read by the running server — on a
deployment that set `publicOrigin` the printed address is that one and the
command says the flag was not used. See [An address your browser can reach](/docs/connect/#an-address-your-browser-can-reach).

## Update the server

If `~/Commentware/server` exists, you installed with the installer. Otherwise
you installed with a package manager.

1. If you installed with the installer, run the install command again:

   **macOS and Linux**

```sh
curl -fsSL https://commentware.com/install/server.sh | sh
```

It lands in ~/Commentware/bin, which it adds to your PATH. Open a new terminal afterwards. Run this command again to update the server.

**Windows**

```powershell
iwr -useb https://commentware.com/install/server.ps1 | iex
```

PowerShell 5.1, which is what Windows ships. It lands in %USERPROFILE%\Commentware\bin, which it adds to your Path. Open a new terminal afterwards.

   If this computer has the extension, the run updates it too: follow the page
   the installer opens in Chrome. Learn more about [what a run installs and
   updates](/docs/concepts/install/#what-a-run-installs-and-updates).

2. If you installed with a package manager, run its install again:

   With pnpm:

```sh
pnpm add -g @commentware-private/cli-backend
```

npm: `npm install -g @commentware-private/cli-backend` · yarn: `npm install -g @commentware-private/cli-backend` · bun: `bun add -g @commentware-private/cli-backend`

pnpm — pnpm needs `pnpm setup` run once before it will install a global command. It refuses with that instruction if you have not. yarn — Yarn 2 and later has no global install. npm comes with Node, so it is already on this machine. bun — bun installs it and then tells you to put its global bin directory on PATH. Until you do, `commentware` is not found.

   The package manager replaces the server's files in place, so the pages of the
   running server can fail to load until the restart. Restart right away.

3. Restart the server, so that it runs the newly installed version:

```sh
commentware restart
```

   A server that an older version started cannot be restarted: stop it with
   `commentware stop`, then start it again the way you started it,
   with the same `--host` and `--public-origin`.

`restart` keeps the settings the server started with, and under a service it
goes through the service. Learn more about [running it at login](#run-it-at-login).

An update that changes the database's shape backs the database up before it
runs, and refuses to run if it cannot. Learn more about [the backup a migration
writes](/docs/concepts/retention/#migrations-back-themselves-up-first).

## Run one server per database

`start` takes a lock on the database and another on the screenshot directory, and
refuses to be the second server on either. The refusal names the process holding
it and the address that process bound.

A server that crashed leaves its lock files behind, and the next start takes
them: the operating system releases a server's lock when the server's process
ends, however it ends. A lock written by an older version is judged by its
process id instead, and the refusal names that lock file, to delete if the
process is long gone.

There is no `--force`. Two servers writing one SQLite file is data loss with no
error attached, and the screenshots are locked separately because two servers
with different databases can still be pointed at one directory of blobs — where
each one's purge would delete the other's screenshots.

`commentware stop` releases both locks on the way out, after the server has
finished what it was doing.

[`commentware`](/docs/server/commands/) lists every command and the options it
takes, for the newest release. `commentware --help` lists the commands your
version has, and every command takes `--help` of its own.

## Check that it worked

The start prints where everything is: the address, the discovery URL, the
database, the screenshots, the config file in force when there is one, the MCP
endpoint and the change channel. `commentware servers` lists every server
running on this machine, wherever it was started, and this one among them.
From another machine, the extension connects with a connection string that
names the address you reach the server at.

## Next steps

- [Manage users and access keys](/docs/server/users/): mint a connection string for
  another person or an agent.
- [Back up and restore](/docs/server/data/): back the database up, and put a
  backup back.