Metadata-Version: 2.4
Name: octopize.deploy_tool
Version: 2.10.0
Summary: Deployment configuration tool for Octopize Avatar platform
Project-URL: Homepage, https://octopize.io
Project-URL: Documentation, https://docs.octopize.io/docs/deploying/self-hosted
Project-URL: Repository, https://github.com/octopize/avatar
Project-URL: Issues, https://github.com/octopize/avatar/issues
Author-email: Octopize <contact@octopize.io>
License: MIT
Keywords: avatar,configuration,deployment,octopize
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Installation/Setup
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.13
Requires-Dist: certifi>=2024.0.0
Requires-Dist: cryptography>=49.0.0
Requires-Dist: httpx2>=2.4.0
Requires-Dist: httpx>=0.28.0
Requires-Dist: jinja2>=3.1.0
Requires-Dist: pandas>=3.0.0
Requires-Dist: python-dotenv>=1.2.2
Requires-Dist: python-on-whales>=0.76.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich-argparse>=1.8.0
Requires-Dist: rich>=13.0.0
Description-Content-Type: text/markdown

# Octopize Avatar Deployment Tool

This package provides the `octopize-deploy-tool` CLI for operating a self-hosted Octopize Avatar
deployment. It bundles deployment templates for each release so it works without fetching files
from GitHub.

Available commands:

- **`install`** — first-time setup: collects configuration interactively and generates all
  deployment files under a versioned app root directory.
- **`update`** — reconfigure an existing deployment: re-runs the configuration wizard seeded from
  the current state and regenerates the deployment files.
- **`migrate`** — migrates an existing flat deployment (from the legacy layout) to the new
  versioned directory structure.
- **`start`** — creates required Docker volumes and starts the deployment stack with
  `docker compose up`.
- **`stop`** — suspends all running services with `docker compose stop`, preserving
  containers and volumes for a fast restart.
- **`generate-env`** — generates per-component `.env` files for local development.
- **`tasks create-users`** — creates user accounts in Authentik and sends each user a
  recovery email so they can set their password without requiring server-side browser access.
