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

# Profiles

> Profile roles (server/client), the config schema, the default-profile pointer, and how the CLI picks a profile.

A **profile** is a **named runtime context** for Task Manager: its own **data directory**, **auth storage**, **HTTP port**, and (for client profiles) its own remote `api_url` and `api_key`. Profiles give you machine-local isolation without mixing data, sessions, or credentials between setups.

## Where profiles live

```text theme={null}
~/.taskmanager/profiles/<profile-name>/
  config.json     # the profile's own settings
  data/           # SQLite database and related files (server profiles only)
  auth/           # auth.json, recovery key, cli-api-keys.json (server profiles only)
```

A small **default-profile pointer** lives one level up, alongside the profiles directory:

```text theme={null}
~/.taskmanager/config.json     # { "default_profile": "main" }
```

## Profile roles

Every profile has a `role`, set by the wizard mode you choose:

| Role     | Created by                       | What this machine does                                                                                                              |
| -------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `server` | `hirotaskmanager --setup-server` | Runs the API server **and** acts as its own local client. The CLI on the same machine uses `http://127.0.0.1:<port>` automatically. |
| `client` | `hirotaskmanager --setup-client` | Talks to a **remote** server only. Contains `api_url` and `api_key`; runs nothing locally.                                          |

You never set `role` by hand — the setup mode picks it. To convert a profile from one role to the other, delete it and re-run the appropriate wizard.

## Configuration schema

### Field matrix

| Field                     | Server profile                                                       | Client profile          |
| ------------------------- | -------------------------------------------------------------------- | ----------------------- |
| **`role`**                | required (`"server"`)                                                | required (`"client"`)   |
| **`port`**                | required                                                             | — (must not be set)     |
| **`data_dir`**            | required                                                             | —                       |
| **`open_browser`**        | optional (default `true`)                                            | —                       |
| **`bind_address`**        | optional (default `"127.0.0.1"`)                                     | —                       |
| **`require_cli_api_key`** | optional (default derived from `bind_address`)                       | —                       |
| **`api_url`**             | — (auto-derived as `http://127.0.0.1:<port>` for the local client)   | required (absolute URL) |
| **`api_key`**             | optional (used by the local client when `require_cli_api_key: true`) | required                |

> **Auth directory is fixed.** The web passphrase hash and CLI API key hashes
> always live at `<profileRoot>/auth/` (i.e. `~/.taskmanager/profiles/<name>/auth/`).
> There is no `auth_dir` field — the wizard does not prompt for it, and adding
> one to `config.json` by hand has no effect.

### Validation rules (enforced when the config is loaded)

1. `role` must be `"server"` or `"client"`.
2. **Server profile:** `port` and `data_dir` are required; `api_url` **must not** be present (it is auto-derived from `port`).
3. **Client profile:** `api_url` (absolute URL) and `api_key` are required; `port`, `data_dir`, `open_browser`, `bind_address`, `require_cli_api_key` **must not** be present.
4. **Public exposure requires a key.** If `bind_address` is non-loopback (anything other than `127.0.0.1`, `localhost`, `::1`), `require_cli_api_key: false` is rejected.
5. **Bootstrap warning** (not an error): if `require_cli_api_key === true` on a server profile **and** no `api_key` is set, the loader warns that the local CLI will fail with `auth_cli_key_required` until you set one (or pass `--api-key` per command). External callers may still authenticate via `cli-api-keys.json`.

Validation failures throw `CliError(invalid_config)` (exit code **9**). The error names the offending field and the profile path.

### Example — same-machine server profile (no key)

```json theme={null}
{
  "role": "server",
  "port": 3001,
  "data_dir": "~/.taskmanager/profiles/main/data",
  "open_browser": true,
  "bind_address": "127.0.0.1"
}
```

### Example — hardened single-host (loopback + forced key)

```json theme={null}
{
  "role": "server",
  "port": 3001,
  "data_dir": "~/.taskmanager/profiles/main/data",
  "bind_address": "127.0.0.1",
  "require_cli_api_key": true,
  "api_key": "tmk-a3f8c1...local CLI's copy of a key minted via server api-key generate"
}
```

### Example — public bind on the LAN (advanced, hand-edited)

```json theme={null}
{
  "role": "server",
  "port": 3001,
  "data_dir": "~/.taskmanager/profiles/main/data",
  "bind_address": "0.0.0.0",
  "require_cli_api_key": true,
  "api_key": "tmk-a3f8c1...local CLI's copy of a key minted via server api-key generate"
}
```

