> ## Documentation Index
> Fetch the complete documentation index at: https://docs.masterskills.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Publish from CI

> Non-interactive publishing with a service token — for pipelines that ship skills.

Teams that keep skills in a git repo can publish them from the pipeline on merge, so the registry always mirrors the reviewed state of the repo.

<Note>
  In CI, your merge review **is** the approval step — `--yes` exists exactly for this. Agents must
  never use it; the interactive approval rules in [Trust & approvals](/concepts/trust) apply to
  humans and agents, not to pipelines you control.
</Note>

## 1. Get a token

Device tokens are created through the browser [login flow](/cli/login) and stored in `~/.masterskills/config.json`. For CI:

1. Run `masterskills login` once on a workstation (consider a dedicated service account so revocation doesn't take a person's machine offline).
2. Copy the `token` value from `~/.masterskills/config.json`.
3. Store it as a CI secret, e.g. `MASTERSKILLS_TOKEN`.

The `MASTERSKILLS_TOKEN` environment variable takes precedence over any stored config, so the CI job needs no config file. The device shows up in the [panel](/panel/devices) like any other and can be revoked there at any time.

## 2. Publish non-interactively

```yaml theme={"dark"}
# .github/workflows/publish-skills.yml
name: Publish skills
on:
  push:
    branches: [main]
    paths: ["skills/**"]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - name: Publish changed skills
        env:
          MASTERSKILLS_TOKEN: ${{ secrets.MASTERSKILLS_TOKEN }}
        run: |
          for dir in skills/*/; do
            npx @masterskills/cli publish "$dir" --org acme --yes
          done
```

Publishing unchanged content is effectively a no-op — versions are content-hash based, so re-running the job doesn't create noise. The server-side secret scan still runs on every publish and rejects findings with a non-zero exit, failing the job loudly rather than shipping a secret.

## Environment variables

| Variable               | Purpose                                            |
| ---------------------- | -------------------------------------------------- |
| `MASTERSKILLS_TOKEN`   | Device token; overrides the stored config          |
| `MASTERSKILLS_API_URL` | Registry base URL — for self-hosted servers        |
| `MASTERSKILLS_HOME`    | Redirect all CLI state away from `~/.masterskills` |
