Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion learn/developers/deploying-from-ci.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ A second constraint pushes the same direction: a git-reference deploy authentica

A GitHub Actions job that declares `id-token: write` can ask GitHub for an identity token that says which repository, workflow, and environment it is running for. The Harper CLI trades that token for a one-hour operation token, if it matches a **trust policy** you stored on the cluster. Nothing durable lives in your CI provider, and there is no 30-day refresh token to rotate. See [Workload identity (OIDC)](/reference/v5/cli/authentication#workload-identity-oidc) for how the CLI does this, and [`add_oidc_trust`](/reference/v5/operations-api/operations#add_oidc_trust) for every rule a policy can carry.

**Nothing in the workflow names the policy.** Once it exists, every `harper` command in the deploy job — `harper deploy`, `harper get_job`, anything else — authenticates through it by itself, and may do what the policy's user's role allows, as long as the job has `id-token: write`, `HARPER_CLI_TARGET` is the policy's `audience`, the run matches the policy's claims, and no other credential is configured. [Using it in a workflow](/reference/v5/cli/authentication#using-it-in-a-workflow) lists those conditions.

:::tip

From v5.4.0, for a workflow that deploys when changes merge to a branch, [`harper deploy setup=true
provider=github-actions`](/reference/v5/cli/commands#github-actions-deploys-oidc) creates a role, a user and a policy
pinned to that branch, and sets the `HARPER_CLI_TARGET` variable, in one command. Its role allows `deploy_component`,
`get_job` and `registration_info` only, not the `get_deployment` the role below adds. This guide's workflows deploy on
version tags, which a branch pin cannot match, so they use the policy below, written by hand.

:::

You set this up once, from your own machine, logged in as a `super_user`:

```bash
Expand All @@ -61,13 +73,14 @@ harper login https://my-cluster.example.com:9925
The user a trust policy names is the privilege boundary: a matching run gets that user's role. A pipeline does not need `super_user`. Give it a role that lists only the operations the workflows below call:

```bash
harper add_role role=ci_deploy permission='{"operations":["deploy_component","get_job","get_deployment"]}'
harper add_role role=ci_deploy permission='{"operations":["deploy_component","get_job","get_deployment","registration_info"]}'
harper add_user username=ci-deploy role=ci_deploy active=true password="$(openssl rand -base64 32)"
```

- **`deploy_component`** is the deploy itself.
- **`get_job`** is what the wait step polls. The jobs table lists `get_job` as open to any role, but an `operations` allowlist refuses every operation it doesn't list, so without it the wait step fails with `403`.
- **`get_deployment`** reads the deployment record.
- **`registration_info`** returns only Harper's version, which `harper deploy` checks before it uploads. Without it the deploy still works, but the cluster logs a refused operation on every run.

`restart_service` is not needed: the rolling restart's job is started by Harper itself, not by the caller. The password is required to create the user, but a GitHub Actions pipeline never uses it, since it authenticates through the trust policy, so this command discards it. [Other CI systems](#other-ci-systems) shows how to set one you keep.

Expand Down
34 changes: 30 additions & 4 deletions reference/cli/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,39 @@ There is no operation that revokes a refresh token directly. To invalidate one,

On a runner that can prove its own identity, the CLI needs **no stored credential at all**. It asks the runtime for an identity token addressed to your instance and trades it for a one-hour operation token. Nothing durable is stored in your CI provider, and there is no 30-day token to rotate.

Configure the instance to trust the workflow once with [`add_oidc_trust`](../operations-api/operations.md#add_oidc_trust), then grant the token permission in the workflow.
Configure the instance to trust the workflow once with [`add_oidc_trust`](../operations-api/operations.md#add_oidc_trust). For a GitHub Actions workflow that deploys from a branch, [`harper deploy setup=true provider=github-actions`](./commands.md#github-actions-deploys-oidc) (v5.4.0) does it in one command: a deploy-only role and user, the policy, and the repository's `HARPER_CLI_TARGET` variable.

On **GitHub Actions**, a job opts into requesting an identity token with `permissions: id-token: write`. Other CI systems expose the same idea differently, and Harper currently detects GitHub Actions only; an unrecognized runtime is not an error, the CLI simply falls through to its other credential sources.
###### Using it in a workflow

[Deploying from a CI/CD Pipeline](/learn/developers/deploying-from-ci#path-a-deploy-a-tagged-release-from-your-repository) has a complete workflow that runs as written, with the trust policy and deploy-only role it needs. In it, `HARPER_CLI_TARGET` is the only variable the job is given, and it is not sensitive — hence `vars` rather than `secrets`.
**Nothing in the workflow names the policy, and no `harper` command takes an option for it.** Once the policy exists, every `harper` command in a job authenticates through it by itself — `harper deploy`, `harper get_job`, or any other operation — when all four of these hold. What each command may then do is up to the role of the user the policy names: one that lists only `deploy_component`, `get_job` and `registration_info`, like the role setup creates, refuses anything else with a `403`.

**With a trust policy in place you need nothing else** — no `HARPER_CLI_REFRESH_TOKEN`, no password, no credentials on the command. That is the point of it: the workflow holds no Harper secret at all.
1. **The job may request an identity token.** On GitHub Actions that is `permissions: id-token: write` on the job.
2. **`HARPER_CLI_TARGET` is the policy's `audience`.** The CLI requests its token for the target it connects to, so the target must be the URL the policy names, with its port and trailing slash (`https://my-instance.harperdb.io:9925/`). It is not sensitive, so a repository variable (`vars`) rather than a secret.
3. **The run matches the policy's claims**: its repository, its workflow file and branch, and its environment.
4. **Nothing else is configured to authenticate.** `auth_username=`/`auth_password=`, credentials in the target URL, `HARPER_CLI_USERNAME`/`HARPER_CLI_PASSWORD`, `HARPER_CLI_REFRESH_TOKEN` or `HARPER_CLI_OPERATION_TOKEN` (or their `CLI_TARGET_` forms), and a saved `harper login` all outrank the exchange — see [Authentication Precedence](#authentication-precedence).

For example, this job deploys with nothing but the target:

```yaml
deploy:
runs-on: ubuntu-latest
environment: production
permissions:
contents: read
id-token: write
env:
HARPER_CLI_TARGET: ${{ vars.HARPER_CLI_TARGET }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- run: npm install --global harper@^5.3
- run: harper deploy project=my-app restart=rolling
```

It is one job, not a whole workflow. [Deploying from a CI/CD Pipeline](/learn/developers/deploying-from-ci#path-a-deploy-a-tagged-release-from-your-repository) has complete workflows that run as written, including the step that waits for a rolling deploy to reach every node.

Harper currently detects GitHub Actions only. Other CI systems expose the same idea differently; on one Harper does not recognize, the CLI falls through to its other credential sources rather than failing.

If your pipeline already sets a token or password, remove it. Leaving it in place is not harmful but it does keep winning — a configured credential outranks the exchange, deliberately, so that enabling this does not silently re-point an existing pipeline at a different user. See [Authentication Precedence](#authentication-precedence) if you need the full order for a mixed setup.

Expand Down
34 changes: 33 additions & 1 deletion reference/cli/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ All parameters are passed as `key=value` arguments. Every parameter is optional.
- `by_ref=true` - Deploy the current project from its GitHub `origin` remote as a pinned commit (`git+https`) instead of uploading a packaged tarball. (Added in: v5.2.3)
- `ref=<committish>` - The branch, tag, or commit to deploy. Resolved to an immutable commit SHA so every cluster node deploys the same commit. Defaults to the current `HEAD`; implies `by_ref`. (Added in: v5.2.3)
- `credential=true` - Attach the sealed credential reference so the cluster can clone a private repository. Provision it first with `harper deploy setup=true`. (Added in: v5.2.3)
- `setup=true` - Provision (seal) a durable encrypted credential for a private deploy source instead of deploying. Interactive. (Added in: v5.2.3)
- `setup=true` - Provision (seal) a durable encrypted credential for a private deploy source instead of deploying; interactive. (Added in: v5.2.3) With `provider=github-actions`, set up [GitHub Actions deploys](#github-actions-deploys-oidc) instead. (Added in: v5.4.0)

#### Waiting for a rolling deploy

Expand Down Expand Up @@ -244,6 +244,38 @@ This interactive flow prompts for the provider (a private GitHub repository or a

See [`deploy_component` credentials](../operations-api/operations.md#deploy-credentials-credentials) for the full credential-entry shape, including passing a literal `token` instead of a sealed `secret` reference.

#### GitHub Actions deploys (OIDC)

<VersionBadge version="v5.4.0" />

`harper deploy setup=true provider=github-actions` sets a cluster up so a GitHub Actions workflow can deploy with its [OIDC identity token](./authentication.md#workload-identity-oidc) instead of a stored credential. Run it once, signed in to the cluster as a `super_user` (`harper login`), from the project's checkout:

```bash
harper deploy setup=true provider=github-actions project=my-app
Comment thread
dawsontoth marked this conversation as resolved.
```

The workflow file defaults to `.github/workflows/deploy.yaml`, the name create-harper projects use. If yours has another name, such as `deploy.yml`, pass it with `workflow=`. Otherwise the policy pins `deploy.yaml`, and the runs of your workflow can't match it:

```bash
harper deploy setup=true provider=github-actions project=my-app workflow=.github/workflows/deploy.yml
```

It needs no secret custody, and it does this:

1. Takes the GitHub repository from the checkout's `origin` remote, or `repo=<owner>/<name>`, and looks it up with the `gh` CLI or GitHub's public API, for its numeric id and for the spelling of its name GitHub uses in the token. When neither answers, such as for a private repository without `gh`, pass the id as `repository_id=<id>` and spell `repo=` as GitHub does; setup says the spelling was not verified.
2. Checks the workflow file, `.github/workflows/deploy.yaml` by default (`workflow=`, a `.yml` or `.yaml` file in `.github/workflows/`): it must be able to run on `main` (`branch=`) and run a job in the `production` environment (`environment=`) with `permissions: id-token: write`. A workflow that could never match or never request a token, such as one whose push filter names only other branches and has no other trigger, one in which no job sets an environment, or one whose job in that environment doesn't grant `id-token: write`, stops setup before it writes anything. What it can't decide, such as a branch pattern, a scheduled or manual trigger, or a reusable workflow, is reported as not verified. So is the whole check when the workflow file isn't in the checkout, or the checkout is of another repository (by its `origin` remote, or `GITHUB_REPOSITORY` when that is set): run setup from the repository's own checkout with the workflow committed, or the policy it writes uses the defaults whether or not they match.
3. Creates a role and a user named `<project>-ci-deploy`, allowed only `deploy_component`, `get_job` (to wait for a rolling deploy) and `registration_info` (the version check `harper deploy` makes before it uploads), and the trust policy `github-actions-<project>`. The policy's `audience` is the target exactly as the CLI requests its token, and its claims are the repository id, the workflow and branch (`workflow_ref`), and the environment.
4. Reads the policy back and compares it, claim by claim, with what it meant to write.
5. Sets the repository's `HARPER_CLI_TARGET` variable with `gh`, if the repository doesn't have one. One that already names this cluster is left as it is. One that names another cluster is left too, since every workflow in the repository that reads it deploys there: setup prints the command to change it and exits non-zero. Without `gh`, or if the variable can't be read or written, it prints the command to run and exits non-zero.

It only creates. Running it again leaves the cluster and the variable unchanged, and a role, user or policy that already exists but differs, or that someone deactivated or disabled, stops it before it writes, naming what differs. To replace a policy, drop it with `harper drop_oidc_trust id=github-actions-<project>` and run setup again. Because `add_oidc_trust` replaces a policy, setup checks again just before writing it, but a policy with the same id that another super user creates in that moment, enabled or not, would still be overwritten. Setup never writes a policy that already exists, so it can't undo a disable or a drop.

If the variable wasn't set, the cluster part is already done: run the printed `gh variable set` command, or run setup again, which leaves the cluster as it is and tries the variable again.

The deploy user can deploy any component on the cluster, not only this project, so who can deploy comes down to who can merge to the branch. To revoke, `harper drop_oidc_trust id=github-actions-<project>` stops new runs, and `harper alter_user username=<project>-ci-deploy active=false` also stops a token already issued, which lasts an hour.

The policy pins one branch, so it suits a workflow that deploys when changes merge. A workflow that deploys on tags needs a policy written by hand: see [Deploying from a CI/CD Pipeline](/learn/developers/deploying-from-ci#add-a-trust-policy).

### `harper restart`

Available since: v4.1.0
Expand Down
2 changes: 1 addition & 1 deletion reference/cli/operations-api-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ This is just a brief overview of all operations available as CLI commands. Revie
| `restart_service` | Restart Harper service | [System](../operations-api/operations.md#registration--licensing) | v4.3.0 |
| `restart` | Restart Harper instance | [System](../operations-api/operations.md#registration--licensing) | v4.3.0 |
| `system_information` | Get system information | [System](../operations-api/operations.md#registration--licensing) | v4.3.0 |
| `registration_info` | Get registration information | [Licensing](../operations-api/operations.md#registration--licensing) | v4.3.0 |
| `registration_info` | Get the Harper version | [Licensing](../operations-api/operations.md#registration--licensing) | v4.3.0 |
| `get_usage_licenses` | Get usage and license info | [Licensing](../operations-api/operations.md#registration--licensing) | v4.7.3 |
| `create_backup` | Create a managed database backup | [Backups](../backups/operations.md) | v5.2.0 |
| `list_backups` | List managed backups | [Backups](../backups/operations.md) | v5.2.0 |
Expand Down
Loading
Loading