Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

GitHub Actions

A GitHub Actions run can ask GitHub for a short-lived token that says which repository, branch, and workflow it is. A service principal can trust that token. The run trades it for a platform token, and GitHub stores no platform secret at all.

In the console, open IAM → App registrations → New application.

  • Kind: Service principal (API access)
  • Name: something that says where it runs, for example shop-github

The console shows a client secret once. A GitHub workflow does not need it.

Open the principal. Under Access, add Editor with the scope set to the one service the workflow deploys. Click Save access.

That is all the workflow can do. It cannot deploy another service, change the directory, or rename the tenant.

Under Federated sign-in, keep the GitHub preset:

FieldExample
Repositoryacme/shop
Runs onBranch
Branchmain

The console shows the subject it will trust, repo:acme/shop:ref:refs/heads/main. Click Trust.

The subject must match exactly. There are no wildcards. A run on another branch, a pull request, or a fork gets a different subject and is refused. To deploy from a GitHub environment instead, pick Environment. The subject is then repo:acme/shop:environment:production, and the environment’s protection rules apply.

The audience defaults to https://api.m12p.com, which is what the CLI asks GitHub for.

In GitHub, open Settings → Secrets and variables → Actions → Variables. None of these is secret:

VariableValue
PLATFORM_TENANTThe tenant id, tnt_...
PLATFORM_CLIENT_IDThe principal’s client id, client_...
PLATFORM_SERVICEThe service id, service_...
permissions:
contents: read
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# ... build and push the images ...
- name: Deploy
env:
PLATFORM_REST: https://api.m12p.com
PLATFORM_TENANT: ${{ vars.PLATFORM_TENANT }}
PLATFORM_CLIENT_ID: ${{ vars.PLATFORM_CLIENT_ID }}
PLATFORM_FEDERATION: github
run: |
base=https://developers.m12p.com/downloads/cli
curl -fsSLO "$base/platform-linux-amd64" && curl -fsSLO "$base/SHA256SUMS"
grep ' platform-linux-amd64$' SHA256SUMS | sha256sum -c -
chmod +x platform-linux-amd64
./platform-linux-amd64 service deploy "${{ vars.PLATFORM_SERVICE }}" \
--compose docker-compose.paas.yml --wait

id-token: write lets the job ask GitHub for its token. It does not give the job write access to the repository.

--compose replaces the Compose file saved on the service with the one in the repository, then deploys it. --wait follows the deployment. If it fails, the step prints why and fails too.

The platform takes a GitHub token only when all of this holds:

  • The issuer is https://token.actions.githubusercontent.com, which the platform trusts.
  • GitHub’s signature is valid. The keys come from GitHub over HTTPS.
  • The token has not expired, allowing one minute of clock difference.
  • The audience and the subject equal the federated credential’s.
  • The client id is a service principal of that tenant.

Each exchange is written to the tenant’s audit log as iam.token.federated, with the repository, ref, commit, workflow, run, and the GitHub user who started it. The token itself is never stored.

If the repository deployed through a deploy hook before:

  1. Push the workflow above and check that a run deploys.
  2. Delete the hook secret from the repository, for example PAAS_WEBHOOK_URL.
  3. Turn the hook off: the service’s Deployments tab, Turn off.