Skip to content
Connect an agent

Hosted diagrams and plans

Trusted publishers

A trusted publisher lets a GitHub Actions workflow act for your organisation with no API key in the repository. You name the repository and its workflow file once in the portal; each run asks GitHub for a signed OpenID Connect token, swaps it for a Plotwork token that lives 15 minutes, and reports drift or publishes with it. It works the way trusted publishing works on npm and PyPI.

For
  • GitHub Actions

TL;DR

QuestionAnswer
Which plan?Pro and Enterprise, the plans with the drift dashboard.
Who sets it up?The organisation's owner or an admin, under Keys and CI, Trusted publishers.
What do I name?The repository (owner/name), the workflow file (diagrams.yml), and optionally a deployment environment and a branch.
What goes in the workflow?permissions: id-token: write, and the Action step with no PLOTWORK_API_KEY.
What can a run do?What you choose: Publish (diagrams to the library) and Drift (reports to the Drift tab). Never read the audit log.
How long does the token live?15 minutes. The Action asks for a new one when a run outlives it.
What does Plotwork store?The entry, and a SHA-256 of each token it gave. Never a token, and no secret of yours.
Can someone take over the repository name?No. GitHub's numeric ids for the repository and its owner are pinned, so a repository deleted and made again under the same name is refused.
Do I need a GitHub App?No.

1. Add the trusted publisher

  1. In the portal, open the organisation and choose Keys and CI. The address /orgs/<handle>/ci opens it too.
  2. Under Trusted publishers, choose Add trusted publisher.
  3. Fill in the repository, such as acme/platform, and the workflow file name, such as diagrams.yml. The file name is the one in .github/workflows, with no folder.
  4. Optionally name a deployment environment (only jobs that run in it may sign in) and a branch (main, or a ref pattern such as refs/tags/v*). Leave them blank for any.
  5. Choose what its runs can do, and give it a name you will recognise in the usage, the call history and the audit log.

Plotwork asks GitHub for the ids of the repository and its owner when you save. A private repository's id is not public, so it is pinned by the first run instead, and the entry says so until then.

2. Use it in the workflow

Choose Workflow beside the entry to copy a file made for it. It looks like this:

yaml
name: diagrams
on:  pull_request:  push:    branches: [main]
permissions:  contents: read  id-token: write
jobs:  diagrams:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v7        with:          persist-credentials: false      - uses: plotwork-official/plotwork-action@86ae8278fa771a422d9df60185182f14b8be4db9 # v1.1.0        with:          command: generate          mode: check          report-drift: true

There is no env: and no secret. When the Action needs the API and finds no PLOTWORK_API_KEY, it asks GitHub for a token with the API's address as the audience, swaps it, and goes on. A key in PLOTWORK_API_KEY still works, and wins when both are there.

From the release after v1.2.0-beta.1, the output signed-in-as holds the organisation and publisher the run signed in as, such as {"organization":"acme","publisher":"Platform CI"}, and is empty when the run used a key or did not sign in. drift-url holds the Drift tab the run was sent to. A later step can check either.

Signing in by itself needs a release of the Action newer than v1.1.0. With v1.1.0, add this step before the Action's, and it passes the Plotwork token on as PLOTWORK_API_KEY:

yaml
      - name: Sign in to Plotwork as a trusted publisher        run: |          id_token=$(curl -sS -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://api.plotwork.dev" | jq -r .value)          echo "::add-mask::$id_token"          token=$(curl -sS -X POST https://api.plotwork.dev/oidc/token -H 'content-type: application/json' -d "{\"token\":\"$id_token\"}" | jq -r .token)          echo "::add-mask::$token"          echo "PLOTWORK_API_KEY=$token" >> "$GITHUB_ENV"

3. What a run can do

You choseThe run can
PublishPublish generated diagrams to the organisation's library, read the library and the style rules, and report drift
DriftReport drift runs to the Drift tab, and read the style rules

A run counts as the trusted publisher everywhere: its calls spend the owner's daily allowance and show in the usage and the call history under its name, a diagram it publishes names it as the publisher, and every policy of the organisation applies, including the networks keys may come from, the daily limits, secret scanning, classification and approval.

4. What is checked

Each token is checked against GitHub's published signing keys, and refused unless all of this holds:

CheckMust be
SignatureRS256, by a key GitHub publishes
Issuerhttps://token.actions.githubusercontent.com
AudienceThe API's address, such as https://api.plotwork.dev
TimesNot expired, already valid, issued in the last ten minutes
RepositoryThe entry's, by name and by GitHub's pinned ids
WorkflowThe entry's file, as the run's workflow_ref names it
EnvironmentThe entry's, when it names one
BranchThe entry's pattern, when it names one
ReuseA token is swapped once

The organisation's audit log records each sign-in as orgs.trusted_publisher.exchange, and a refused one as orgs.trusted_publisher.refused with the reason, at most once an hour for each entry.

5. Troubleshooting

The run saysWhy, and what to do
this job cannot ask GitHub for an ID tokenThe job has no id-token: write. Add it under permissions. A pull request from a fork never gets it; such runs check without reporting.
oidc.no_trusted_publisherNo entry trusts this run. Check the repository, the workflow file name, the environment and the branch. The audit log's orgs.trusted_publisher.refused event names which did not match. A pull request's ref is refs/pull/<n>/merge, so an entry limited to main refuses pull requests.
oidc.token_invalidThe token failed a check: usually an audience other than the API's address, as when PLOTWORK_API_URL points at another Plotwork service. Ask for a new token for the right address.
oidc.token_reusedThat token was swapped already. Ask GitHub for a new one.
oidc.organization_requiredMore than one organisation trusts this run. Set rules-org, or PLOTWORK_ORG, to the one to sign in to.
plan.trusted_publishers_not_includedThe owner's plan has no trusted publishers. The entries stay and work again after an upgrade.
auth.token_expiredThe run outlived its 15 minutes. The Action swaps again before sending results; a step of your own should do the same.
auth.key_network_refusedThe organisation accepts its keys only from some networks, and GitHub's hosted runners are not in them. Use a self-hosted runner in an allowed network, or add the range.
oidc.unavailableGitHub's keys could not be read just now. Run the job again.

See also

PageWhy
Diagrams in CIThe Action's commands and inputs
The drift dashboardWhat a drift report sends and where it shows
OrganisationsRoles, and organisation keys
Audit logThe sign-in and refusal events
Plans and billingWhat Pro and Enterprise include