This guide explains how to enable CLA checking on repositories in the NetFoundry, OpenZiti, or OpenZiti-Test-Kitchen organizations.
- Contributors sign the CLA once by commenting on any PR
- Their signature is stored in a versioned JSON file (e.g.,
v1.1/cla.json) in this repo - All future PRs from that contributor automatically pass
- Organization members/owners are automatically skipped (covered by employment agreements)
Once your org is set up (see below), enabling CLA on a repo is just:
mkdir -p .github/workflows
curl -so .github/workflows/cla.yml https://raw.githubusercontent.com/netfoundry/cla/main/workflow-template/cla.ymlThat's it. The workflow calls the reusable workflow in this repo, so all logic is centralized.
A GitHub App named "NetFoundry CLA" (or similar) with:
- Permissions: Contents (read and write)
- Installation: Installed on all three organizations
Each organization needs these secrets:
| Secret | Description |
|---|---|
CLA_APP_ID |
The GitHub App's numeric ID |
CLA_APP_PRIVATE_KEY |
The GitHub App's private key (PEM format) |
To enable CLA checks for a new organization (e.g., openziti, openziti-test-kitchen):
- Go to the GitHub App settings: https://github.com/organizations/netfoundry/settings/apps
- Click on the CLA app
- Click "Install App" in the left sidebar
- Select the organization to install on
- Choose repository access:
- "All repositories" (recommended) - automatically covers new repos
- Or select specific repositories
- Go to Organization Settings → Secrets and variables → Actions
- Click "New organization secret"
- Add
CLA_APP_ID:- Name:
CLA_APP_ID - Value: (the App ID from the GitHub App settings page)
- Repository access: "All repositories" or select specific ones
- Name:
- Add
CLA_APP_PRIVATE_KEY:- Name:
CLA_APP_PRIVATE_KEY - Value: (the private key PEM content, including BEGIN/END lines)
- Repository access: Same as above
- Name:
This is all you need to do per repo - just copy one file:
workflow-template/cla.yml → .github/workflows/cla.yml
The workflow is minimal (just calls the reusable workflow in this repo). All logic lives in netfoundry/cla, so updates happen in one place:
name: "CLA Check"
on:
issue_comment:
types: [created]
pull_request_target:
types: [opened, closed, synchronize, labeled]
permissions:
actions: write
contents: read
pull-requests: write
statuses: write
jobs:
cla:
uses: netfoundry/cla/.github/workflows/cla-workflow.yml@main
secrets:
CLA_APP_ID: ${{ secrets.CLA_APP_ID }}
CLA_APP_PRIVATE_KEY: ${{ secrets.CLA_APP_PRIVATE_KEY }}Using gh CLI to add to multiple repos:
for repo in repo1 repo2 repo3; do
gh repo clone "openziti/$repo" "/tmp/$repo"
mkdir -p "/tmp/$repo/.github/workflows"
cp workflow-template/cla.yml "/tmp/$repo/.github/workflows/cla.yml"
cd "/tmp/$repo"
git add .github/workflows/cla.yml
git commit -m "Add CLA check workflow"
git push
cd -
done- Add the workflow to a test repository
- Create a PR from an account that hasn't signed the CLA
- Verify the bot comments asking for a signature
- Sign by commenting:
I have read the CLA Document and I hereby sign the CLA - Verify the signature appears in the versioned
cla.json(e.g.,v1.1/cla.json) in this repo - Verify the PR status check passes
The workflow automatically skips the CLA check for anyone with write, maintain, or admin permissions on the repository — even if they open a PR from a fork. This covers org members in teams with write access, direct collaborators, and repo owners.
These accounts bypass the CLA check:
dependabot[bot]renovate[bot]github-actions[bot]
To add additional users (e.g., contractors not in the org), either:
- Add them to the allowlist in
cla-workflow.yml - Or add them directly to the current version's
cla.json
Sometimes a PR should not require a CLA at all: a docs-only change, an adopters.md
entry, a contribution a maintainer is willing to vouch for, etc. Apply the
cla-waived label to the PR and the CLA check is skipped for that PR.
- The label is a maintainer vouch: only users with triage, write, maintain, or admin on the repo can apply labels, so the waiver is inherently gated.
- Applying the label fires a
pull_request_targetlabeledevent, which re-runs the workflow and clears the check. (This requireslabeledin the caller'spull_request_targettypes -- it is included in the template. Older caller files without it still work via arecheck clacomment.) - The waiver is per-PR. It does not record a signature, so the contributor will still be prompted on their next PR unless they sign or get allowlisted.
- Create the
cla-waivedlabel in each repo where you want to use it (GitHub does not share labels across repos). Any color/description is fine; only the exact namecla-waivedis matched.
When a contributor (typically a corporate contributor) signs the CLA out-of-band -- emails us a signed PDF, signs on paper, etc. -- they have no PR comment for the bot to record. Use the Record offline CLA signature workflow in this repo to add them to the ledger:
- Go to the Actions tab in
netfoundry/cla. - Select "Record offline CLA signature" -> "Run workflow".
- Fill in:
- username -- the contributor's GitHub handle (with or without
@). - reason -- short justification, e.g.
Signed ICLA emailed 2026-05-17, Acme Corp. This is stored in the JSON entry as anotefield and in the commit message, so future maintainers can answer "why is this person in the ledger without a sign comment?". - cla_version -- defaults to
v1.1; change when a new version is current.
- username -- the contributor's GitHub handle (with or without
- Run. The workflow resolves the user's numeric GitHub id, appends an entry to
<version>/cla.json, and commits tomainasnf-cla-bot[bot].
The workflow refuses to add a duplicate (it matches by numeric id, so a later username change does not bypass the check) and fails loudly if the GitHub user does not exist.
All logic lives in scripts/add-cla-signature.sh, so you can preview a change before pushing:
GH_TOKEN=$(gh auth token) bash scripts/add-cla-signature.sh \
--username someone \
--reason "Signed ICLA emailed 2026-05-17, Acme Corp" \
--no-push--no-push leaves the commit local. Drop the flag (and use a token with write access to
netfoundry/cla) to push directly.
The workflows link to the official NetFoundry CLA PDFs:
- Individual CLA: https://github.com/netfoundry/cla/raw/main/v1.1/NetFoundry-ICLA-v1.1.pdf
- Corporate CLA: https://netfoundry.io/docs/assets/files/NetFoundry-CCLA-a68e768031f697589e7d435f17e0cf31.pdf
If these URLs change, update the path-to-document value in cla-workflow.yml.
When you release a new version of the ICLA (e.g., v1.2, v2.0), you can force all contributors to re-sign. No changes are needed in any other repository - the reusable workflow handles everything centrally.
- Add the new PDF to a new version directory (e.g.,
v2.0/NetFoundry-ICLA-v2.0.pdf) - Update two lines in
.github/workflows/cla-workflow.yml:path-to-signatures: 'v2.0/cla.json' path-to-document: 'https://github.com/netfoundry/cla/raw/main/v2.0/NetFoundry-ICLA-v2.0.pdf'
- Commit and push to
main
The new version directory and cla.json file will be created automatically when the first contributor signs. Since the new cla.json starts empty, every contributor will be prompted to re-sign.
Previous signatures are preserved in their version directories (e.g., v1.1/cla.json) as a historical record.
"Failed to create token for cla: Not Found"
- The GitHub App is not installed on the organization
- Or the App doesn't have access to the
netfoundry/clarepository - Go to the App's installation settings and ensure
clais included
"Resource not accessible by integration"
- The
CLA_APP_IDorCLA_APP_PRIVATE_KEYsecrets are missing - Or the secrets aren't accessible to the repository running the workflow
- Check organization secret settings and repository access
Bot doesn't comment on PRs
- Check that the workflow file is in
.github/workflows/cla.yml - Check the Actions tab for workflow run errors
- Ensure
pull_request_targettrigger is present (not justpull_request)
Signatures not being recorded
- The GitHub App needs Contents (read and write) permission on
netfoundry/cla - Check that the App is installed with access to the
clarepository