Skip to content

Your OSSign Workflow

Lars Scheibling edited this page Apr 26, 2026 · 5 revisions

Welcome

So, you've put in an application to OSSign, it's passed the review, and now it's time to set up a signing workflow.

To lighten the load we've had on applications, we've decided to change the process for getting started with code signing slightly so that we don't have to spend as much time onboarding each new signee.

In these wiki pages, you will find more information on what you have to do to get started.

Your Repository

You will have received a link for a repository, usually in the format github.com/OSSign/YourGithubUser-YourGithubRepositoryName. In this repository is a starter workflow file which will be handling the signing of your application. This is the part we will need to set up at this point.

The signing workflow

Step 1: Initiation

The automated signing workflow starts with initiating a signature, which can either be made automatically from your repository with one of our workflows or manually via a ticket to our support (which you've been emailing with).

The automatic workflow has three options:

  1. Keep your workflow running until the file has been signed. This is usually not recommended, because this can take a while to approve and sign, especially in the beginning.

  2. Use the waiting loop workflow. This will send the signing request, and then periodically run a check until signing has been completed. This is more resource-efficient and only takes a couple of seconds every 20-30 minutes until done.

  3. Just start the signing via your workflow, and then fetch the released files manually once it completes.

You can find an example of how this workflow works by visiting our example repository here: github.com/ossign/example-workflow

Step 2: The signing workflow

This part happens outside of your repository in the companion repo that we set up for you, under the OSSign organization. More information below.

Step 3: Fetching the binaries

Once our workflow has finished, the signed files will be published in a release on our end. We can optionally implement other steps for delivery on our side through the workflow you'll be setting up, that is entirely up to you!

For the automated workflow, once finished, it will return a json with a list of binaries and the URLs where to fetch them if you want to automate that part.

The signing

This is the part where the magic happens. You can find the entire sample workflow in this file

1. Inputs

These inputs can be made from your end when starting the workflow. Currently, this is only the branch or tag reference for which the build should happen, for example "main" or "v1.0.0". The Release name can be set to anything, you can use the same name here as the branch if you use tags, otherwise you can use the run ID.

What do I need to do? You can leave this section unchanged.

name: Build and Sign

on:
  workflow_dispatch:
    inputs:

      source_branch:
        description: 'The source branch or ref to build from'
        default: 'main'
        required: true

      release_name:
        description: 'The name for the release'
        required: true

2. Repository name and path

This is just the repository name and path, these should already be set to your repository. If not, and your repository name is "LinusTorvalds/linux", you set the source repository to LinusTorvalds/linux and the source repository name to linux

What do I need to do? Ensure the values are correct for your project

env:
  SOURCE_REPOSITORY: 'xyz/xyz'
  SOURCE_REPOSITORY_NAME: 'this-project'

3. The build

This part is where you insert the build of your application or installer. The final upload artifacts step needs to contain all executables that have been built.

This part should not include any signing. See the comments below for more information.

What do I need to do? Add your build workflow here

jobs:
  build:
    # Change this to the OS you need to build your application
    runs-on: ubuntu-latest
    # runs-on: windows-latest
    
    # This permission is required to upload artifacts.
    # Do not change this or add additional permissions.
    permissions:
      contents: write
      
    steps:
      # You can leave this step as-is unless you need to increase the fetch-depth or add recursion
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          repository: ${{ env.SOURCE_REPOSITORY }}
          ref: ${{ github.event.inputs.source_branch }}
          fetch-depth: 1

      ########
      ## INSERT BUILD HERE
      ########

      # Using globs, include all executables and artifacts you need to be signed here
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: files-to-sign
          path: |
            path/*
            files/*

4. The signing

This is the step where the signing happens. This step requires manual approval, which can take a while especially with the first few signatures for your project.

See the comments below for more information. This step should not run any custom code, third-party actions, or scripts from your repository.

sign:
    # This environment contains the signing secret, so this needs to stay in place
    environment: OSSign

    # The build step needs to be completed before this can start
    needs: build

    # This can always run on ubuntu-latest
    runs-on: ubuntu-latest

    # Leave these permissions as-is
    permissions:
      contents: write
    steps:
      # Fetches the artifacts from the previous step
      - name: Download artifacts
        uses: actions/download-artifact@v5
        with:
          name: files-to-sign

      # Performs the actual signing
      # This step may need to be repeated if you have both .exe and .msi files in your repository, once per filetype
      - uses: ossign/ossign@main
        with:
          # Leave these parts as-is
          config: ${{ secrets.OSSIGN_CONFIG }}
          token: ${{ secrets.GITHUB_TOKEN }}
      
          # Add one of inputFile or inputFiles
          # Sign a single file
          inputFile: path/to/file.exe
      
          # Sign multiple files using a glob pattern
          inputFiles: |
            path/to/*.exe
            path/to/another/**/*.exe
      
          # Type of signature. Can be "pecoff", "msi" or "powershell"
          # Pecoff is the "regular" windows application signature
          # MSI is for MSI files
          signatureType: pecoff


      # The final release of the signed binaries
      - name: Release
        uses: softprops/action-gh-release@v2
        with:
          files: |
            dist/*.msi
            dist/*.exe
          name: ${{ github.event.inputs.release_name }}
          tag_name: ${{ github.run_id }}
          body: |
            <img src="https://github.com/ossign.png" alt="OSSign Logo" width="100" height="100" />
            # Signed Release ${{ github.event.inputs.release_name }}
            
            These are the distributable files for the signed release of ${{ env.SOURCE_REPOSITORY_NAME }} ${{ github.event.inputs.release_name }}

            The source code was fetched from https://github.com/${{ env.SOURCE_REPOSITORY }}@${{ github.event.inputs.source_branch }}

Testing the workflow

You can fork this repository and test all changes locally. For this, you can use the following OSSIGN_CONFIG variable contents.

The certificate displayed below is only an example certificate, and holds no trust

{
    "tokenType": "certificate",
    "certificate": {
        "certificate": "-----BEGIN CERTIFICATE-----\nMIICEjCCAXugAwIBAgIUchRukHCbf3cZq37F8CJ0NE+82EowDQYJKoZIhvcNAQEL\nBQAwGzEZMBcGA1UEAwwQVGVzdCBDZXJ0aWZpY2F0ZTAeFw0yNjAxMjgyMzI0NTla\nFw0zNjAxMjYyMzI0NTlaMBsxGTAXBgNVBAMMEFRlc3QgQ2VydGlmaWNhdGUwgZ8w\nDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAL2c6EfIfA8Ui3WpuMqWcMJ0GleJp3OX\nkdTHbdmH7fiSi8QuNwPlcy14zbj6UebsHwegR+7QlHCmJG9WjP0YYLx1N4khnMj7\nZ/qI+3iEmk9afjG+L2Ihb4/tmifYQloBIBBOBc7h1kuEzXnmpRfsZp6Qiil4SNmn\njtsSFhrZGLI3AgMBAAGjUzBRMB0GA1UdDgQWBBRtz3rXq9czSkGkDfQap8kGplQb\nijAfBgNVHSMEGDAWgBRtz3rXq9czSkGkDfQap8kGplQbijAPBgNVHRMBAf8EBTAD\nAQH/MA0GCSqGSIb3DQEBCwUAA4GBALc5XQVoHKb4j7cUzUkxqS4PZZNEqlzZ+g5D\n/BdKXrovKpkd5wG1Y8ci5NXj5V2tA9pHm+SLAGXJWdPUdu3irtLFzZXMcr9nQPwG\nTBmPja9tBq1fVTya8RRZA5KZ65N1g5lasCksnbyPldgU1M/V5yORwdK0siZM4Fjs\nJEfB6p+a\n-----END CERTIFICATE-----\n",
        "privateKey": "-----BEGIN PRIVATE KEY-----\nMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAL2c6EfIfA8Ui3Wp\nuMqWcMJ0GleJp3OXkdTHbdmH7fiSi8QuNwPlcy14zbj6UebsHwegR+7QlHCmJG9W\njP0YYLx1N4khnMj7Z/qI+3iEmk9afjG+L2Ihb4/tmifYQloBIBBOBc7h1kuEzXnm\npRfsZp6Qiil4SNmnjtsSFhrZGLI3AgMBAAECgYARln9ZQTh4saAp/t88M24sK1bS\nLduRdkq5oPIIjno9Z2J9hQfnXZ4sZps2gEmekOJj87MYbNKIDHEuvql/RIaca5TD\nNpAigNCGnCDcT8BV3cuaqa9LK7IDFnswIEMn1q4ADJnM3QyKShau9myJewH8Tz4Q\nHzhxlvDNtKFwX0WveQJBAOnsH+yuBbN/KWp3RJJWn966Pju4taOohr1oLvKaE2Ii\nZxH4+92AKXaFiNJwrTk/Gq4qV/nXhe4Ar7VlDRr5A9UCQQDPgjUkc8AVHvSM1J5h\nVEBtmI5tnq+8Avh9tk4nwviCh6HMKhKc2Y0JUBv8mdO0Zel9y3EWUUmk0dEDXZZT\nHT/bAkAmFL+ZuzbIYuIuJ95s6Fc8Xht1g3tmei/9M7G44uZW6nzXCy6Nf6jAV7rP\nb3JzyFcilVgfHzv5Y/k20Y2Rn4pFAkAaPF43s6LPiNBmleNIbvyOXsFzPqL9ZGrC\nijAres0sw7VDOPaNejwIt2Yyc8h+gHwa+YPczH5BJn4ErOp6q7INAkEAxfcU5KIE\nS0EtN6raGsZqfjbiNzGTeVVpcipurc1Hpbe3hoT59RokTNGc/WXXbEpyzyQvJLLR\nk3b5/iMJr7pZiQ==\n-----END PRIVATE KEY-----\n"
    },
    "timestampUrl": "http://timestamp.globalsign.com/tsa/advanced",
    "msTimestampUrl": "http://timestamp.microsoft.com/tsa"
}

Done?

When you are done, open a pull request to the ossign repository with your changes and notify us via your application that you have finished your work.

The goal here is to have a working signature workflow that follows the specifications above. We will go through it, approve and then build the first signed release and send you your production credentials.

Questions? Feel free to reach out via your application ticket

The trigger workflow

The trigger workflow is optional, and submits a build/signing request to OSSign for your binaries. This request will then be reviewed, approved, signed, and the signed binaries will be published for you to fetch them.

The trigger workflow will be set up on your end, and consists of three main steps. When approved, you will receive production credentials for the signing workflow.

  1. Trigger/Dispatch
  2. Wait for completion
  3. Fetch the binaries

You can find a complete example workflow in this repository github.com/ossign/example-workflow

1. Trigger/dispatch

This step triggers the signing workflow on our end, which will automatically build the binary to start with, and then wait for approval to run the signature step.

You will find more information about the last option (dispatch_only) in the next section.

This workflow uses our dispatch action with the following options:

# Dispatch a request to the OSSign repo for starting the signature workflow
- name: Dispatch to OSSign
  id: dispatch
  uses: ossign/actions/workflow/dispatch@main
  with:

  # You receive the username and token from OSSign after your application has been approved
  # Keep this token secret
  username: ${{ secrets.OSSIGN_USER }}
  token: ${{ secrets.OSSIGN_TOKEN }}

  # If you want to keep the workflow running until you have the signed file, you can change this to false
  # Bear in mind that it might sometimes take a while before the dispatch is approved, and this will keep the workflow running and billing if you are using a non-free runner
  dispatch_only: true

And that's it, once completed you will then have successfully dispatched the signing workflow. The output from the steps contains a run_id which can be used to track the progress of the signing.

2. Waiting

There are three options for the waiting period:

2.1 dispatch_only: false

This means that the workflow will keep running and waiting for the signature to be approved and completed. Since this step may take some time this is usually not recommended, if you want an automatic approcah you sould choose option 2 or 3.

2.2 dispatch_only: true + waiting loop.

This loop requires some more configuration, you can find the entire set of instructions here: https://github.com/OSSign/example-workflow/blob/ossign/README.md. This step runs a collection workflow on an interval to check if the signing has completed, if not it will re-start after a period of time. When the signing is done, you can then fetch the files automatically.

2.3 dispatch_only: true+ manual retrieval.

With this version you can start the workflow with the above dispatch action, and then wait for the workflow to finish. When finished, you can fetch the binaries via a workflow_dispatch-activated action on your end, or manually from our releases.

3. Fetching the signed files

When using the automatic retrieval, you can use the single_check parameter of the dispatch action to check if the workflow has finished. For this, you need the workflow ID that was output from the previous step along with your credentials. With option 1 on the waiting (dispatch_only: false), this happens in the same step as the initial dispatch step.

- name: Check if signing is finished
  id: check
  uses: ossign/actions/workflow/dispatch@main
  with:
    username: ${{ secrets.OSSIGN_USER }}
    token: ${{ secrets.OSSIGN_TOKEN }}
    
    # This is the output of the dispatch_only action that started the workflow
    single_check: ${{ steps.dispatch.outputs.workflow_id }}

When finished, this YAML will return a json object containing the signed files that were published to the signing repository in the steps.check.outputs.signed_artifacts variable

This is an example of the returned JSON object:

[
        {
            "id": 123456789,
            "name": "signed-file-1.exe",
            "url": "https://api.github.com/repos/abc/def/releases/assets/123456789",
            "browser_download_url": "https://github.com/abc/def/releases/download/17502283799/signed-file-1.exe"
        },
        {
            "id": 987654321,
            "name": "signed-installer-1.msi",
            "url": "https://api.github.com/repos/abc/def/releases/assets/987654321",
            "browser_download_url": "https://github.com/abc/def/releases/download/17502283799/signed-installer-1.msi"
        }
]

You can then use this object to fetch the signed files and publish them to your releases.