- **`tasks authentik-migrate`** — migrates existing user data from CSV exports into
  [authentik](https://goauthentik.io/), the identity provider bundled with Avatar.
- **`tasks`** — one-off operational sub-commands for post-install administration
  (e.g. generating a self-signed TLS certificate).

## Installation

### Option 1 — uvx (no install required, recommended for one-shot use)

[uv](https://docs.astral.sh/uv/) can run the tool directly from PyPI without a permanent install.
If you don't have `uv` yet:

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

Then run any command directly:

```bash
uvx octopize-deploy-tool install --app-root ./app/avatar
```

### Option 2 — virtual environment

Modern Linux distributions and macOS prevent installing packages into the system Python
(PEP 668 — "externally managed environment"). A virtual environment avoids that:

```bash
python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install octopize-deploy-tool
octopize-deploy-tool install --app-root ./app/avatar
```

### Option 3 — global pip

If your system Python allows global installs:

```bash
pip install octopize-deploy-tool
```

Verify the CLI is available:

```bash
octopize-deploy-tool --help
```

### Option 4 — Docker (if PyPI is not accessible)

If PyPI is blocked in your environment (air-gapped networks, corporate proxies, or registries
that allow [quay.io](https://quay.io/octopize) but not PyPI), a pre-built Docker image is
available:

```bash
docker pull quay.io/octopize/deploy-tool:latest
```

See the [Docker](#docker) section below for full usage examples for each command.

## Quick Start

Set up a new deployment in two steps:

1. Run the configuration wizard to generate all deployment files:

   ```bash
   octopize-deploy-tool install --app-root ./app/avatar
   ```

2. Start the deployment:

   ```bash
   octopize-deploy-tool start --app-root ./app/avatar
   ```

## Commands: When to Use What

| Command              | Use when                                                                         |
| -------------------- | -------------------------------------------------------------------------------- |
| `install`            | Setting up a new Avatar deployment for the first time                            |
| `update`             | Upgrading an existing deployment to a newer version of the Avatar application    |
| `migrate`            | Moving a deployment set up with the legacy layout to the new versioned structure |
| `start`              | Bringing a configured deployment live, or restarting after a host reboot         |
| `stop`               | Suspending the running stack temporarily (preserves data; fast to restart)       |
| `tasks create-users` | Creating user accounts in Authentik after the deployment is live                 |

### `install`

Use `install` when you are setting up Avatar for the first time. It runs the full configuration
wizard, collects all required values interactively (or from a seed config file), generates all
deployment files under a versioned directory tree, and leaves the deployment ready to start.

```bash
octopize-deploy-tool install --app-root ./app/avatar
```

After a successful install, a `user_config/` directory is created under the app root:

```
./app/avatar/
└── user_config/
    ├── config.example.yaml   ← commented reference — copy and edit to create config.yaml
    └── hooks/                ← reserved for future lifecycle hook scripts
```

To speed up future installs, copy the example file, fill in your values, and commit it:

```bash
cp ./app/avatar/user_config/config.example.yaml ./app/avatar/user_config/config.yaml
# Edit config.yaml with your PUBLIC_URL, ENV_NAME, TLS settings, etc.
```

On subsequent installs, the tool detects a unique `*.yaml` or `*.yml` file in `user_config/` automatically
(ignoring `config.example.yaml`):

- **Interactive mode** — you are prompted: _"Found user_config/config.yaml — use it to
  pre-populate configuration? [Y/n]"_
- **Non-interactive mode** — a notice is printed that the file was found; pass `--from-config`
  explicitly to use it (conservative default).

> **TLS paths and the deploy-tool container**: if you run the deploy tool inside Docker and your
> TLS certificate lives on the _host_ (not inside the container), add `--skip-tls-path-validation`
> to bypass the existence check:
>
> ```bash
> octopize-deploy-tool install \
>   --app-root /app-root \
>   --from-config /app-root/user_config/config.yaml \
>   --non-interactive \
>   --skip-tls-path-validation
> ```

You can always override auto-detection by passing `--from-config` explicitly:

```bash
octopize-deploy-tool install \
  --app-root ./app/avatar \
  --from-config ./app/avatar/user_config/config.yaml \
  --non-interactive
```

Common `install` options:

```text
--app-root PATH        Root directory for the deployment (default: current directory)
--from-config PATH     YAML seed config to pre-populate answers
--non-interactive      Run without prompts; fails fast if any required value is missing
--verbose              Show detailed progress output
```

### `update`

Use `update` when you are upgrading to a newer version of the Avatar application. It carries your
existing configuration forward unchanged, re-renders the deployment files using the new templates
bundled in the updated deploy tool, diffs the result against the currently running files, and
applies the changes.

```bash
octopize-deploy-tool update --app-root ./app/avatar
```

If `user_config/config.yaml` (or `config.yml`) exists under the app root and `--from-config` was
not passed explicitly, the tool detects it automatically:

- **Interactive mode** — you are prompted: _"Found user_config/config.yaml — use it to
  pre-populate configuration? [Y/n]"_
- **Non-interactive mode** — the file is used automatically and a notice is printed.

After `update`, run `start` to apply the new files to the running stack:

```bash
octopize-deploy-tool start --app-root ./app/avatar
```

> **Need to change a configuration value** (URL, TLS certificate, email settings, …)?  
> `update` is not the right tool for that — it carries existing values forward without
> re-asking questions. To go through the configuration wizard again, use:
>
> ```bash
> octopize-deploy-tool configure --fresh --app-root ./app/avatar
> ```

Common `update` options:

```text
--app-root PATH        Root directory for the deployment (default: current directory)
--non-interactive      Run without prompts
--verbose              Show detailed progress output
```

### `migrate`

Use `migrate` when you have a deployment that was generated by the legacy flat layout (a directory
containing `.env` and `.secrets/`) and want to bring it under the new versioned directory
structure. `migrate` reads the existing `.env` and `.secrets/` files to pre-seed all configuration
values so you do not have to re-enter them.

```bash
octopize-deploy-tool migrate \
  --app-root ./app/avatar-new \
  --from-legacy ./app/avatar-old
```

After `migrate`, run `start` to bring the migrated deployment live:

```bash
octopize-deploy-tool start --app-root ./app/avatar-new
```

Common `migrate` options:

```text
--app-root PATH        Root directory for the new versioned deployment (default: current directory)
--from-legacy DIR      Path to the existing flat deployment directory (required)
--non-interactive      Run without prompts
--verbose              Show detailed progress output
```

### `start`

Use `start` to create the required Docker volumes and bring the deployment stack live. Run it after
`install`, `migrate`, or `update`. `start` is idempotent — if a volume already exists it is left
unchanged, making it safe to re-run after a host reboot.

```bash
octopize-deploy-tool start --app-root ./app/avatar
```

Common `start` options:

```text
--app-root PATH        Root directory for the deployment (default: current directory)
--verbose              Show detailed progress output
```

### `stop`

Use `stop` to suspend the running stack without removing containers or volumes. This is the
complement of `start` — services can be brought back up quickly with `start` without losing
any data.

```bash
octopize-deploy-tool stop --app-root ./app/avatar
```

Common `stop` options:

```text
--app-root PATH        Root directory for the deployment (default: current directory)
--verbose              Show detailed progress output
```

## `tasks`

The `tasks` namespace groups one-off operational commands that are useful after the stack is
running or during initial setup.

### `tasks create-users`

Use `tasks create-users` after a fresh deployment is live to create user accounts in Authentik.
The command reads the Authentik URL and bootstrap token from `generated_files/.env`, creates each
user, assigns them to the correct group, and sends a recovery email so they can set their password
without needing server-side browser access.

Run this once the stack is healthy (after `start` and `verify`). It is safe to re-run — existing
users are detected and skipped.

**Create an admin and a standard user:**

```bash
octopize-deploy-tool tasks create-users \
  --app-root ./app/avatar \
  --admins alice@company.com \
  --users bob@company.com,carol@company.com
```

**Skip sending the recovery email** (create accounts silently; users set their password later):

```bash
octopize-deploy-tool tasks create-users \
  --app-root ./app/avatar \
  --admins alice@company.com \
  --no-email
```

**Verbose output** to see the resolved Authentik URL and per-user status:

```bash
octopize-deploy-tool tasks create-users \
  --app-root ./app/avatar \
  --admins alice@company.com \
  --verbosity 2
```

The bootstrap token is the one generated during `install` and stored automatically in
`generated_files/.env` as `AUTHENTIK_BOOTSTRAP_TOKEN`. Pass `--authentik-token` to override it,
or `--authentik-url` to point at a different Authentik instance.

```text
--admins EMAILS        Comma-separated admin email addresses
--users EMAILS         Comma-separated standard user email addresses
--app-root PATH        App root directory (parent of generated_files/; default: current directory)
--no-email             Create accounts without sending a recovery email
--authentik-url URL    Authentik base URL (overrides SSO_PROVIDER_URL from generated_files/.env)
--authentik-token TOK  Bootstrap token (overrides AUTHENTIK_BOOTSTRAP_TOKEN from generated_files/.env)
--verbosity {1,2}      1 = essential output (default), 2 = verbose
```

---

### `tasks create-self-signed-cert`

Generates a self-signed X.509 TLS certificate and private key for a given domain and writes them
to a directory. Use this when you need a quick certificate for internal or testing deployments
and do not want to run raw `openssl` commands.

The certificate is self-signed (issuer == subject) and includes a SubjectAlternativeName (SAN)
extension for the domain, making it compatible with modern TLS stacks. Both generated files are
written with standard permissions (certificate `0644`, private key `0600`).

```bash
octopize-deploy-tool tasks create-self-signed-cert \
  --domain avatar.company.com \
  --output-dir /etc/nginx/certs
```

To set a custom validity period:

```bash
octopize-deploy-tool tasks create-self-signed-cert \
  --domain avatar.company.com \
  --output-dir /etc/nginx/certs \
  --valid-days 730
```

If clients connect by IP address rather than hostname, use `--ip-address` to embed one or more
IP addresses as IPAddress SAN entries alongside the DNS name. The flag can be repeated:

```bash
octopize-deploy-tool tasks create-self-signed-cert \
  --domain avatar.company.com \
  --ip-address 192.168.1.10 \
  --ip-address ::1 \
  --output-dir /etc/nginx/certs
```

The command creates `--output-dir` automatically if it does not exist. If `fullchain.crt` or
`server.key` already exist in the target directory a warning is printed before they are
overwritten.

```text
--domain DOMAIN        DNS hostname to embed as CN and SAN (required)
                       e.g. avatar.company.com
--output-dir DIR       Directory to write fullchain.crt and server.key
                       (default: current directory; created if missing)
--valid-days DAYS      Certificate validity period in days (default: 365)
--ip-address IP        IP address (IPv4 or IPv6) to add as an IPAddress SAN entry
                       May be repeated: --ip-address 192.168.1.10 --ip-address ::1
```

---

## Docker

If you don't have Python installed, you can use the published Docker image instead:

```
quay.io/octopize/deploy-tool:latest
```

> **Note on file ownership:** For commands that write files to a bind-mounted directory, run the
> container with `--user "$(id -u):$(id -g)"` so generated files are owned by your host user.
>
> **Commands that require Docker** (`install`, `start`, `stop`, `verify`) need three extra mounts.
> Run this once in your shell before the `docker run` commands below:
>
> ```bash
> export COMPOSE_PLUGIN=$(docker info --format '{{range .ClientInfo.Plugins}}{{if eq .Name "compose"}}{{.Path}}{{end}}{{end}}')
> ```
>
> Then add to every `docker run` that needs Docker:
>
> ```
>   -v /var/run/docker.sock:/var/run/docker.sock
>   -v "$(which docker):/usr/local/bin/docker:ro"
>   -v "${COMPOSE_PLUGIN}:${COMPOSE_PLUGIN}:ro"
> ```

### `install`

`install` calls Docker directly (to check for existing volumes), so the host Docker socket must
be mounted:

**Interactive:**

```bash
docker run -it --rm \
  --user "$(id -u):$(id -g)" \
  -v "$(pwd)/avatar:/app-root" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$(which docker):/usr/local/bin/docker:ro" \
  -v "${COMPOSE_PLUGIN}:${COMPOSE_PLUGIN}:ro" \
  quay.io/octopize/deploy-tool:latest install --app-root /app-root
```

**Non-interactive, using `user_config/config.yaml` inside the app-root (recommended):**

After a first install, edit `./avatar/user_config/config.yaml`, then re-use it for future
installs without any extra mounts:

```bash
docker run --rm \
  --user "$(id -u):$(id -g)" \
  -v "$(pwd)/avatar:/app-root" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$(which docker):/usr/local/bin/docker:ro" \
  -v "${COMPOSE_PLUGIN}:${COMPOSE_PLUGIN}:ro" \
  quay.io/octopize/deploy-tool:latest install \
    --app-root /app-root \
    --from-config /app-root/user_config/config.yaml \
    --non-interactive
```

If a single `*.yaml` / `*.yml` file is found in `user_config/` (excluding `config.example.yaml`), the tool
prints a notice. Pass `--from-config /app-root/user_config/config.yaml` explicitly to use it.

**Non-interactive, seed config stored outside the app-root:**

Mount the directory containing the config file as a separate read-only volume:

```bash
docker run --rm \
  --user "$(id -u):$(id -g)" \
  -v "$(pwd)/avatar:/app-root" \
  -v "$(pwd)/config:/input:ro" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$(which docker):/usr/local/bin/docker:ro" \
  -v "${COMPOSE_PLUGIN}:${COMPOSE_PLUGIN}:ro" \
  quay.io/octopize/deploy-tool:latest install \
    --app-root /app-root \
    --from-config /input/config.yaml \
    --non-interactive
```

> **Important — enter host paths during configuration:** When the wizard asks for filesystem
> paths — such as the path to your TLS certificate or private key — enter the path where that
> file exists on your **host machine**, not the path inside this container.
>
> For example, if your TLS certificate lives at `/etc/ssl/avatar/server.crt` on the host,
> enter `/etc/ssl/avatar/server.crt` at the prompt. Docker Compose will mount those paths from
> the host when it starts the Avatar services. TLS path validation is skipped automatically
> when the deploy tool detects it is running inside Docker.
>
> **Note on Docker socket permissions:** If you see a permission error on the socket, add
> `--group-add $(stat -c '%g' /var/run/docker.sock)` alongside `--user` to grant the container
> access to the socket's group on the host.
>
> **Note on the Docker binary:** The deploy-tool image does not bundle the Docker CLI. Mount it
> from your host with `-v "$(which docker):/usr/local/bin/docker:ro"`. The official Docker CLI
> binary is statically compiled, so it works across Linux distributions without extra
> dependencies.

### `update`

Mount the existing app-root. If your `--from-config` override file lives outside the app-root,
add a second read-only mount (same pattern as `install`):

```bash
docker run -it --rm \
  --user "$(id -u):$(id -g)" \
  -v "$(pwd)/avatar:/app-root" \
  quay.io/octopize/deploy-tool:latest update --app-root /app-root
```

### `migrate`

Mount both the new app-root **and** the legacy deployment directory as a separate read-only volume.
The `--from-legacy` path must point to its container-side location:

```bash
docker run -it --rm \
  --user "$(id -u):$(id -g)" \
  -v "$(pwd)/avatar-new:/app-root" \
  -v "$(pwd)/avatar-old:/legacy:ro" \
  quay.io/octopize/deploy-tool:latest migrate \
    --app-root /app-root \
    --from-legacy /legacy
```

### `start`

`start` calls Docker directly (to create volumes and run `docker compose up`), so the host Docker
socket must be mounted:

```bash
docker run --rm \
  -v "$(pwd)/avatar:/app-root" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$(which docker):/usr/local/bin/docker:ro" \
  -v "${COMPOSE_PLUGIN}:${COMPOSE_PLUGIN}:ro" \
  quay.io/octopize/deploy-tool:latest start --app-root /app-root
```

> **Note:** `--user` is omitted here because the process needs access to the Docker socket. If you
> see a permission error, add `--group-add $(stat -c '%g' /var/run/docker.sock)` to grant the
> container access to the socket's group on the host.

### `stop`

Like `start`, `stop` calls Docker directly and requires the host Docker socket:

```bash
docker run --rm \
  -v "$(pwd)/avatar:/app-root" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$(which docker):/usr/local/bin/docker:ro" \
  -v "${COMPOSE_PLUGIN}:${COMPOSE_PLUGIN}:ro" \
  quay.io/octopize/deploy-tool:latest stop --app-root /app-root
```

### `verify`

`verify` calls Docker directly (to exec into the running stack), so the host Docker socket must
be mounted:

```bash
docker run --rm \
  -v "$(pwd)/avatar:/app-root" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$(which docker):/usr/local/bin/docker:ro" \
  -v "${COMPOSE_PLUGIN}:${COMPOSE_PLUGIN}:ro" \
  quay.io/octopize/deploy-tool:latest verify \
    --app-root /app-root \
    --test-email your@email.com
```

> **Note:** `--user` is omitted here because the process needs access to the Docker socket. If you
> see a permission error, add `--group-add $(stat -c '%g' /var/run/docker.sock)` to grant the
> container access to the socket's group on the host.

## Non-Interactive Usage

Provide a YAML configuration file to skip interactive prompts. Any value not covered by the
config file will use the built-in default.

Example seed config:

```yaml
PUBLIC_URL: avatar.example.com
ENV_NAME: prod
ORGANIZATION_NAME: MyCompany
```

Then run:

```bash
octopize-deploy-tool install \
  --app-root ./app/avatar \
  --from-config config.yaml \
  --non-interactive
```

## Generated Files

The `install` command writes deployment files under a versioned directory tree:

```text
./app/avatar/
├── .deployment_states/
│   └── v1/
│       ├── deployment_state.yaml
│       └── generated/             # reference copy of rendered files (read-only)
├── customer_inputs/               # operator-owned configuration overrides
└── generated_files/               # what Docker Compose actually uses
    ├── .env
    ├── docker-compose.yml
    ├── nginx/nginx.conf
    ├── authentik/
    │   ├── octopize-avatar-blueprint.yaml
    │   ├── custom-templates/
    │   └── branding/
    └── .secrets/
```

Each subsequent `update` or `migrate` run adds a new versioned slot (e.g. `v2/`), keeping a full
history of prior states.

The `generate-env` command writes component `.env` files directly to their resolved destinations:

```text
./avatar-local/
├── api/.env
└── web/.env
```

## Typical Deployment Workflow

1. Generate configuration:

   ```bash
   octopize-deploy-tool install --app-root ./app/avatar
   ```

2. Review the generated files:

   ```bash
   ls ./app/avatar/generated_files/
   cat ./app/avatar/generated_files/.env
   ls -la ./app/avatar/generated_files/.secrets/
   ```

3. Add any required TLS certificates for production.

4. Start the services:

   ```bash
   octopize-deploy-tool start --app-root ./app/avatar
   ```

5. Verify the deployment:

   ```bash
   octopize-deploy-tool verify --app-root ./app/avatar --test-email your@email.com
   ```

   This checks the Avatar API health (`/health/config-full-check`) and runs an
   Authentik email delivery test. Pass `--test-email` with an address you can
   receive mail at to confirm SMTP is working.

6. Create user accounts:

   ```bash
   octopize-deploy-tool tasks create-users \
     --app-root ./app/avatar \
     --admins admin@company.com \
     --users alice@company.com,bob@company.com
   ```

   Each user receives a recovery email with a link to set their password. Re-run at any
   time to add more users — existing accounts are automatically skipped.

## `generate-env`

Use `generate-env` to create per-component `.env` files for local development without generating
the full deployment bundle.

Example:

```bash
octopize-deploy-tool generate-env \
  --component api \
  --api-output-path ./avatar-local/api/.env \
  --component web \
  --web-output-path ./avatar-local/web/.env
```

Common `generate-env` options:

```text
--config FILE              YAML configuration file to load
--non-interactive          Run without prompts, using config/defaults
--verbose                  Show detailed progress output
--component NAME           Generate only the selected component (repeatable; defaults to all)
--api-output-path PATH     Override the API env output path for this run
--web-output-path PATH     Override the web env output path for this run
--python-client-output-path PATH
                           Override the python_client env output path for this run
--output-path COMPONENT=PATH
                           Repeatable generic output-path override
--target NAME              Load named URLs from the environments config section
--api-url URL              Override the API URL
--storage-url URL          Override the storage public URL
--sso-url URL              Override the SSO provider URL
```

## Troubleshooting

### Services fail to start after `start`

Check that Docker is running and the generated files are present:

```bash
ls ./app/avatar/generated_files/
docker compose -f ./app/avatar/generated_files/docker-compose.yml ps
docker compose -f ./app/avatar/generated_files/docker-compose.yml logs -f
```

### Existing containers cause bind-mount or startup issues

Stop and remove old containers and volumes, then restart:

```bash
docker compose -f ./app/avatar/generated_files/docker-compose.yml down --volumes --remove-orphans
octopize-deploy-tool start --app-root ./app/avatar
```

### Verbose output for debugging

Add `--verbose` to any command to see detailed progress and template rendering output:

```bash
octopize-deploy-tool install --app-root ./app/avatar --verbose
```

## Migrating Users to authentik

When upgrading from an older Avatar deployment that managed users directly in the Avatar API
database, use `octopize-deploy-tool tasks authentik-migrate` to import those users into authentik.

### Overview

The tool reads three CSV exports from the Avatar database and creates the equivalent users, groups,
and group assignments in authentik via its REST API:

| Source CSV          | authentik entity             |
| ------------------- | ---------------------------- |
| `organizations.csv` | Groups (one per org)         |
| `licenses.csv`      | Group `attributes.license`   |
| `users.csv`         | Users with `attributes.role` |

Users that already have an `authentik_id` value in the CSV are automatically skipped, making the
tool safe to run incrementally.

### Extracting CSV Data

Exec into the API container and export the required tables:

```bash
docker compose exec -it api bash
cd /app/avatar
python bin/dbtool.py shell
```

Then in the pgcli shell:

```sql
\copy (SELECT * FROM licenses) TO '/tmp/licenses.csv' WITH CSV HEADER
\copy (SELECT * FROM users) TO '/tmp/users.csv' WITH CSV HEADER
\copy (SELECT * FROM organizations) TO '/tmp/organizations.csv' WITH CSV HEADER
```

### Usage

**Dry run** — preview all operations without making any changes:

```bash
octopize-deploy-tool tasks authentik-migrate \
  --users /tmp/users.csv \
  --orgs /tmp/organizations.csv \
  --licenses /tmp/licenses.csv \
  --dry-run
```

**Full migration** — execute against a live authentik instance:

```bash
octopize-deploy-tool tasks authentik-migrate \
  --users /tmp/users.csv \
  --orgs /tmp/organizations.csv \
  --licenses /tmp/licenses.csv \
  --authentik-url https://avatar.example.com/sso \
  --authentik-token YOUR_API_TOKEN
```

The authentik URL is the `/sso` path of your Avatar instance. The API token is generated
automatically by the deploy tool and stored in the `.env` file.

Every run writes a JSONL log (`migration_log.jsonl` by default). If some operations fail, retry
just the failed ones:

```bash
octopize-deploy-tool tasks authentik-migrate \
  --from-log migration_log.jsonl \
  --failed-only \
  --authentik-url https://avatar.example.com/sso \
  --authentik-token YOUR_API_TOKEN
```

### Getting an authentik API Token

1. Log in to your authentik instance
2. Navigate to **Admin** → **Tokens & App passwords**
3. Create a token with the following permissions:
   - `core:groups:create`
   - `core:users:create`
   - `core:groups:update` (for user assignments)

### Migration CLI Reference

```text
octopize-deploy-tool tasks authentik-migrate [options]

  --users PATH           Path to users CSV file
  --orgs PATH            Path to organizations CSV file
  --licenses PATH        Path to licenses CSV file
  --authentik-url URL    Base URL of the authentik instance
                         (e.g. https://avatar.example.com/sso)
  --authentik-token TOK  API token for authentik
  --dry-run              Preview operations without executing
  --log PATH             JSONL log output path (default: migration_log.jsonl)
  --from-log PATH        Replay operations from a previous log instead of CSV
  --failed-only          With --from-log, only replay previously-failed ops
```

## Further Information

- Source repository: <https://github.com/octopize/avatar>
- Deployment documentation: <https://docs.octopize.io/docs/deploying/self-hosted>
- Network topology reference: [docs/network-topology.md](docs/network-topology.md)
