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_targetruns 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 |