DocsFor Maintainers

Requesting a Signed Build

Ask OSSign to sign a tag or branch from a workflow in your own repository, wait for the review and build, and fetch the signed files.

What You Need

After your first signed release, we will add your project to our system as an approved trigger for the signing workflow. You can then use the OSSign actions to request signed builds automatically from your repository.

Normally, we will use something called Trusted Workflows, which allow the OSSign server to verify that a workflow run in your repository is authorized to request a signed build without needing a token.

If your project is set up as a trusted trigger, you do not need to store a token in your repository. The OSSign server will verify the workflow run using the GitHub OIDC token automatically.

If your project is not set up as a trusted trigger, you will need to use a client name and token to authenticate requests.

Using a Client Name and Token

We will send you a client name and a token. Add them as repository secrets in your own repository:

Secret Value
OSSIGN_CLIENT The client name, e.g. acme
OSSIGN_TOKEN The token. It is shown to us once; keep it secret.

Requests can also be started by us by submitting a ticket, if you would rather not automate it.

Trigger authentication

When triggering a workflow to request a signed build, the OSSign server needs to authenticate the request. This can be done in two ways:

Trusted Workflows

If your project is set up as a trusted trigger, the OSSign server will verify the workflow run using the GitHub OIDC token automatically. You use the following configuration:

jobs:
  request:
    permissions:
      # This is required to be able to create an ID token to authenticate the workflow run with OSSign
      id-token: write

- name: Request signing
  uses: OSSign/actions/workflow/dispatch@main
  with:
    dispatch_only: true

Using a Client Name and Token

If your project is not set up as a trusted trigger, you will need to provide a client name and token as repository secrets (OSSIGN_CLIENT and OSSIGN_TOKEN). You use the following configuration:

# This workflow step requests a signed build using the client name and token, and doesn't require id-token: write permissions

- name: Request signing
  uses: OSSign/actions/workflow/dispatch@main
  with:
    dispatch_only: true
    username: ${{ secrets.OSSIGN_CLIENT }}
    token: ${{ secrets.OSSIGN_TOKEN }}

Triggering a workflow

A review by a person can take a while. Choose how your workflow waits:

Option How When to use it
Wait in the job The default shown above (dispatch_only: false) Free runners, or when most of your releases are approved automatically with small changes. This is not recommended
Dispatch, then check on a schedule dispatch_only: true, plus a scheduled workflow that runs a single check every X minutes Paid runners: each check takes a few seconds every 20 to 30 minutes
Dispatch, then fetch by hand dispatch_only: true You download the files from the release in your OSSign repository yourself

Wait in the job

The simplest setup requests a signing on every version tag and waits for the result:

name: Request signing

on:
  push:
    tags: ["v*"]

jobs:
  request:
    runs-on: ubuntu-latest
    steps:
      - name: Request signing and wait
        id: ossign
        uses: OSSign/actions/workflow/dispatch@main

      - name: Show signed artifacts
        run: echo '${{ steps.ossign.outputs.signed_artifacts }}' | jq .

The action dispatches the tag that triggered the run, then polls until the signing workflow has finished, for up to 24 hours. The job fails if the request is rejected or the build fails.

We usually do not recommend this option, since it will block your workflow until the signing is complete, which can take a long time if a human review is required. It can also incur a lot of costs if you are using paid runners for the duration of the wait.

Dispatch only, check later

To dispatch without waiting for a response, run the following workflow step:

      - name: Dispatch to OSSign
        id: dispatch
        uses: OSSign/actions/workflow/dispatch@main
        with:
          dispatch_only: true
      # Later, e.g. in a scheduled workflow, with the id from the dispatch step
      - name: Check if signing is finished
        id: check
        uses: OSSign/actions/workflow/dispatch@main
        with:
          single_check: ${{ steps.dispatch.outputs.workflow_id }}

The example workflow repository has a complete scheduled-check setup. It uses a Github environment to add a wait timer before running a workflow that checks if the signing is finished to minimize the time that a runner is active and reduce costs.

Fetching the Signed Files

When signing is finished, signed_artifacts holds the files of the release:

[
  {
    "id": 123456789,
    "name": "tool.exe",
    "url": "https://api.github.com/repos/OSSign/acme--tool/releases/assets/123456789",
    "browser_download_url": "https://github.com/OSSign/acme--tool/releases/download/v1.2.3-1/tool.exe"
  }
]

Download them with curl -LO on each browser_download_url and publish them with your own release.

Without a Token: Trusted Workflow

Instead of a token, we can trust a workflow in your repository, for example tags matching refs/tags/v*. The workflow then sends its own GitHub OIDC token with every call. There is nothing to store and nothing to rotate.

The job needs permissions: id-token: write. Keep build steps you don't fully trust out of that job.

Automatically

The dispatch step will automatically handle tokens for you, so you don't need to manually request an ID token or include it in the workflow step.

jobs:
  dispatch:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
    steps:
      - name: Dispatch to OSSign
        id: dispatch
        uses: OSSign/actions/workflow/dispatch@main
        with:
          dispatch_only: true

Manually

  • The token's audience is the server's dispatch audience, which we give you with the client name. It differs from the signing audience.
  • Mint a fresh token for every call. Tokens expire within minutes, and a review takes longer.
  • pull_request_target runs are always refused.
  request-with-oidc:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    env:
      OSSIGN_SERVER: https://sign.example.com          # from us
      OSSIGN_CLIENT: acme                              # from us
      OSSIGN_AUDIENCE: https://sign.example.com/dispatch
    steps:
      - name: Dispatch and wait
        run: |
          token() {
            curl -sS --fail -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
              "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=$OSSIGN_AUDIENCE" | jq -r .value
          }
          ID=$(curl -sS --fail -X POST \
            -H "Authorization: Bearer $(token)" -H "Content-Type: application/json" \
            -d "{\"ref\": \"${GITHUB_REF_NAME}\"}" \
            "$OSSIGN_SERVER/api/v1/dispatch/$OSSIGN_CLIENT" | jq -r .id)
          for i in $(seq 1 2880); do        # 15 s x 2880 = 12 h
            STATUS=$(curl -sS -H "Authorization: Bearer $(token)" "$OSSIGN_SERVER/api/v1/dispatch/$OSSIGN_CLIENT/$ID")
            case "$(echo "$STATUS" | jq -r .status)" in
              success)  echo "$STATUS" | jq -r '.assets[] | "\(.name)  \(.browser_download_url)"'; exit 0 ;;
              rejected) echo "::error::Rejected: $(echo "$STATUS" | jq -r '.review.reason // .error')"; exit 1 ;;
              failure|error) echo "::error::Failed: $(echo "$STATUS" | jq -r .error)"; exit 1 ;;
              *) sleep 15 ;;
            esac
          done
          exit 1

Using the API Directly

From another CI system or a script, the action's two calls are:

# Request a signing. Returns {"id": ...}
curl -X POST -H "Authorization: Bearer $OSSIGN_TOKEN" -H "Content-Type: application/json" \
  -d '{"ref": "v1.2.3"}' https://sign.example.com/api/v1/dispatch/<client>

# Status, review state and, once finished, the release assets
curl -H "Authorization: Bearer $OSSIGN_TOKEN" https://sign.example.com/api/v1/dispatch/<client>/<id>
status Meaning
success Signed and released; assets lists the files
rejected The review rejected the request; review.reason says why
failure, error The signing workflow failed
anything else Still in review or building; check again later
Edit This Page on GitHub Maintained by the OSSign team, Uppsala, Sweden