See [Advanced: exposing the API on a network interface](#advanced-exposing-the-api-on-a-network-interface) for when to use this and what to set together.

### Example — client profile (talks to a remote server)

```json theme={null}
{
  "role": "client",
  "api_url": "https://tasks.example.com",
  "api_key": "tmk-a3f8c1...paste full key here"
}
```

## Default-profile pointer

`~/.taskmanager/config.json`:

```json theme={null}
{ "default_profile": "main" }
```

Both `--setup-server` and `--setup-client` offer to write this pointer at the end of the wizard. Change it later with:

```bash theme={null}
hirotaskmanager profile use <name>
```

`profile use` errors if `<name>` doesn't exist on disk.

## How the CLI picks a profile

Resolution order, per command:

1. **`--profile <name>`** on the command line (highest priority).
2. **`default_profile`** in `~/.taskmanager/config.json`.
3. If neither is set **and exactly one profile exists** on disk, use it implicitly.
4. Otherwise the CLI errors with the list of available profiles and a hint to run `hirotaskmanager profile use <name>` (or pass `--profile`).

## How a profile is created

Use the wizards in **`hirotaskmanager`**. Each one writes the profile's `config.json`, optionally mints a CLI API key, and optionally sets the default-profile pointer.

| Mode   | Use it when                                                                  | Wizard flag                      |
| ------ | ---------------------------------------------------------------------------- | -------------------------------- |
| Server | This machine runs the server (works for both same-machine and VPS installs). | `hirotaskmanager --setup-server` |
| Client | This machine only talks to a remote server.                                  | `hirotaskmanager --setup-client` |

Plain `hirotaskmanager` with no configured profile asks **"server or client?"** once, then routes to the right flow.

To re-run setup for an existing profile (change port, paths, etc.), use:

```bash theme={null}
hirotaskmanager --setup --profile <name>
```

Re-running pre-fills your existing answers and refuses role changes — delete the profile and re-setup to convert.

## Updating a profile

* **Interactive:** `hirotaskmanager --setup --profile <name>`.
* **Manual:** edit `~/.taskmanager/profiles/<name>/config.json` carefully (keep valid JSON). Back up the file first. Validation errors will surface on the next CLI invocation.

## Advanced: exposing the API on a network interface

`bind_address` is treated as an **advanced, config-file-only** setting. The setup wizard never asks for it: new profiles always default to `127.0.0.1` (loopback), and re-running the wizard preserves whatever value is already in `config.json`.

When loopback is the right answer:

* **Same machine.** The CLI on this machine talks to `http://127.0.0.1:<port>` automatically.
* **Remote access via reverse proxy.** Put Caddy, nginx, or similar in front of the loopback API and terminate TLS there. The API stays unreachable from the network directly.

When you want the API socket itself reachable from the network (LAN, Tailscale, etc.), edit `config.json` by hand and set **both** fields together:

```json theme={null}
{
  "bind_address": "0.0.0.0",
  "require_cli_api_key": true
}
```

The validator enforces this pairing: any non-loopback `bind_address` (anything other than `127.0.0.1`, `localhost`, `::1`) **must** also have `require_cli_api_key: true`, set explicitly. Setting `bind_address` to `0.0.0.0` without `require_cli_api_key`, or with `require_cli_api_key: false`, is rejected with `invalid_config` (exit code **9**) on the next CLI invocation.

Mint a CLI API key for any caller that will reach the public socket:

```bash theme={null}
hirotaskmanager server api-key generate --profile <name>
```

Plain HTTP on a non-loopback host sends traffic in cleartext; prefer a reverse proxy with TLS for any access beyond a trusted local network.

## Related

* [Quickstart](/task-manager/get-started/quickstart) — same-machine setup.
* [Advanced setup](/task-manager/get-started/advanced-setup) — VPS / split / hardened / dev workflows.
* [Concepts](/task-manager/get-started/concepts) — concepts and how the CLI fits the app.
* [Server operations](/task-manager/cli/server) — `server start/stop/status` and `server api-key …`.
* [Errors & exit codes](/task-manager/cli/error-codes) — `invalid_config`, `auth_cli_key_required`, and friends.

## Hiro developers only

For Hiro developers working on Task Manager itself, **`--dev`** and **`--profile`** are independent:

```bash theme={null}
hirotm server start --dev --profile dev
hirotm server start --dev --profile staging
hirotm server start --profile dev
```

* **`--dev`** changes runtime behavior, including the default port (**3002**) and dev CORS.
* **`--profile`** selects the profile directory for config, auth, and data.
* The **`dev`** profile is not magic. It is just a profile name.
* All profiles, including **`dev`**, store data under **`~/.taskmanager/profiles/<name>/data/`** unless you override the path.

See [Advanced setup → For developers](/task-manager/get-started/advanced-setup#for-developers) for a worked dual-profile example.
