> For the complete documentation index, see [llms.txt](https://docs.bluerock.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bluerock.io/bluedev-container/bluedev-identity-deployment-kit.md).

# BlueDev Identity Deployment Kit

The Bluedev Identity Deployment Kit provides instructions for bringing up a StepCA server to manage OIDC provisioners and handle enrollment requests initiated by `bluedev-bouncer` on BlueRock hosts.

The StepCA server can be deployed either directly within a BlueRock VM instance or on a separate VM to serve multiple BlueRock instances. The StepCA package contains Docker files for StepCA and Caddy, along with `just` command scripts for setup and management.

* **StepCA:** Registers OIDC provisioners for `bluedevx` sign-in and accept enrollment authentication requests initiated by `bluedev-bouncer`.
* **Caddy (Optional):** Handles automatic TLS certificate generation and maintenance from Let's Encrypt for public deployments. For private, VPN-gated network architectures, StepCA can run independently without Caddy by binding directly to a private interface.

<figure><img src="https://1983702018-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCNnVnPvaRpvlLmPT6IAN%2Fuploads%2F2hbZaT7Q4t1eBaKtaunV%2FSelf-hosted-setp-CA.png?alt=media&amp;token=94c7d37d-2a1c-4510-b20a-0b6c57570e48" alt=""><figcaption></figcaption></figure>

## StepCA Deployment

Deploying the StepCA server establishes the central Certificate Authority (CA) required to issue short-lived SSH certificates for user authentication and host enrollment. The following procedures outline the end-to-end setup, from extracting the deployment package to bringing up the service.

{% hint style="success" icon="lightbulb-exclamation-on" %}
**Important:**

Whether deployed on a BlueRock VM or an independent VM, StepCA can generate TLS certificates using a Public IP address instead of a DNS domain. For successful enrollment, ensure firewall or security group rules permit inbound traffic from BlueRock host instances to the StepCA port via this IP.
{% endhint %}

### Prerequisites and Installation

1. Extract the package source:

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">$ mkdir bluedevca
   $ cp ~/bru_packages/bluedev/bluedev-identity-ca-0.1.0.tar.gz ~/bluedevca/
   $ cd ~/bluedevca/
   $ tar zxvf bluedev-identity-ca-0.1.0.tar.gz
   </code></pre>

   <div data-gb-custom-block data-tag="hint" data-style="success" data-icon="lightbulb-exclamation-on" class="hint hint-success"><p><strong>Important:</strong> </p><p>Execute the package extraction and all subsequent deployment steps using a privileged user account (not <code>root</code>) that possesses privileges to execute Docker commands.</p></div>
2. Install prerequisites:
   1. For AL2023:

      ```shellscript
      $ sudo dnf install -y spal-release
      $ sudo dnf install -y just
      $ sudo dnf install docker
      $ sudo usermod -aG docker ec2-user
      $ sudo systemctl enable docker.service
      $ sudo systemctl start docker.service
      $ sudo systemctl status docker
      $ sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$(uname -m) -o /usr/libexec/docker/cli-plugins/docker-compose
      $ sudo chmod +x /usr/local/bin/docker-compose
      ```
   2. For Ubuntu:

      ```shellscript
      $ sudo apt-get update
      $ sudo apt-get install -y docker.io just curl
      $ sudo usermod -aG docker $USER
      $ sudo systemctl enable docker.service
      $ sudo systemctl start docker.service
      $ sudo systemctl status docker
      $ sudo mkdir -p /usr/lib/docker/cli-plugins
      $ sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$(uname -m) -o /usr/lib/docker/cli-plugins/docker-compose
      $ sudo chmod +x /usr/lib/docker/cli-plugins/docker-compose
      ```

### Configure Environment

Navigate to the `compose` directory and configure the environment settings:

```shellscript
$ cd compose/
$ cp .env.example .env
$ vi .env
```

Define the following variables in the `.env` file to set the enrollment password and the host certificate validity windows:

* `BOUNCER_PASSWORD`: The password matched during BlueRock Host enrollment.
* `DEFAULT_HOST_SSH_CERT_DURATION`: Sets the validity duration assigned to enrolled hosts upon signing.
* `MAX_HOST_SSH_CERT_DURATION`: Sets the maximum validity ceiling enforced by `step-ca` for host certificates. Keep the default at or below the max.

```shellscript
BOUNCER_PASSWORD=<BOUNCER_PASSWORD>
DEFAULT_HOST_SSH_CERT_DURATION=720h
MAX_HOST_SSH_CERT_DURATION=720h
```

### **Choose a Deployment Architecture**

Configure the `.env` file based on the required network architecture:

#### Private Deployment (without Caddy)&#x20;

Use this configuration for a CA reachable over a VPN-gated private subnet or SSH tunnel (without Caddy). StepCA publishes directly on a specific interface using self-signed TLS certificates generated for the designated private IP.

```shellscript
# Host address the :9000 listener publishes on
STEPCA_BIND_ADDR=<PRIVATE_IP_ADDRESS>

# Host port for the publish + client URL
STEPCA_BIND_PORT=9000
```

{% hint style="info" icon="notes-sticky" %}
**Note:**

Avoid setting `STEPCA_BIND_ADDR` to `0.0.0.0`. This configuration generates the leaf certificate without Subject Alternative Name (SAN) information and bypasses strict firewall routing. Specify the exact private IP address and let the subnet's security group serve as the security boundary.

Without Caddy deployment, `CADDY_DOMAIN` should be blank.
{% endhint %}

#### Public Deployment (with Caddy)

Use this configuration for a publicly reachable StepCA server using Let's Encrypt or a local CA:

```shellscript
# Public DNS address OR Public IP address of the Step CA VM instance
CADDY_DOMAIN=<STEP_CA_VM_PUBLIC_DNS_OR_PUBLIC_IP>

# Email used for Let's Encrypt registration
CADDY_ACME_EMAIL=<ADMIN_EMAIL>

# Standard incoming ports to allow in AWS Security Group
CADDY_HTTP_PORT=80
CADDY_HTTPS_PORT=443
```

{% hint style="info" icon="notes-sticky" %}
**Note:**

When a registered public DNS domain is unavailable, set `CADDY_DOMAIN` directly to the Public IP address of the VM instance (for example, `CADDY_DOMAIN=000.0.000.00`). StepCA will include the Public IP in the Subject Alternative Names (SANs) of the certificate so client handshakes validate without certificate naming errors.

Without Caddy deployment, `CADDY_DOMAIN` should be blank.
{% endhint %}

## Configure Google Identity Provisioner

1. Create a Google OIDC Desktop Client App via the Google Cloud Console (*Requires admin privileges*) to enable Google Workspace integration.\
   \
   To create a Google OIDC app:
   1. Access the **Google Cloud Console** and select or create a project.
   2. Navigate to **APIs & Services**, then select **Credentials**.
   3. Configure the **OAuth Consent Screen** by selecting the user type (**Internal** or **External**) and defining the mandatory fields, such as the App Name and support email.
   4. Select **Create Credentials** and choose **OAuth client ID**.
   5. Select **Desktop** app as the **Application type**. This automatically sets the redirect URI to `http://127.0.0.1`. Define a name and select **Create**.
   6. Copy the generated **Client ID** and **Client Secret**.

{% hint style="success" icon="lightbulb-exclamation-on" %}
**Important:**

For additional context on Google's authentication protocols, refer to the official documentation:

* [OAuth 2.0 API Guidelines](https://developers.google.com/identity/protocols/oauth2)
* [OpenID Connect Overview](https://developers.google.com/identity/openid-connect/openid-connect)
  {% endhint %}

2. Copy and edit the Google provider template:

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">$ cd providers/
   $ cp provisioner.env.example google.env
   $ vi google.env
   </code></pre>
3. Configure the required parameters:

   ```shellscript
   PROVISIONER_NAME=google
   ISSUER_URL=https://accounts.google.com
   CLIENT_ID=<GOOGLE_CLIENT_ID>
   CLIENT_SECRET=<GOOGLE_CLIENT_SECRET>
   ADMINS=<ADMIN_EMAIL_ADDRESS>
   DOMAIN_RESTRICT=<AUTH_DOMAIN>
   BLUEDEV_DOMAINS=<AUTH_DOMAIN>
   DEFAULT_SSH_CERT_DURATION=8h
   MAX_SSH_CERT_DURATION=24h
   ```

### **Google Workspace Provisioner Configuration Variables**

Review and define the following variables carefully before executing the provisioner setup script. Incorrect values will prevent successful host enrollment and developer authentication.

<table data-header-hidden data-search="false"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><strong>Variable</strong></td><td><strong>Description</strong></td><td><strong>Notes</strong></td></tr><tr><td><code>PROVISIONER_NAME</code></td><td>The internal identifier for the OIDC provisioner within the StepCA configuration.</td><td>Example: <code>google</code></td></tr><tr><td><code>ISSUER_URL</code></td><td>The OpenID Connect discovery endpoint for Google Workspace.</td><td>The value must be exactly <code>https://accounts.google.com</code>.</td></tr><tr><td><code>CLIENT_ID</code></td><td>The OAuth 2.0 Client ID generated within the Google Cloud Console credentials page.</td><td>Required to identify the application to Google's authorization servers.</td></tr><tr><td><code>CLIENT_SECRET</code></td><td>The sensitive OAuth 2.0 Client Secret paired with the designated Client ID.</td><td>Must be kept secure and never committed to version control.</td></tr><tr><td><code>ADMINS</code></td><td>A comma-separated list of administrator email addresses authorized to manage the CA.</td><td>Example: <code>admin@domain.com</code></td></tr><tr><td><code>DOMAIN_RESTRICT</code></td><td>The specific email domain authorized to authenticate via this OIDC provisioner.</td><td>Example: <code>bluerock.io</code></td></tr><tr><td><code>BLUEDEV_DOMAINS</code></td><td>The domain name utilized for generating the SSH principal mapping.</td><td>Typically matches the <code>DOMAIN_RESTRICT</code> value.</td></tr><tr><td><code>DEFAULT_SSH_CERT_DURATION</code></td><td>The standard time-to-live (TTL) validity period assigned to issued SSH certificates.</td><td>Example: <code>8h</code></td></tr><tr><td><code>MAX_SSH_CERT_DURATION</code></td><td>The absolute maximum validity period permitted for issued SSH certificates.</td><td>Example: <code>24h</code></td></tr></tbody></table>

{% hint style="info" %}
**Note:**&#x20;

Configuring the `BLUEDEV_DOMAINS` parameter is mandatory. This variable is required to correctly map and generate the SSH principal during authentication.
{% endhint %}

### Service Bring-Up and Management

Review available commands using `just help`:

```shellscript
bluedev-identity — CA-side deployment kit

  SETUP
    just up [--no-caddy]              bring step-ca [+ caddy] up; register provisioners
    just add <name>                   register OIDC provisioner from providers/<name>.env
    just remove <name>                remove OIDC provisioner from step-ca

  SHARE WITH DOWNSTREAM
    just fingerprint [<h:p>|--auto]   print CA root SPKI (or full pin URL)
    just bouncer-info                 print bouncer enrollment inputs (URL + name + password)

  MAINTENANCE
    just update [ca|caddy|all]        apply config changes (default: all) — non-destructive
    just rotate-bouncer-password      re-key bouncer provisioner (DESTRUCTIVE: invalidates enrolled bouncers)
    just info                         read-only health + state probe
    just dump-config [ca|caddy|defaults]
                                      print live container config (default: ca)
    just down [--force]               destructive teardown (containers + volumes)

  KIT DEVELOPMENT
    just test [unit|component|all]    run the test gate (auto-detects step-ca for component)

  Per-verb detail:  just <verb> --help
```

1. Register the OIDC provider and start services:

   ```shellscript
   $ just add google

   $ just up
   ```
2. Check health status:

   ```shellscript
   $ just info
   ```

   \
   Expected Output:

   ```shellscript
   == bluedev-identity info (read-only) ==

     filesystem
       [ ok ]   compose/.env present                     
       [ ok ]   BOUNCER_PASSWORD set                     

     docker
       [ ok ]   daemon reachable                         
       [ ok ]   compose plugin v2                        

     containers
       [ ok ]   step-ca running                          
       [ ok ]   caddy running                            (ec2-3-144-79-172.us-east-2.compute.amazonaws.com)

     step-ca api
       [ ok ]   /health 200 ok                           

     provisioners
       [ ok ]   google registered                        

     bluedev JWK provisioners
       [ ok ]   bluedev-bouncer registered               (dev-host bouncers auth as this)
       [ ok ]   admin (break-glass)                      info-only; not for routine signing

   Summary: HEALTHY

   ```
3. Retrieve the auto-generated CA enrolment string:

   ```shellscript
   $ just fingerprint --auto
   ```

   Output format:

   ```shellscript
   https://<STEP_CA_VM_PUBLIC_DNS_OR_PUBLIC_IP>:443#fp=<CA_FINGERPRINT_HASH>

   ## Example using Public IP:
   `https://000.0.000.00:443#fp=a1b2c3d4e5...`
   ```

   \
   For private deployments, the generated fingerprint URL utilizes the specified private IP address.\
   \
   Output format:

   ```shellscript
   https://<STEP_CA_VM_PRIVATE_IP>:443#fp=<CA_FINGERPRINT_HASH>

   ## Example using Private IP:
   `https://x0x.x0.x0.xx:443#fp=a1b2c3d4e5...`
   ```

{% hint style="success" icon="lightbulb-exclamation-on" %}
**Important:**

Executing a destructive teardown regenerates the CA fingerprint and requires re-enrolling the bouncer on all VMs. Do not execute `just down --force` for routine configuration modifications.

To apply configuration changes to an existing deployment without losing the CA fingerprint, execute the non-destructive update command:

```shellscript
$ just update ca
```

{% endhint %}

### Enrol BlueRock Host

Log in to the BlueRock VM and execute the enrollment command using the generated CA URL:

```shellscript
$ sudo bluedev-bouncer enroll --ca https://<STEP_CA_VM_PUBLIC_DNS_OR_PUBLIC_IP>:443#fp=<CA_FINGERPRINT_HASH> --password
```

Enter the `BOUNCER_PASSWORD` when prompted

{% hint style="info" icon="notes-sticky" %}
**Note:**

**The**  `<STEP_CA_VM_PUBLIC_DNS_OR_PUBLIC_IP>` accepts either the domain name or raw **Public IP address** of your StepCA server. For private deployments without Caddy, the URL will use the StepCA server's private IP address. Ensure the BlueRock host has network connectivity to this private IP (for example, by residing in the same private subnet or being reachable via VPN).
{% endhint %}

## Modifying Certificate Expiration (TTL)

Updating these configuration values is only required if the validity window needs to be changed after the initial CA deployment.

### Modifying User Certificate Expiration (TTL)

To extend the session validity period and reduce sign-out frequency, update the Time-to-Live (TTL) settings in the provider configuration.

1. Open the relevant provider environment file (for example, `providers/entra.env` or `providers/google.env`).
2. Update the duration parameters to the required validity period (for example, 168 hours). The `MAX_SSH_CERT_DURATION` value must always be greater than or equal to the `DEFAULT_SSH_CERT_DURATION` value:

   ```yaml
   DEFAULT_SSH_CERT_DURATION=168h
   MAX_SSH_CERT_DURATION=168h
   ```
3. Apply the configuration changes by executing the non-destructive update command:

   ```shellscript
   just update ca
   ```

{% hint style="info" icon="notes" %}
**Note:**&#x20;

Updated validity periods take effect upon the next authentication. Existing sessions maintain the current expiration until the next sign-in.
{% endhint %}

### Modifying Host Certificate Expiration

To modify the host SSH certificate lifetimes on the StepCA server after the initial deployment:

1. Open `./compose/.env` in the identity CA kit directory (`/opt/bluedev-identity`) on the StepCA VM instance.
2. Update the `DEFAULT_HOST_SSH_CERT_DURATION` and `MAX_HOST_SSH_CERT_DURATION` variables to the new required validity period.
3. Execute the non-destructive update command on the StepCA VM instance to apply the updated environment variables:

   ```shellscript
   $ just update ca
   ```

{% hint style="success" icon="lightbulb-exclamation-on" %}
**Important:**

Do not execute `just down --force && just up`. This destructive teardown regenerates the CA root and invalidates the pinned fingerprint on all existing hosts. Additionally, duration changes only take effect during the next enrollment or certificate renewal, and existing host certificates maintain their original expiration dates.
{% endhint %}

## Renewing the Host Certificate After TTL Change&#x20;

Host certificates require manual monitoring for expiration. To prevent connectivity loss, verify the remaining time-to-live (TTL) and manually renew the certificate before it expires.

### Renewing the Host Certificate

To renew a certificate before the TTL expires, retrieve the credentials from the StepCA server and execute the enrollment command again.

1. **Retrieve CA Information** (On the StepCA VM): \
   Retrieve the required CA URL, provisioner, and password by running the following command on the StepCA VM instance:

   ```shellscript
   $ just bouncer-info
   ```
2. **Execute Renewal** (On the BlueRock Host): \
   Log in to the affected BlueRock host and execute the enrollment command to renew the certificate:

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">$ sudo bluedev-bouncer enroll --ca 'https://&#x3C;STEP_CA_VM_IP_OR_DNS>:&#x3C;PORT-NUMBER>#fp=&#x3C;SPKI-FINGERPRINT>' --password
   </code></pre>

**Operational Requirements:**

* **`--ca`** (Mandatory): \
  Must include the full URL appended with `#fp=<SPKI-FINGERPRINT>`. A non-interactive enrollment refuses a bare fingerprint.
* **`--password`** (Mandatory): \
  Must read from standard input (`stdin`) to prevent exposing plain-text credentials in process list arguments.
* **`--principals`** (Optional): \
  Required to map the host's primary private IP address. Omitting this flag generates a valid certificate that leaves the host unreachable.

{% hint style="info" icon="notes" %}
**Note:**&#x20;

Back up `/etc/bluedev/user_ca.pub` prior to renewal and confirm it remains unchanged afterward, as it carries the appended gateway CA. The `enroll` command automatically reloads `sshd` upon completion.
{% endhint %}

### Checking Host Certificate Validity

To check the host certificate status and remaining TTL directly on the BlueRock host instance, run the primary JSON health probe:

```shellscript
 $ sudo bluedev-bouncer self-check --json
```

The output reports the remaining validity window and warns when a renewal is required.

**Expected Output:**

```shellscript
{
  "name": "host cert /etc/ssh/ssh_host_ed25519_key-cert.pub",
  "ok": true,
  "detail": "valid until <TIMESTAMP> (<N> days from now)"
}
```

Alternatively, inspect the certificate and view the principals via `ssh-keygen`:

```shellscript
$ sudo ssh-keygen -L -f /etc/ssh/ssh_host_ed25519_key-cert.pub | grep -E 'Valid|Principals' -A1
```

{% hint style="success" icon="lightbulb-exclamation-on" %}
**Important:**&#x20;

The host's primary private IP address must appear under `Principals`. A certificate within its validity window but missing the private IP principal fails exactly like an expired certificate and causes connection failures.
{% endhint %}

## Configure Microsoft Entra ID Provisioner

1. Create an OIDC Desktop App using App Registrations in the Azure Portal to enable Microsoft Entra ID integration.\
   \
   To create a Microsoft Entra ID app:
   1. Access the **Azure Portal** and navigate to **Microsoft Entra ID**.
   2. Select **App registrations**, then select **New registration**.
   3. Define the application name and select the supported organizational directory account types.
   4. Set the **Platform** to **Public client (mobile & desktop)**.
   5. Set the Redirect URI to `http://127.0.0.1`.
   6. Select **Register** and copy the generated Application (client) ID.

{% hint style="success" icon="lightbulb-exclamation-on" %}
**Important:**

For detailed architectural context, refer to the official [Microsoft identity platform documentation](https://learn.microsoft.com/en-us/entra/identity-platform/v2-overview).
{% endhint %}

2. Create and edit `entra.env`:

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">$ cd providers/
   $ vi entra.env
   </code></pre>
3. Configure the required parameters:

   ```shellscript
   PROVISIONER_NAME=entra
   ISSUER_URL=https://login.microsoftonline.com/<TENANT_ID>/v2.0/.well-known/openid-configuration
   CLIENT_ID=<ENTRA_OIDC_APP_CLIENT_ID>
   ADMINS=<ADMIN_EMAIL_ADDRESS>
   DOMAIN_RESTRICT=<AUTH_DOMAIN>
   BLUEDEV_DOMAINS=<AUTH_DOMAIN>
   DEFAULT_SSH_CERT_DURATION=8h
   MAX_SSH_CERT_DURATION=24h
   ```

### **Entra ID Provisioner Configuration Variables**

Review and define the following variables carefully before executing the provisioner setup script. Incorrect values will prevent successful host enrollment and developer authentication.

| Variable                    | Description                                                                          | Notes                                                 |
| --------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------- |
| `PROVISIONER_NAME`          | The internal identifier for the OIDC provisioner within the StepCA configuration.    | Example: `entra`                                      |
| `ISSUER_URL`                | The OpenID Connect discovery endpoint for the Microsoft Entra ID tenant.             | Replace `<TENANT_ID>` with the exact Entra tenant ID. |
| `CLIENT_ID`                 | The Application (client) ID generated during the Entra ID app registration process.  | Located in the Entra ID application overview panel.   |
| `ADMINS`                    | A comma-separated list of administrator email addresses authorized to manage the CA. | Example: `admin@domain.com`                           |
| `DOMAIN_RESTRICT`           | The specific email domain authorized to authenticate via this OIDC provisioner.      | Example: `bluerock.io`                                |
| `BLUEDEV_DOMAINS`           | The domain name utilized for generating the SSH principal mapping.                   | Typically matches the `DOMAIN_RESTRICT` value.        |
| `DEFAULT_SSH_CERT_DURATION` | The standard time-to-live (TTL) validity period assigned to issued SSH certificates. | Example: `8h`                                         |
| `MAX_SSH_CERT_DURATION`     | The absolute maximum validity period permitted for issued SSH certificates.          | Example: `24h`                                        |

{% hint style="info" %}
**Note:**&#x20;

Configuring the `BLUEDEV_DOMAINS` parameter is mandatory. This variable is required to correctly map and generate the SSH principal during authentication.
{% endhint %}

### Bring-Up Service

Register the Entra ID provider and start services:

```shellscript
$ just add entra
$ just up
```

### Retrieve Fingerprint and Enroll Host

1. Get the host enrollment string:

   ```shellscript
   $ just fingerprint --auto
   ```

   \
   Expected output:

   ```shellscript
   https://<STEP_CA_VM_PUBLIC_DNS>:443#fp=<CA_FINGERPRINT_HASH>
   ```
2. Log in to the target BlueRock VM and execute enrollment:

   ```shellscript
   $ sudo bluedev-bouncer enroll --ca https://<STEP_CA_VM_PUBLIC_DNS>:443#fp=<CA_FINGERPRINT_HASH> --password
   ```

   \
   Enter the `BOUNCER_PASSWORD` when prompted

### Unenroll a BlueRock Host

If the BlueRock Identity Service was previously configured on a host instance, the bouncer must be unenrolled to prevent configuration conflicts before switching to a different authentication method (such as SSH Key Trust) or decommissioning the environment.

To reverse a host enrollment and clear the Certificate Authority (CA) trust configuration, execute the following command directly on the BlueRock host instance terminal:

```shellscript
$ sudo bluedev-bouncer unenroll
```

Command Actions:

The `unenroll` command reverses the `enroll` process. Upon execution, it performs the following system modifications:

* Removes the bouncer SSH configuration file (`/etc/ssh/sshd_config.d/51-bluedev-bouncer-ca.conf`).
* Deletes the CA public key located at `/etc/bluedev/user_ca.pub` (unless the `--keep-ca-pubkey` flag is specified).
* Removes all host-certificate files (unless the `--keep-host-cert` flag is specified).

## Bluedev Bouncer CLI Reference

The `bluedev-bouncer` runs directly on the target BlueRock host to handle system setup and CA trust enrollment. Below is the complete command-line interface surface for operator reference.

```shellscript
$ sudo bluedev-bouncer --help
```

Expected output:

```shellscript
CLI surface of the bouncer.

The binary has exactly two shapes:

- **Session mode** — sshd `ForceCommand` runs the binary with empty argv; clap parses no subcommand; the bouncer reads the verb from `SSH_ORIGINAL_COMMAND` and routes through the wire verb allowlist (`crates/verbs`). The wire cannot reach any operator surface. - **Operator mode** — one of the [`Command`] subcommands. All host-mutation and diagnostic verbs go here for a single uniform surface. Plan 08 (`plans/08-install-subcommand.md`) drove the lifecycle half; `self-check` / `print-paths` / `build-catalog` / `install-catalog-packages` were promoted alongside so the operator-facing CLI doesn't mix flag-style and subcommand-style modes.

Usage: bluedev-bouncer [OPTIONS] [COMMAND]

Commands:
  install                   Install bluedev-bouncer on this host. Performs the host setup: useradd, conffile placement, state-dir creation, sshd snippet two-phase commit, tmpfiles, systemctl reload. Idempotent; requires root
  uninstall                 Uninstall bluedev-bouncer. Reverses `install`. Catalog and packages dirs are preserved unless `--purge`
  enroll                    Opt-in CA trust + (optional) host-cert signing via a CA provisioner. Writes `/etc/bluedev/user_ca.pub`, optionally signs the host's SSH host keys, writes `/etc/ssh/sshd_config.d/51-bluedev-bouncer-ca.conf`. Trust-only path and JWK signing path are both wired
  unenroll                  Reverse of `enroll`. Removes the `51-*` snippet, the CA pubkey at `/etc/bluedev/user_ca.pub` (unless `--keep-ca-pubkey`), and every host-cert file (unless `--keep-host-cert`)
  doctor                    Deprecated alias for `self-check`. The install/enroll artifact checks doctor used to run (snippets, host-cert expiry, `sshd -t`) are now the root phase of the unified `self-check`; this variant prints a deprecation notice and defers to it
  self-check                Operator health probe. Verifies docker daemon reachability, devcontainer CLI on PATH, `git >= 2.25`, allowlist files present, scratch perms, etc. Exit codes per check
  print-paths               Print resolved `[paths]` values from the config file as shell-eval-safe `KEY='value'` lines on stdout. One parser (this binary) owns the TOML schema; downstream scripts read from here
  sessions                  Admin view: list every bouncer-managed container, its owner, and any live client sessions (client IP + connect time). Read-only; run as root
  build-catalog             Pre-build every devcontainer template under `[paths] catalog_root` so subsequent `up` invocations skip the slow image-build step. Writes to `bluedev-primed/<id>:latest`
  install-catalog-packages  Bake every template's `[paths] packages_root` `.deb`/`.rpm` contents into the primed image. Skips `devcontainer build` — `--install-catalog-packages` invokes it internally — so subsequent `up` skips both build AND runtime package install
  link                      Serve one protocol-v2 link connection over stdin/stdout. The v2 sshd `ForceCommand` entry point: reads the handshake from stdin, authenticates from the SSH session environment, and runs the multiplexed dispatch loop until the link closes
  help                      Print this message or the help of the given subcommand(s)

Options:
      --config <CONFIG>
          Path to the bouncer's main config file. Applies to every subcommand that loads config (session mode included)
          
          [default: /etc/bluedev/bouncer.toml]

  -h, --help
          Print help (see a summary with '-h')
```

### Command reference table

<table data-header-hidden data-search="false"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><strong>Command</strong></td><td><strong>Usage / Description</strong></td><td><strong>Example</strong></td></tr><tr><td><code>install</code></td><td>Installs <code>bluedev-bouncer</code> on the host. Performs system setup (user creation, configuration placement, <code>sshd</code> snippet setup, systemctl reload). Idempotent; requires <code>root</code>.</td><td><code>sudo bluedev-bouncer install</code></td></tr><tr><td><code>uninstall</code></td><td>Uninstalls the bouncer, reversing the <code>install</code> process. Preserves catalog and package directories unless <code>--purge</code> is used.</td><td><code>sudo bluedev-bouncer uninstall --purge</code></td></tr><tr><td><code>enroll</code></td><td>Configures CA trust and optionally signs host SSH certificates via a CA provisioner. Writes necessary CA and host-cert configuration files.</td><td><code>sudo bluedev-bouncer enroll --ca 'https://&#x3C;CA>:9000#fp=&#x3C;FP>'</code></td></tr><tr><td><code>unenroll</code></td><td>Reverses the <code>enroll</code> process. Removes the CA public key and host certificates unless specific <code>--keep</code> flags are provided.</td><td><code>sudo bluedev-bouncer unenroll --keep-host-cert</code></td></tr><tr><td><code>self-check</code></td><td>Runs health probes to verify Docker daemon reachability, DevContainer CLI availability, <code>git</code> version, file permissions, and certificate validity.</td><td><code>sudo bluedev-bouncer self-check --json</code></td></tr><tr><td><code>doctor</code></td><td><em><strong>[Deprecated]</strong>.</em> Alias for <code>self-check</code>. Prints a deprecation notice and defers to <code>self-check</code>.</td><td><code>sudo bluedev-bouncer doctor</code></td></tr><tr><td><code>print-paths</code></td><td>Prints resolved configuration paths as shell-safe <code>KEY='value'</code> variables for downstream scripts to read.</td><td><code>bluedev-bouncer print-paths > paths.env</code></td></tr><tr><td><a href="#view-active-bluedev-bouncer-sessions"><code>sessions</code></a></td><td>Provides an admin view listing every bouncer-managed container, its owner, and active client sessions (IP and connect time). Read-only; requires <code>root</code>.</td><td><code>sudo bluedev-bouncer sessions</code></td></tr><tr><td><code>build-catalog</code></td><td>Pre-builds every DevContainer template to skip the slow image-build step during subsequent <code>up</code> invocations.</td><td><code>sudo bluedev-bouncer build-catalog</code></td></tr><tr><td><code>install-catalog-packages</code></td><td>Bakes <code>.deb</code>/<code>.rpm</code> packages into the primed image, bypassing runtime package installation for faster startup.</td><td><code>sudo bluedev-bouncer install-catalog-packages</code></td></tr><tr><td><code>link</code></td><td>Internal command used by <code>sshd ForceCommand</code> for protocol-v2 link connections. Handles handshakes and multiplexed dispatch loops.</td><td><em>(Internal use by sshd daemon; not run manually)</em></td></tr><tr><td><code>help</code></td><td>Prints the help message or detailed usage for a specific subcommand.</td><td><code>sudo bluedev-bouncer help enroll</code></td></tr></tbody></table>

#### Global Options

| **Option**          | **Usage / Description**                                                                  | **Example**                                               |
| ------------------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| `--config <CONFIG>` | Overrides the path to the main configuration file. Default: `/etc/bluedev/bouncer.toml`. | `sudo bluedev-bouncer --config /opt/custom.toml sessions` |
| `-h, --help`        | Prints a summary help message.                                                           | `sudo bluedev-bouncer --help`                             |

### View Active `bluedev-bouncer` Sessions

Execute the `bluedev-bouncer sessions` command to display all containers managed by the `bluedev-bouncer`   and view active client sessions on the BlueRock host:

```bash
$ sudo bluedev-bouncer sessions

CONTAINER ID  NAME                   WORKSPACE                 OWNER                      IMAGE                                      STATE    SESSIONS  CLIENTS
9e224510d573  coral-claude-ar-c1     claude-workload-<ID>      <USER_EMAIL>@<domain>.com  bluedev-primed/coral-claudedesktop:latest  running  1         <CLIENT_IP> (11m)
5f19294b67f5  ubuntu-bluerock        ubuntu-bluerock-<ID>      <USER_EMAIL>@<domain>.com  bluedev-primed/ubuntu-bluerock:latest      running  1         <CLIENT_IP> (15s)
8c998a803c05  ubuntu-bluerock-ar-c2  ubuntu-bluerock-<ID>      <USER_EMAIL>@<domain>.com  bluedev-primed/ubuntu-bluerock:latest      running  2         <CLIENT_IP> (10m), <CLIENT_IP> (3m)
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.bluerock.io/bluedev-container/bluedev-identity-deployment-kit.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
