Skip to main content
The default way to keep a harness current is the Moda GitHub App: hosted analysis runs on every default-branch push with no CI configuration. Use this page when you want analysis to run inside your own CI instead — either with GitHub Actions OIDC (no long-lived secret) or with an API key (works in any CI system).
moda harness analyze is opt-in. Without the --experimental flag or the environment variable MODA_HARNESS_ANALYZE_CLI=1, the command exits with code 1 and points you to the GitHub App. Every workflow on this page sets MODA_HARNESS_ANALYZE_CLI: "1".

Option A: GitHub Actions with OIDC

moda harness analyze --github-actions authenticates with the workflow’s GitHub Actions OIDC identity token, exchanged for a short-lived Moda token. No Moda API key is stored in the repository or used on the runner. The command builds a source snapshot on the runner, submits it for hosted analysis, polls for the result, and syncs the graph when permitted. Sync rules:
  • The harness graph syncs only when the workflow runs on the default branch, triggered by a push or a manual workflow_dispatch.
  • On any other ref or event, the run analyzes but does not sync.
  • The target workspace is resolved server-side from the repository identity in the OIDC token; the workflow cannot redirect it.
Prerequisites:
  • The repository is connected through the Moda GitHub App (Settings → Integrations) with sync mode GitHub Actions or Hosted analysis. Otherwise the token exchange fails with 403.
  • The workflow grants permissions: id-token: write.
  • @moda-ai/cli 1.23.0 or later (--github-actions was added in 1.23.0).
.github/workflows/moda-sync.yml
Replace <version> with the current @moda-ai/cli release. Pin an exact version rather than @latest so CI runs a release you have reviewed. After a successful default-branch run, the updated harness appears under Harness in the dashboard.

Option B: API-key rescan workflow

This variant authenticates with a Moda API key instead of OIDC. The commands run anywhere the CLI runs, so it also fits CI systems other than GitHub Actions.

Generate the workflow

To only rescan when agent-relevant paths change, scope it (this implies --harness-rescan):
Either form writes .github/workflows/moda-harness-rescan.yml, pinned to the CLI version that generated it. Each run executes three commands:
  1. moda harness analyze --remote --yes — builds a safe source snapshot in CI, uploads it to Moda’s hosted analysis over your API key, polls until the run completes, and writes the resulting report to .moda/harness-report.json.
  2. moda harness approve --yes — validates the report and writes a non-interactive approval.
  3. moda harness sync --from-report — uploads the approved graph to your workspace.
Analysis itself runs on Moda’s side under a Moda-held LLM key, so the workflow needs no LLM credentials — only your Moda API key.

Repository secrets

  • MODA_API_KEY (required) — an API key (moda_sk_...) from Settings → Ingestion keys. See Authentication.
  • MODA_TENANT_ID (optional) — keys are bound to a single workspace, so the key alone determines where the harness syncs. This secret only labels the CLI’s local configuration and can be omitted.

The workflow

This is the file moda init --harness-rescan generates. If you copy it by hand, replace <version> with the current @moda-ai/cli release.
.github/workflows/moda-harness-rescan.yml

Behavior notes

  • Default branch only. The workflow runs on pushes to main and on workflow_dispatch, never on pull_request, so unmerged agent changes are never synced.
  • Path scoping. Add on.push.paths globs so only pushes touching agent-relevant code trigger a rescan.
  • Superseding runs. The concurrency group cancels an in-flight rescan when a newer push lands.
  • What leaves your CI. harness analyze --remote uploads a bounded source snapshot: source, configuration, prompt, skill, eval, and deployment files, plus README and AGENTS files. Secret-like paths and symlinks are skipped, and secret-shaped strings are redacted before upload. The snapshot request is capped at 5 MB and the snapshot is deleted after the run. See Limits.
  • Read-only. The job has contents: read permissions and never writes back to the repository.

The approval gate

Syncing with moda harness sync --from-report (option B) requires an explicit approval. The GitHub App hosted path and the OIDC path (option A) do not — in both, the server validates the report and syncs it directly.
  • moda harness approve validates the report first — it refuses to approve a report that fails validation — then writes .moda/harness-report-approval.json, bound to the report’s hash. Without --yes it asks for interactive confirmation.
  • moda harness sync --from-report refuses reports that are not approved. Because the approval is hash-bound, any change to the report after approval requires approving again.
  • The serialized harness graph is capped at 2 MB on sync; the error message suggests narrowing the analysis root if you hit it.

Resuming a detached run

Remote analysis runs are recorded in .moda/harness-remote-run.json. If CI is interrupted mid-poll, moda harness pull fetches the finished report for the recorded run (--run-id selects a specific run), and moda harness pull --sync also syncs it.

Local analysis

Running the analysis on your own machine (moda harness analyze --experimental) is experimental. The hosted paths above are the supported way to produce and sync a harness.

Troubleshooting

Next steps