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.
- 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/cli1.23.0 or later (--github-actionswas added in 1.23.0).
.github/workflows/moda-sync.yml
<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
--harness-rescan):
.github/workflows/moda-harness-rescan.yml, pinned to the CLI version that generated it. Each run executes three commands:
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.moda harness approve --yes— validates the report and writes a non-interactive approval.moda harness sync --from-report— uploads the approved graph to your workspace.
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 filemoda 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
mainand onworkflow_dispatch, never onpull_request, so unmerged agent changes are never synced. - Path scoping. Add
on.push.pathsglobs so only pushes touching agent-relevant code trigger a rescan. - Superseding runs. The
concurrencygroup cancels an in-flight rescan when a newer push lands. - What leaves your CI.
harness analyze --remoteuploads 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: readpermissions and never writes back to the repository.
The approval gate
Syncing withmoda 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 approvevalidates 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--yesit asks for interactive confirmation.moda harness sync --from-reportrefuses 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
- Harness overview — the hosted GitHub App path and what the dashboard shows.
- Authentication — creating and storing the API key for option B.
- CLI reference — flags for the
moda harnesscommand family.