From 797c55168fc02b51bb85cbc8d415da5e66b60f52 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Thu, 8 Oct 2026 16:56:42 -0400 Subject: [PATCH 01/11] Document `harper deploy setup=true provider=github-actions`, and how a workflow uses a trust policy - commands.md: a GitHub Actions deploys (OIDC) section for the new setup provider: what it checks and creates, that it only creates, its names, and how to revoke; `setup=true` mentions it - authentication.md: a "Using it in a workflow" subsection that says plainly that nothing in a workflow names the policy, and every `harper` command in a job authenticates through it when four conditions hold, with a one-job excerpt - operations.md and the CI guide point at both, and the guide notes the setup command suits branch-triggered workflows, not its tag-triggered ones - 5.4 release notes: the new setup Co-Authored-By: Claude Opus 5.5 --- learn/developers/deploying-from-ci.mdx | 9 +++++++ reference/cli/authentication.md | 34 +++++++++++++++++++++++--- reference/cli/commands.md | 26 +++++++++++++++++++- reference/operations-api/operations.md | 2 +- release-notes/v5-lincoln/5.4.md | 4 +++ 5 files changed, 69 insertions(+), 6 deletions(-) diff --git a/learn/developers/deploying-from-ci.mdx b/learn/developers/deploying-from-ci.mdx index 9c91bfe4..7b358b61 100644 --- a/learn/developers/deploying-from-ci.mdx +++ b/learn/developers/deploying-from-ci.mdx @@ -50,6 +50,15 @@ 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, 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 + + 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) does this whole section in one +command: the role, the user, a policy pinned to that branch, and the `HARPER_CLI_TARGET` variable. 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 diff --git a/reference/cli/authentication.md b/reference/cli/authentication.md index 25eabfb3..4e671d3c 100644 --- a/reference/cli/authentication.md +++ b/reference/cli/authentication.md @@ -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) does it in one command: the policy, a deploy-only user for it, 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: -**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.** A password, a `HARPER_CLI_REFRESH_TOKEN`, a saved `harper login`, or `auth_username=` outranks the exchange — see below. + +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. diff --git a/reference/cli/commands.md b/reference/cli/commands.md index 4ce79f0a..7be5b28a 100644 --- a/reference/cli/commands.md +++ b/reference/cli/commands.md @@ -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=` - 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 @@ -244,6 +244,30 @@ 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) + + + +`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 +``` + +It needs no secret custody, and it does this: + +1. Takes the GitHub repository from the checkout's `origin` remote, or `repo=/`, and looks up its numeric id with the `gh` CLI or GitHub's public API. `repository_id=` skips the lookup. +2. Checks the workflow file, `.github/workflows/deploy.yaml` by default (`workflow=`): it must deploy on pushes to `main` (`branch=`) and run a job in the `production` environment (`environment=`). A workflow that could never match stops setup before it writes anything; one it cannot read, or that isn't in the checkout, is reported as not verified. +3. Creates a role and a user named `-ci-deploy`, allowed only `deploy_component` and `get_job` (to wait for a rolling deploy), and the trust policy `github-actions-`. 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`. Without `gh`, or if the write fails, it prints the command to run and exits non-zero. + +It only creates. Running it again changes nothing, 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-` and run setup 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-` stops new runs, and `harper alter_user username=-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 diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index 23e1e330..eb158cb9 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -620,7 +620,7 @@ A CI runner can authenticate to Harper with **no stored credential**. It present The alternative is a `HARPER_CLI_REFRESH_TOKEN` secret: a 30-day credential, one per user, that expires on a schedule nobody tracks. A trust policy replaces it with a rule you configure once, and revoke with `drop_oidc_trust`. -The workflow needs `permissions: id-token: write` and the `HARPER_CLI_TARGET` variable, and no secret at all, since the target is not sensitive. [Deploying from a CI/CD Pipeline](/learn/developers/deploying-from-ci#let-the-workflow-authenticate-with-oidc) has a complete workflow, with the trust policy and deploy-only role it needs. See [CLI Authentication](../cli/authentication.md#workload-identity-oidc) for the client half and where the exchange sits in credential precedence. +The workflow needs `permissions: id-token: write` and the `HARPER_CLI_TARGET` variable, and no secret at all, since the target is not sensitive. Nothing in it names the policy: every `harper` command in the job authenticates through it by itself — see [Using it in a workflow](../cli/authentication.md#using-it-in-a-workflow). For a workflow that deploys from a branch, [`harper deploy setup=true provider=github-actions`](../cli/commands.md#github-actions-deploys-oidc) creates the policy and a deploy-only user for it in one command. [Deploying from a CI/CD Pipeline](/learn/developers/deploying-from-ci#let-the-workflow-authenticate-with-oidc) has a complete workflow, with the trust policy and deploy-only role it needs. See [CLI Authentication](../cli/authentication.md#workload-identity-oidc) for the client half and where the exchange sits in credential precedence. Policies live in the replicated `system.hdb_oidc_trust` table, so configuring one on any node applies cluster-wide. diff --git a/release-notes/v5-lincoln/5.4.md b/release-notes/v5-lincoln/5.4.md index 32120367..23e84479 100644 --- a/release-notes/v5-lincoln/5.4.md +++ b/release-notes/v5-lincoln/5.4.md @@ -23,3 +23,7 @@ A durable MQTT session now resumes through the same checked replay. When a recon ### Certifying a Release in a Canary Worker A `deploy_component` with `restart: true` or `restart: "rolling"` now checks its release before rolling it out. The first worker started on the release, the canary, is held out of traffic until it reports whether the deployed component loaded, and the rollout goes on only if it did. Each later replacement is checked the same way. When the node that received the deploy rejects the release, the deploy fails with `400`. The release it replaced is made live again, or, with nothing to go back to, the component fails closed on that node. A new `certification` field in the response reports the outcome, and says when a restarting deploy went out unchecked (`uncertified` or `unavailable`, as for a `package: file:` deploy). With `"rolling"`, each other node then certifies the release with a canary of its own, and a node that rejects it fails the deploy's job, not the deploy: poll its `restartJobId` with `get_job` for the outcome. A node running a version before 5.4.0 activates without a canary. A deploy that restarts nothing is not test-loaded. If its release throws when it loads, `get_status` reports the failure. See [Certifying a release in a canary worker](/reference/v5/operations-api/operations#certifying-a-release-in-a-canary-worker). + +### Setting Up GitHub Actions Deploys + +`harper deploy setup=true provider=github-actions` sets a cluster up so a GitHub Actions workflow can deploy with its OIDC identity token instead of a stored credential. It creates a deploy-only role and user, a trust policy matching the workflow's repository, branch and environment, and the repository's `HARPER_CLI_TARGET` variable. It checks the policy against the workflow file before it writes, and reads it back after. It only creates, so running it again changes nothing, and a record that differs, or that someone deactivated, stops it before it writes. See [GitHub Actions deploys (OIDC)](/reference/v5/cli/commands#github-actions-deploys-oidc). From c9a6c60598018f29185b552a5bc280146ffda18c Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Thu, 8 Oct 2026 21:41:30 -0400 Subject: [PATCH 02/11] Match the setup steps to the final provider: the repository is always looked up, and what the workflow check refuses Co-Authored-By: Claude Opus 5.5 --- reference/cli/commands.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/cli/commands.md b/reference/cli/commands.md index 7be5b28a..c3edb5b8 100644 --- a/reference/cli/commands.md +++ b/reference/cli/commands.md @@ -256,8 +256,8 @@ harper deploy setup=true provider=github-actions project=my-app It needs no secret custody, and it does this: -1. Takes the GitHub repository from the checkout's `origin` remote, or `repo=/`, and looks up its numeric id with the `gh` CLI or GitHub's public API. `repository_id=` skips the lookup. -2. Checks the workflow file, `.github/workflows/deploy.yaml` by default (`workflow=`): it must deploy on pushes to `main` (`branch=`) and run a job in the `production` environment (`environment=`). A workflow that could never match stops setup before it writes anything; one it cannot read, or that isn't in the checkout, is reported as not verified. +1. Takes the GitHub repository from the checkout's `origin` remote, or `repo=/`, 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=` 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=`). A workflow that could never match, such as one whose push filter names only other branches and has no other trigger, or in which no job sets an environment, 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. 3. Creates a role and a user named `-ci-deploy`, allowed only `deploy_component` and `get_job` (to wait for a rolling deploy), and the trust policy `github-actions-`. 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`. Without `gh`, or if the write fails, it prints the command to run and exits non-zero. From c8a608bed9d48580f0757dcd8659ae3d90946995 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Thu, 8 Oct 2026 21:50:25 -0400 Subject: [PATCH 03/11] Close the setup tip, and say authentication is not authorization From the pre-push review: - the CI guide's :::tip closing marker sat on the paragraph's last line, so the admonition never closed; it now has a line of its own - "every harper command authenticates" now says each may do only what the policy's user's role allows, and the guide notes setup's role lacks the get_deployment the manual role adds - condition 4 lists every credential that outranks the exchange, and links the precedence order - commands.md says a missing workflow file leaves the whole check unverified, that a concurrent writer can still overwrite the policy, and what to do when setting the variable fails; every page names the role setup creates Co-Authored-By: Claude Opus 5.5 --- learn/developers/deploying-from-ci.mdx | 11 +++++++---- reference/cli/authentication.md | 6 +++--- reference/cli/commands.md | 8 +++++--- reference/operations-api/operations.md | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/learn/developers/deploying-from-ci.mdx b/learn/developers/deploying-from-ci.mdx index 7b358b61..661e51bd 100644 --- a/learn/developers/deploying-from-ci.mdx +++ b/learn/developers/deploying-from-ci.mdx @@ -50,14 +50,17 @@ 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, 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. +**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 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) does this whole section in one -command: the role, the user, a policy pinned to that branch, and the `HARPER_CLI_TARGET` variable. This guide's -workflows deploy on version tags, which a branch pin cannot match, so they use the policy below, written by hand. ::: +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` and +`get_job` 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`: diff --git a/reference/cli/authentication.md b/reference/cli/authentication.md index 4e671d3c..df95cfac 100644 --- a/reference/cli/authentication.md +++ b/reference/cli/authentication.md @@ -261,16 +261,16 @@ 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). For a GitHub Actions workflow that deploys from a branch, [`harper deploy setup=true provider=github-actions`](./commands.md#github-actions-deploys-oidc) does it in one command: the policy, a deploy-only user for it, and the repository's `HARPER_CLI_TARGET` variable. +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) does it in one command: a deploy-only role and user, the policy, and the repository's `HARPER_CLI_TARGET` variable. ###### Using it in a workflow -**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: +**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` and `get_job` refuses anything else with a `403`. 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.** A password, a `HARPER_CLI_REFRESH_TOKEN`, a saved `harper login`, or `auth_username=` outranks the exchange — see below. +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: diff --git a/reference/cli/commands.md b/reference/cli/commands.md index c3edb5b8..e33f1b3c 100644 --- a/reference/cli/commands.md +++ b/reference/cli/commands.md @@ -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=` - 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) With `provider=github-actions`, set up [GitHub Actions deploys](#github-actions-deploys-oidc) instead. (Added in: v5.4.0) +- `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 @@ -257,12 +257,14 @@ harper deploy setup=true provider=github-actions project=my-app It needs no secret custody, and it does this: 1. Takes the GitHub repository from the checkout's `origin` remote, or `repo=/`, 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=` 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=`). A workflow that could never match, such as one whose push filter names only other branches and has no other trigger, or in which no job sets an environment, 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. +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=`). A workflow that could never match, such as one whose push filter names only other branches and has no other trigger, or in which no job sets an environment, 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: run setup from the repository 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 `-ci-deploy`, allowed only `deploy_component` and `get_job` (to wait for a rolling deploy), and the trust policy `github-actions-`. 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`. Without `gh`, or if the write fails, it prints the command to run and exits non-zero. -It only creates. Running it again changes nothing, 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-` and run setup again. +It only creates. Running it again leaves the cluster 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-` and run setup again. Because `add_oidc_trust` replaces a policy, setup checks again just before writing it, but a policy another super user creates or disables in that moment would still be overwritten. + +If setting the variable fails, 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 retries the variable. 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-` stops new runs, and `harper alter_user username=-ci-deploy active=false` also stops a token already issued, which lasts an hour. diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index eb158cb9..eec80f97 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -620,7 +620,7 @@ A CI runner can authenticate to Harper with **no stored credential**. It present The alternative is a `HARPER_CLI_REFRESH_TOKEN` secret: a 30-day credential, one per user, that expires on a schedule nobody tracks. A trust policy replaces it with a rule you configure once, and revoke with `drop_oidc_trust`. -The workflow needs `permissions: id-token: write` and the `HARPER_CLI_TARGET` variable, and no secret at all, since the target is not sensitive. Nothing in it names the policy: every `harper` command in the job authenticates through it by itself — see [Using it in a workflow](../cli/authentication.md#using-it-in-a-workflow). For a workflow that deploys from a branch, [`harper deploy setup=true provider=github-actions`](../cli/commands.md#github-actions-deploys-oidc) creates the policy and a deploy-only user for it in one command. [Deploying from a CI/CD Pipeline](/learn/developers/deploying-from-ci#let-the-workflow-authenticate-with-oidc) has a complete workflow, with the trust policy and deploy-only role it needs. See [CLI Authentication](../cli/authentication.md#workload-identity-oidc) for the client half and where the exchange sits in credential precedence. +The workflow needs `permissions: id-token: write` and the `HARPER_CLI_TARGET` variable, and no secret at all, since the target is not sensitive. Nothing in it names the policy: every `harper` command in the job authenticates through it by itself, and may do what the policy's user's role allows — see [Using it in a workflow](../cli/authentication.md#using-it-in-a-workflow). For a workflow that deploys from a branch, [`harper deploy setup=true provider=github-actions`](../cli/commands.md#github-actions-deploys-oidc) creates a deploy-only role and user and the policy in one command. [Deploying from a CI/CD Pipeline](/learn/developers/deploying-from-ci#let-the-workflow-authenticate-with-oidc) has a complete workflow, with the trust policy and deploy-only role it needs. See [CLI Authentication](../cli/authentication.md#workload-identity-oidc) for the client half and where the exchange sits in credential precedence. Policies live in the replicated `system.hdb_oidc_trust` table, so configuring one on any node applies cluster-wide. From 3bf4f3d8ad5bf6ef4363a77fdeb8bbcaa452e9ad Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Thu, 8 Oct 2026 21:55:36 -0400 Subject: [PATCH 04/11] Qualify the release note's workflow check and create-only claim, and say a rerun sets the variable again Co-Authored-By: Claude Opus 5.5 --- reference/cli/commands.md | 2 +- reference/operations-api/operations.md | 2 +- release-notes/v5-lincoln/5.4.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/cli/commands.md b/reference/cli/commands.md index e33f1b3c..c00027ee 100644 --- a/reference/cli/commands.md +++ b/reference/cli/commands.md @@ -262,7 +262,7 @@ It needs no secret custody, and it does this: 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`. Without `gh`, or if the write fails, it prints the command to run and exits non-zero. -It only creates. Running it again leaves the cluster 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-` and run setup again. Because `add_oidc_trust` replaces a policy, setup checks again just before writing it, but a policy another super user creates or disables in that moment would still be overwritten. +It only creates. Running it again leaves the cluster unchanged and sets `HARPER_CLI_TARGET` again to this target, 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-` and run setup again. Because `add_oidc_trust` replaces a policy, setup checks again just before writing it, but a policy another super user creates or disables in that moment would still be overwritten. If setting the variable fails, 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 retries the variable. diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index eec80f97..6c0d17fc 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -620,7 +620,7 @@ A CI runner can authenticate to Harper with **no stored credential**. It present The alternative is a `HARPER_CLI_REFRESH_TOKEN` secret: a 30-day credential, one per user, that expires on a schedule nobody tracks. A trust policy replaces it with a rule you configure once, and revoke with `drop_oidc_trust`. -The workflow needs `permissions: id-token: write` and the `HARPER_CLI_TARGET` variable, and no secret at all, since the target is not sensitive. Nothing in it names the policy: every `harper` command in the job authenticates through it by itself, and may do what the policy's user's role allows — see [Using it in a workflow](../cli/authentication.md#using-it-in-a-workflow). For a workflow that deploys from a branch, [`harper deploy setup=true provider=github-actions`](../cli/commands.md#github-actions-deploys-oidc) creates a deploy-only role and user and the policy in one command. [Deploying from a CI/CD Pipeline](/learn/developers/deploying-from-ci#let-the-workflow-authenticate-with-oidc) has a complete workflow, with the trust policy and deploy-only role it needs. See [CLI Authentication](../cli/authentication.md#workload-identity-oidc) for the client half and where the exchange sits in credential precedence. +The workflow needs `permissions: id-token: write` and the `HARPER_CLI_TARGET` variable, and no secret at all, since the target is not sensitive. Nothing in it names the policy: every `harper` command in the job authenticates through it by itself, and may do what the policy's user's role allows — see [Using it in a workflow](../cli/authentication.md#using-it-in-a-workflow). For a workflow that deploys from a branch, [`harper deploy setup=true provider=github-actions`](../cli/commands.md#github-actions-deploys-oidc) creates a deploy-only role and user and the policy, and sets the repository's `HARPER_CLI_TARGET` variable, in one command. [Deploying from a CI/CD Pipeline](/learn/developers/deploying-from-ci#let-the-workflow-authenticate-with-oidc) has a complete workflow, with the trust policy and deploy-only role it needs. See [CLI Authentication](../cli/authentication.md#workload-identity-oidc) for the client half and where the exchange sits in credential precedence. Policies live in the replicated `system.hdb_oidc_trust` table, so configuring one on any node applies cluster-wide. diff --git a/release-notes/v5-lincoln/5.4.md b/release-notes/v5-lincoln/5.4.md index 23e84479..6b89d11f 100644 --- a/release-notes/v5-lincoln/5.4.md +++ b/release-notes/v5-lincoln/5.4.md @@ -26,4 +26,4 @@ A `deploy_component` with `restart: true` or `restart: "rolling"` now checks its ### Setting Up GitHub Actions Deploys -`harper deploy setup=true provider=github-actions` sets a cluster up so a GitHub Actions workflow can deploy with its OIDC identity token instead of a stored credential. It creates a deploy-only role and user, a trust policy matching the workflow's repository, branch and environment, and the repository's `HARPER_CLI_TARGET` variable. It checks the policy against the workflow file before it writes, and reads it back after. It only creates, so running it again changes nothing, and a record that differs, or that someone deactivated, stops it before it writes. See [GitHub Actions deploys (OIDC)](/reference/v5/cli/commands#github-actions-deploys-oidc). +`harper deploy setup=true provider=github-actions` sets a cluster up so a GitHub Actions workflow can deploy with its OIDC identity token instead of a stored credential. It creates a deploy-only role and user, a trust policy matching the workflow's repository, branch and environment, and the repository's `HARPER_CLI_TARGET` variable. It checks the policy against the workflow file in the checkout before it writes, where it can, and reads it back after. It only creates: running it again leaves the cluster as it is, and a record that differs, or that someone deactivated or disabled, stops it before it writes. See [GitHub Actions deploys (OIDC)](/reference/v5/cli/commands#github-actions-deploys-oidc). From 3519a9a0cfae99efedd482bd194a24bec551ed52 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Thu, 8 Oct 2026 22:00:57 -0400 Subject: [PATCH 05/11] Note v5.4.0 in plain text where it sits mid-sentence, keeping the badge for headings Co-Authored-By: Claude Opus 5.5 --- learn/developers/deploying-from-ci.mdx | 2 +- reference/cli/authentication.md | 2 +- reference/operations-api/operations.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/learn/developers/deploying-from-ci.mdx b/learn/developers/deploying-from-ci.mdx index 661e51bd..796db113 100644 --- a/learn/developers/deploying-from-ci.mdx +++ b/learn/developers/deploying-from-ci.mdx @@ -54,7 +54,7 @@ A GitHub Actions job that declares `id-token: write` can ask GitHub for an ident :::tip - For a workflow that deploys when changes merge to a branch, [`harper deploy setup=true +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` and `get_job` only, not the `get_deployment` the role below adds. This guide's workflows deploy on version tags, which a diff --git a/reference/cli/authentication.md b/reference/cli/authentication.md index df95cfac..23a5a447 100644 --- a/reference/cli/authentication.md +++ b/reference/cli/authentication.md @@ -261,7 +261,7 @@ 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). For a GitHub Actions workflow that deploys from a branch, [`harper deploy setup=true provider=github-actions`](./commands.md#github-actions-deploys-oidc) does it in one command: a deploy-only role and user, the policy, and the repository's `HARPER_CLI_TARGET` variable. +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. ###### Using it in a workflow diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index 6c0d17fc..a94aa5e0 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -620,7 +620,7 @@ A CI runner can authenticate to Harper with **no stored credential**. It present The alternative is a `HARPER_CLI_REFRESH_TOKEN` secret: a 30-day credential, one per user, that expires on a schedule nobody tracks. A trust policy replaces it with a rule you configure once, and revoke with `drop_oidc_trust`. -The workflow needs `permissions: id-token: write` and the `HARPER_CLI_TARGET` variable, and no secret at all, since the target is not sensitive. Nothing in it names the policy: every `harper` command in the job authenticates through it by itself, and may do what the policy's user's role allows — see [Using it in a workflow](../cli/authentication.md#using-it-in-a-workflow). For a workflow that deploys from a branch, [`harper deploy setup=true provider=github-actions`](../cli/commands.md#github-actions-deploys-oidc) creates a deploy-only role and user and the policy, and sets the repository's `HARPER_CLI_TARGET` variable, in one command. [Deploying from a CI/CD Pipeline](/learn/developers/deploying-from-ci#let-the-workflow-authenticate-with-oidc) has a complete workflow, with the trust policy and deploy-only role it needs. See [CLI Authentication](../cli/authentication.md#workload-identity-oidc) for the client half and where the exchange sits in credential precedence. +The workflow needs `permissions: id-token: write` and the `HARPER_CLI_TARGET` variable, and no secret at all, since the target is not sensitive. Nothing in it names the policy: every `harper` command in the job authenticates through it by itself, and may do what the policy's user's role allows — see [Using it in a workflow](../cli/authentication.md#using-it-in-a-workflow). For a workflow that deploys from a branch, [`harper deploy setup=true provider=github-actions`](../cli/commands.md#github-actions-deploys-oidc) (v5.4.0) creates a deploy-only role and user and the policy, and sets the repository's `HARPER_CLI_TARGET` variable, in one command. [Deploying from a CI/CD Pipeline](/learn/developers/deploying-from-ci#let-the-workflow-authenticate-with-oidc) has a complete workflow, with the trust policy and deploy-only role it needs. See [CLI Authentication](../cli/authentication.md#workload-identity-oidc) for the client half and where the exchange sits in credential precedence. Policies live in the replicated `system.hdb_oidc_trust` table, so configuring one on any node applies cluster-wide. From 47fe9de86755c7855b88c77e6b4a875ce6f30725 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Mon, 12 Oct 2026 15:50:16 -0400 Subject: [PATCH 06/11] List registration_info in the CI deploy roles, and correct what it returns - commands.md: the role `harper deploy setup=true provider=github-actions` creates now also allows registration_info (HarperFast/harper#3127). - deploying-from-ci.mdx and operations.md: the hand-written deploy role and policy scope list it too, since `harper deploy` checks the version with it first and an allowlist that omits it logs a denial on every deploy. - operations.md: registration_info returns only `{ version, deprecated }` (since v5.0.0), not registration status, RAM or license details. Co-Authored-By: Claude Opus 5.5 --- learn/developers/deploying-from-ci.mdx | 9 +++++---- reference/cli/commands.md | 2 +- reference/operations-api/operations.md | 16 ++++++++++------ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/learn/developers/deploying-from-ci.mdx b/learn/developers/deploying-from-ci.mdx index 796db113..7ce7fe65 100644 --- a/learn/developers/deploying-from-ci.mdx +++ b/learn/developers/deploying-from-ci.mdx @@ -56,9 +56,9 @@ A GitHub Actions job that declares `id-token: write` can ask GitHub for an ident 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` and -`get_job` 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. +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. ::: @@ -73,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. diff --git a/reference/cli/commands.md b/reference/cli/commands.md index c00027ee..73875d25 100644 --- a/reference/cli/commands.md +++ b/reference/cli/commands.md @@ -258,7 +258,7 @@ It needs no secret custody, and it does this: 1. Takes the GitHub repository from the checkout's `origin` remote, or `repo=/`, 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=` 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=`). A workflow that could never match, such as one whose push filter names only other branches and has no other trigger, or in which no job sets an environment, 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: run setup from the repository 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 `-ci-deploy`, allowed only `deploy_component` and `get_job` (to wait for a rolling deploy), and the trust policy `github-actions-`. 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. +3. Creates a role and a user named `-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-`. 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`. Without `gh`, or if the write fails, it prints the command to run and exits non-zero. diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index a94aa5e0..c725bb96 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -674,7 +674,7 @@ harper add_oidc_trust \ harper list_oidc_trust ``` -`claims` is a JSON object, so quote it as a single shell argument — the CLI parses each value as JSON, which is what turns that string into the nested object the operation expects. `operations` works the same way if you scope the policy: `operations='["deploy_component","get_job","get_deployment"]'`. +`claims` is a JSON object, so quote it as a single shell argument — the CLI parses each value as JSON, which is what turns that string into the nested object the operation expects. `operations` works the same way if you scope the policy: `operations='["deploy_component","get_job","get_deployment","registration_info"]'`. `harper login` stores a token for that target, so step 2 needs no credentials of its own. If you would rather not store one, pass the target and credentials explicitly instead: @@ -697,7 +697,7 @@ The user the policy names is the privilege boundary: a matching run gets that us "issuer": "https://token.actions.githubusercontent.com", "audience": "https://my-instance.harperdb.io:9925/", "user": "ci-deploy", - "operations": ["deploy_component", "get_job", "get_deployment"], + "operations": ["deploy_component", "get_job", "get_deployment", "registration_info"], "claims": { "repository_id": "67890", "workflow_ref": "HarperFast/my-app/.github/workflows/deploy.yml@refs/heads/main", @@ -723,12 +723,12 @@ A deploy user does not need `super_user`. A role that lists the operations the p "operation": "add_role", "role": "ci_deploy", "permission": { - "operations": ["deploy_component", "get_job", "get_deployment"] + "operations": ["deploy_component", "get_job", "get_deployment", "registration_info"] } } ``` -`get_job` is what a pipeline polls to wait for a `"restart": "rolling"` deploy to reach every node, and `get_deployment` reads the deployment record. Both have to be listed: an `operations` allowlist, and a policy's `operations` scope, refuse every operation they don't name, including ones like `get_job` that any role may otherwise call. `restart_service` is not needed, since Harper starts the rolling restart's job itself. +`get_job` is what a pipeline polls to wait for a `"restart": "rolling"` deploy to reach every node, `get_deployment` reads the deployment record, and `registration_info` is the version check `harper deploy` makes before it uploads; without that one the deploy still works, but the node logs a refused operation on every run. Each has to be listed: an `operations` allowlist, and a policy's `operations` scope, refuse every operation they don't name, including ones like `get_job` that any role may otherwise call. `restart_service` is not needed, since Harper starts the rolling restart's job itself. Deploying is still administrative authority, since the deployed component runs inside the Harper process. A deploy that passes a literal registry or git `token` in `credentials` needs `super_user` on a node that holds secret custody, because Harper seals that token into the secrets store. Give a least-privilege role a `secret` reference instead. @@ -2097,7 +2097,7 @@ Operations for license management. | Operation | Description | Role Required | | ----------------------- | -------------------------------------------------- | ------------- | -| `registration_info` | Returns registration and version information | any | +| `registration_info` | Returns the Harper version | any | | `install_usage_license` | Installs a Harper usage license block | super_user | | `get_usage_licenses` | Returns all usage licenses with consumption counts | super_user | | `get_fingerprint` | _(Deprecated)_ Returns the machine fingerprint | super_user | @@ -2105,12 +2105,16 @@ Operations for license management. ### `registration_info` -Returns the instance registration status, version, RAM allocation, and license expiration. +Returns the instance's Harper version, and `deprecated: true`. It no longer returns registration details. `harper deploy` calls it to check the target's version before it uploads, so a role with an `operations` allowlist that runs `harper deploy` should list it. ```json { "operation": "registration_info" } ``` +```json +{ "version": "5.3.2", "deprecated": true } +``` + ### `install_usage_license` Installs a usage license block. A license is a JWT-like structure (`header.payload.signature`) signed by Harper. Multiple blocks may be installed; earliest blocks are consumed first. From 011b039adb101e9bec846470f7ea6237d60bc286 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Mon, 12 Oct 2026 15:56:10 -0400 Subject: [PATCH 07/11] Keep registration_info's other mentions consistent, and qualify the 5.4 note - operations.md: `get_fingerprint` no longer points at registration_info, which returns only the version. - operations-api-commands.md: registration_info gets the Harper version. - authentication.md: the example deploy role lists registration_info, like the role setup creates. - 5.4 release note: a rerun sets the variable again, and a policy another super user changes just before setup writes it is still overwritten. Co-Authored-By: Claude Opus 5.5 --- reference/cli/authentication.md | 2 +- reference/cli/operations-api-commands.md | 2 +- reference/operations-api/operations.md | 2 +- release-notes/v5-lincoln/5.4.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/reference/cli/authentication.md b/reference/cli/authentication.md index 23a5a447..8b56037c 100644 --- a/reference/cli/authentication.md +++ b/reference/cli/authentication.md @@ -265,7 +265,7 @@ Configure the instance to trust the workflow once with [`add_oidc_trust`](../ope ###### Using it in a workflow -**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` and `get_job` refuses anything else with a `403`. +**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`. 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. diff --git a/reference/cli/operations-api-commands.md b/reference/cli/operations-api-commands.md index 229a34d6..ab5a431a 100644 --- a/reference/cli/operations-api-commands.md +++ b/reference/cli/operations-api-commands.md @@ -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_fingerprint` | Get instance fingerprint | [Licensing](../operations-api/operations.md#registration--licensing) | v4.3.0 | | `set_license` | Set license key | [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 | diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md index c725bb96..e0dacb11 100644 --- a/reference/operations-api/operations.md +++ b/reference/operations-api/operations.md @@ -2165,7 +2165,7 @@ For modern equivalents, see [Components Overview](../components/overview.md). | Operation | Replaced By | | ---------------------- | ------------------------------------------------------------------- | | `install_node_modules` | Handled automatically by `deploy_component` and `restart` | -| `get_fingerprint` | Use `registration_info` | +| `get_fingerprint` | None: `registration_info` returns only the version | | `set_license` | Use `install_usage_license` | | `search_by_hash` | Use `search_by_id` | | `search_attribute` | Use `attribute` field in `search_by_value` / `search_by_conditions` | diff --git a/release-notes/v5-lincoln/5.4.md b/release-notes/v5-lincoln/5.4.md index 6b89d11f..9958b118 100644 --- a/release-notes/v5-lincoln/5.4.md +++ b/release-notes/v5-lincoln/5.4.md @@ -26,4 +26,4 @@ A `deploy_component` with `restart: true` or `restart: "rolling"` now checks its ### Setting Up GitHub Actions Deploys -`harper deploy setup=true provider=github-actions` sets a cluster up so a GitHub Actions workflow can deploy with its OIDC identity token instead of a stored credential. It creates a deploy-only role and user, a trust policy matching the workflow's repository, branch and environment, and the repository's `HARPER_CLI_TARGET` variable. It checks the policy against the workflow file in the checkout before it writes, where it can, and reads it back after. It only creates: running it again leaves the cluster as it is, and a record that differs, or that someone deactivated or disabled, stops it before it writes. See [GitHub Actions deploys (OIDC)](/reference/v5/cli/commands#github-actions-deploys-oidc). +`harper deploy setup=true provider=github-actions` sets a cluster up so a GitHub Actions workflow can deploy with its OIDC identity token instead of a stored credential. It creates a deploy-only role and user, a trust policy matching the workflow's repository, branch and environment, and the repository's `HARPER_CLI_TARGET` variable. It checks the policy against the workflow file in the checkout before it writes, where it can, and reads it back after. It only creates: running it again leaves the cluster as it is and sets the variable again, and a record that differs, or that someone deactivated or disabled, stops it before it writes. A policy another super user creates or disables in the moment before setup writes it is still overwritten. See [GitHub Actions deploys (OIDC)](/reference/v5/cli/commands#github-actions-deploys-oidc). From 71bd74acbfbcdcda7e645a1be2c642a3c8fa340d Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Mon, 12 Oct 2026 20:03:05 -0400 Subject: [PATCH 08/11] Say to pass workflow= when the workflow file isn't deploy.yaml Setup defaults to .github/workflows/deploy.yaml, create-harper's name, while the CI guide and the operations examples use deploy.yml. A missing default file is only reported as unverified, so setup could still pin deploy.yaml and leave a deploy.yml workflow's token unable to match. The reference now says so next to the command, with a workflow= example. Co-Authored-By: Claude Opus 5.5 --- reference/cli/commands.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/reference/cli/commands.md b/reference/cli/commands.md index 73875d25..b609581d 100644 --- a/reference/cli/commands.md +++ b/reference/cli/commands.md @@ -254,6 +254,12 @@ See [`deploy_component` credentials](../operations-api/operations.md#deploy-cred harper deploy setup=true provider=github-actions project=my-app ``` +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=/`, 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=` and spell `repo=` as GitHub does; setup says the spelling was not verified. From d74635048b23683aefdd200bf3320938d6146bcf Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Mon, 12 Oct 2026 20:14:26 -0400 Subject: [PATCH 09/11] Describe setup's create-only variable, the id-token check, and the checkout rule Follows HarperFast/harper#3127's review changes: setup leaves a HARPER_CLI_TARGET that already names another cluster, requires a job in the environment to grant `id-token: write`, and checks the local workflow only when the checkout is the repository the policy is for. Co-Authored-By: Claude Opus 5.5 --- reference/cli/commands.md | 8 ++++---- release-notes/v5-lincoln/5.4.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/reference/cli/commands.md b/reference/cli/commands.md index b609581d..84bca0ac 100644 --- a/reference/cli/commands.md +++ b/reference/cli/commands.md @@ -263,14 +263,14 @@ harper deploy setup=true provider=github-actions project=my-app workflow=.github It needs no secret custody, and it does this: 1. Takes the GitHub repository from the checkout's `origin` remote, or `repo=/`, 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=` 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=`). A workflow that could never match, such as one whose push filter names only other branches and has no other trigger, or in which no job sets an environment, 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: run setup from the repository with the workflow committed, or the policy it writes uses the defaults whether or not they match. +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's `origin` is another repository: 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 `-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-`. 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`. Without `gh`, or if the write fails, it prints the command to run and exits non-zero. +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 unchanged and sets `HARPER_CLI_TARGET` again to this target, 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-` and run setup again. Because `add_oidc_trust` replaces a policy, setup checks again just before writing it, but a policy another super user creates or disables in that moment would still be overwritten. +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-` and run setup again. Because `add_oidc_trust` replaces a policy, setup checks again just before writing it, but a policy another super user creates or disables in that moment would still be overwritten. -If setting the variable fails, 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 retries the variable. +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-` stops new runs, and `harper alter_user username=-ci-deploy active=false` also stops a token already issued, which lasts an hour. diff --git a/release-notes/v5-lincoln/5.4.md b/release-notes/v5-lincoln/5.4.md index 9958b118..ea17163f 100644 --- a/release-notes/v5-lincoln/5.4.md +++ b/release-notes/v5-lincoln/5.4.md @@ -26,4 +26,4 @@ A `deploy_component` with `restart: true` or `restart: "rolling"` now checks its ### Setting Up GitHub Actions Deploys -`harper deploy setup=true provider=github-actions` sets a cluster up so a GitHub Actions workflow can deploy with its OIDC identity token instead of a stored credential. It creates a deploy-only role and user, a trust policy matching the workflow's repository, branch and environment, and the repository's `HARPER_CLI_TARGET` variable. It checks the policy against the workflow file in the checkout before it writes, where it can, and reads it back after. It only creates: running it again leaves the cluster as it is and sets the variable again, and a record that differs, or that someone deactivated or disabled, stops it before it writes. A policy another super user creates or disables in the moment before setup writes it is still overwritten. See [GitHub Actions deploys (OIDC)](/reference/v5/cli/commands#github-actions-deploys-oidc). +`harper deploy setup=true provider=github-actions` sets a cluster up so a GitHub Actions workflow can deploy with its OIDC identity token instead of a stored credential. It creates a deploy-only role and user, a trust policy matching the workflow's repository, branch and environment, and the repository's `HARPER_CLI_TARGET` variable. It checks the policy against the workflow file in the repository's checkout before it writes, where it can, and reads it back after. It only creates: running it again leaves the cluster and the variable as they are, a record that differs, or that someone deactivated or disabled, stops it before it writes, and a `HARPER_CLI_TARGET` that names another cluster is left in place. A policy another super user creates or disables in the moment before setup writes it is still overwritten. See [GitHub Actions deploys (OIDC)](/reference/v5/cli/commands#github-actions-deploys-oidc). From 9f5007365e3d76279786262ab6bd8c058658cb25 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Mon, 12 Oct 2026 20:34:51 -0400 Subject: [PATCH 10/11] Say GITHUB_REPOSITORY, when set, names the checkout's repository Setup takes the checkout's repository from GITHUB_REPOSITORY before the origin remote, so the checkout rule now says both. Co-Authored-By: Claude Opus 5.5 --- reference/cli/commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/cli/commands.md b/reference/cli/commands.md index 84bca0ac..94919149 100644 --- a/reference/cli/commands.md +++ b/reference/cli/commands.md @@ -263,7 +263,7 @@ harper deploy setup=true provider=github-actions project=my-app workflow=.github It needs no secret custody, and it does this: 1. Takes the GitHub repository from the checkout's `origin` remote, or `repo=/`, 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=` 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's `origin` is another repository: 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. +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 `-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-`. 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. From fc3981e5420673085ecfdb9e06a26f6c52e6964f Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Tue, 13 Oct 2026 10:26:38 -0400 Subject: [PATCH 11/11] Say the setup race can only replace a policy created in the window Setup never writes a policy that already exists, so another super user's disable can't be overwritten; only a policy with the same id created in the moment before setup writes it can be (HarperFast/harper#3127). Co-Authored-By: Claude Opus 5.5 --- reference/cli/commands.md | 2 +- release-notes/v5-lincoln/5.4.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/cli/commands.md b/reference/cli/commands.md index 94919149..a803f1b7 100644 --- a/reference/cli/commands.md +++ b/reference/cli/commands.md @@ -268,7 +268,7 @@ It needs no secret custody, and it does this: 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-` and run setup again. Because `add_oidc_trust` replaces a policy, setup checks again just before writing it, but a policy another super user creates or disables in that moment would still be overwritten. +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-` 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. diff --git a/release-notes/v5-lincoln/5.4.md b/release-notes/v5-lincoln/5.4.md index ea17163f..cb076222 100644 --- a/release-notes/v5-lincoln/5.4.md +++ b/release-notes/v5-lincoln/5.4.md @@ -26,4 +26,4 @@ A `deploy_component` with `restart: true` or `restart: "rolling"` now checks its ### Setting Up GitHub Actions Deploys -`harper deploy setup=true provider=github-actions` sets a cluster up so a GitHub Actions workflow can deploy with its OIDC identity token instead of a stored credential. It creates a deploy-only role and user, a trust policy matching the workflow's repository, branch and environment, and the repository's `HARPER_CLI_TARGET` variable. It checks the policy against the workflow file in the repository's checkout before it writes, where it can, and reads it back after. It only creates: running it again leaves the cluster and the variable as they are, a record that differs, or that someone deactivated or disabled, stops it before it writes, and a `HARPER_CLI_TARGET` that names another cluster is left in place. A policy another super user creates or disables in the moment before setup writes it is still overwritten. See [GitHub Actions deploys (OIDC)](/reference/v5/cli/commands#github-actions-deploys-oidc). +`harper deploy setup=true provider=github-actions` sets a cluster up so a GitHub Actions workflow can deploy with its OIDC identity token instead of a stored credential. It creates a deploy-only role and user, a trust policy matching the workflow's repository, branch and environment, and the repository's `HARPER_CLI_TARGET` variable. It checks the policy against the workflow file in the repository's checkout before it writes, where it can, and reads it back after. It only creates: running it again leaves the cluster and the variable as they are, a record that differs, or that someone deactivated or disabled, stops it before it writes, and a `HARPER_CLI_TARGET` that names another cluster is left in place. A policy with the same id that another super user creates in the moment before setup writes it is still overwritten. See [GitHub Actions deploys (OIDC)](/reference/v5/cli/commands#github-actions-deploys-oidc).