CI/CD Secrets Configuration
CI/CD Secrets Configuration
Version: 3.0 Last Updated: 2026-07-15 Target Audience: Repository maintainers and administrators
AIWG's Gitea CI/CD workflows use vault for repository-managed secrets. Gitea Actions stores only the vault bootstrap pair:
| Tracker secret | Purpose |
|---|---|
| `VAULT_CI_ROLE_ID` | AppRole role ID for the `ci-aiwg` reader. |
| `VAULT_CI_SECRET_ID` | AppRole secret ID for the `ci-aiwg` reader. |
Do not recreate legacy Gitea secrets such as `NPM_TOKEN`, `GH_ACCESS_TOKEN`, `AIWG_IO_DISPATCH_TOKEN`, or `DOCSITE_DEPLOY_KEY`. Those values live in vault and are fetched at runtime with `../../ci/vault-fetch.sh`.
`secrets.GITHUB_TOKEN` is not part of this migration. It is a per-run token issued by the CI system and is still used by PR-comment workflows.
Vault Fetch Specs
| Workflow | Spec | Exported value |
|---|---|---|
| `.gitea/workflows/npm-publish.yml` | `ci/vault-fetch.npm-publish.spec` | `NODE_AUTH_TOKEN` |
| `.gitea/workflows/gitea-release.yml` | `ci/vault-fetch.gitea-release.spec` | `GITEA_TOKEN` |
| `.gitea/workflows/upload-release-sigs.yml` | `ci/vault-fetch.upload-release-sigs.spec` | `GITEA_TOKEN` |
| `.gitea/workflows/github-mirror.yml` | `ci/vault-fetch.github-mirror.spec` | `GH_TOKEN` |
| `.gitea/workflows/notify-site.yml` | `ci/vault-fetch.notify-site.spec` | `AIWG_IO_DISPATCH_TOKEN` |
| `.gitea/workflows/docsite-deploy.yml` | `ci/vault-fetch.docsite-deploy.spec` | `DOCSITE_DEPLOY_KEY_FILE` |
The specs contain only `_VAULT_PATH` and `_VAULT_FIELD` placeholders. Concrete vault paths, metadata paths, hostnames, and fields are stored as private Gitea variables or in the private operations catalog.
Validate specs without reading live secrets:
for f in ci/vault-fetch.*.spec; do
bash ci/vault-fetch.sh --spec "$f" --dry-run
done
Run the local consistency gate:
npm run lint:vault-migration
Classification
| Name | Class | Destination |
|---|---|---|
| `VAULT_CI_ROLE_ID` | `BOOTSTRAP` | Gitea Actions secret |
| `VAULT_CI_SECRET_ID` | `BOOTSTRAP` | Gitea Actions secret |
| `GITHUB_TOKEN` | CI-issued token | No storage; issued per run |
| `GT_NPM_TOKEN_VAULT_PATH`, `GT_NPM_TOKEN_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
| `GT_RELEASE_TOKEN_VAULT_PATH`, `GT_RELEASE_TOKEN_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
| `GH_MIRROR_TOKEN_VAULT_PATH`, `GH_MIRROR_TOKEN_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
| `AIWG_IO_DISPATCH_TOKEN_VAULT_PATH`, `AIWG_IO_DISPATCH_TOKEN_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
| `DOCSITE_DEPLOY_KEY_VAULT_PATH`, `DOCSITE_DEPLOY_KEY_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
| `RELEASE_SIGNING_KEY_VAULT_PATH`, `RELEASE_SIGNING_KEY_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
| `RELEASE_SIGNING_PASSPHRASE_VAULT_PATH`, `RELEASE_SIGNING_PASSPHRASE_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
| `VAULT_ADDR` | `ROUTE` | Gitea Actions variable |
| `DOCSITE_DEPLOY_HOST`, `DOCSITE_DEPLOY_PORT`, `DOCSITE_DEPLOY_USER`, `DOCSITE_DEPLOY_PATH` | `CONFIG` | Gitea Actions variables |
The machine-readable variable manifest is `../../ci/vault-migration-plan.json`.
Provisioning
Use the private operations catalog to create the scoped policy and AppRole. Keep the concrete policy file outside this repository, then pass it to:
npm run provision:vault-approle -- --policy-file /path/to/private-policy.hcl
VAULT_ADMIN_TOKEN=<admin-token> npm run provision:vault-approle -- \
--policy-file /path/to/private-policy.hcl \
--apply
To induct or rotate values from an approved local medium, prepare one file per route ID:
gitea-npm-token.value
gitea-release-token.value
github-mirror-token.value
aiwg-io-dispatch-token.value
docsite-deploy-key.value
release-signing-key.value
release-signing-passphrase.value
Preview the vault writes without printing paths:
npm run provision:vault-migration -- \
--routing-env /path/to/private-routing.env \
--values-dir /path/to/approved-value-files
Apply only after confirming the private routing env and value files:
npm run provision:vault-migration -- \
--routing-env /path/to/private-routing.env \
--values-dir /path/to/approved-value-files \
--apply
Validate and apply the Gitea handoff without printing values:
npm run configure:gitea-vault -- \
--bootstrap-env ~/.config/vault/handoff/aiwg-ci.env \
--vars-env /path/to/private-routing-and-deploy.env
npm run configure:gitea-vault -- \
--bootstrap-env ~/.config/vault/handoff/aiwg-ci.env \
--vars-env /path/to/private-routing-and-deploy.env \
--apply
Docsite Deployment Config
The `Docsite Deploy` workflow publishes the AIWG documentation tenant over SSH. The private key is fetched from vault. Target connection details are Gitea Actions variables and should not be hardcoded in workflow files.
`GT_ACCESS_TOKEN` is not used by AIWG's current docsite workflows. The publisher is installed from npm as `@pagenary/publisher`; do not add a clone token back to the docsite workflows unless the publisher source model changes again.
Troubleshooting
Vault Bootstrap Missing
If `VAULT_CI_ROLE_ID` or `VAULT_CI_SECRET_ID` is absent, secret-bearing Gitea jobs skip instead of attempting a partial publish. Add or rotate the AppRole bootstrap pair in Gitea Actions secrets.
Fetch Fails with 403
The `ci-aiwg` AppRole is missing the required path policy or is trying to read the wrong leaf. Compare the workflow spec placeholder names with the private vault routing env and policy.
Gitea Registry Publish Fails
Check the private routing entry, token scope, and token freshness. The value must remain vault-only.