Beta — enabled per deployment. Reach out to an administrator to turn GitOps on
for your tenant. Behaviour and the on-disk format may still change.
What syncs
Each Noxus entity is exported as one artifact (thenoxus/v4 versioned
representation). GitOps operates over these artifact kinds, one directory per kind:
| Kind | Repo directory |
|---|---|
flow | flows/ |
flow_version | flows/versions/ |
agent | agents/ |
agent_version | agents/versions/ |
knowledge_base | knowledge-bases/ |
inbox | inboxes/ |
file | files/ |
connection | connections/ |
trigger | triggers/ |
deployment | deployments/ |
secret | secrets/ |
includeSlugs / excludeSlugs.
Secrets sync, their values don’t. For a
secret — and the credential on a
connection — only the entry travels to Git: its name, id, and how other artifacts
reference it. The encrypted value never leaves the database.The artifact format
Every artifact is a single YAML document — an envelope with a stable header and a kind-specificspec:
metadata.idis the artifact’s identity. The trailing hex on theslug(…-a1b2c3) is derived from that id, so renaming an entity never rebinds it to a different one.- References between artifacts (a trigger pointing at a flow, an agent using a
knowledge base) are stored as ids inside
spec, so the whole set stays internally consistent when it moves.
Repository layout
Enable and connect
Set up the GitHub provider (one-time, admin)
Before anyone can connect an account, an administrator registers an OAuth app so Noxus can
speak to GitHub on a user’s behalf. On GitHub, go to Settings → Developer settings →
OAuth Apps → New OAuth App, set the Authorization callback URL to
https://<your-noxus-domain>/api/backend/integrations/oauth/callback, and copy the
generated Client ID and Client Secret. Enter both in the Noxus Admin panel for the
GitHub provider, granting the repo scope — read and write, since GitOps pushes
commits. See Admin: connecting integration providers for the
full walkthrough.If your deployment ships the Noxus-managed GitHub provider, you can skip this — no client
ID or secret needed. GitLab is configured the same way; generic Git uses an access token
(or username + password) instead of OAuth.Connect a Git account
Once the provider exists, each user connects their own account in Settings →
Integrations (GitHub, GitLab, or generic Git). This credential is what GitOps
uses to read and write the repo.
Point at a repository
Open Settings → GitOps, pick the connection, then set the repository
(
owner/repo) and branch (defaults to main).Choose what syncs and how
Select the artifact kinds, pick a direction, and set the sync interval.
Save the configuration.
Direction and scheduling
GitOps runs in one of three directions:| Direction | Behaviour |
|---|---|
db_to_git (default) | Push workspace config out to the repo. |
git_to_db | Import config from the repo into the workspace. |
bidirectional | Sync both ways; authoritative (db or git) breaks ties. |
syncIntervalMinutes (default 15). A background worker
runs every five minutes and syncs each enabled workspace whose interval has
elapsed. Set the interval to 0 for manual-only — the scheduler skips the
workspace, but Sync now still works.
The
enabled flag only gates the scheduler. A manual Sync now needs just a
valid connection and repository. A bidirectional manual run returns two run records
(one per direction); single-direction returns one.Per-artifact push and pull
You don’t have to sync the whole workspace. From the editor, the Git control on a flow or agent shows whether that artifact is linked, ahead, or behind the repo, and lets you push or pull just that one — optionally with its dependencies (e.g. pushing a flow can carry the knowledge bases and connections it references).The API
All endpoints are scoped to a workspace (group_id). Mutating calls require the
workspace integrations:edit permission; reads require membership. Every route
returns 404 while GITOPS_ENABLED is off.
| Method | Path | Purpose |
|---|---|---|
GET | /groups/{group_id}/git-sync/config | Read the sync config |
PUT | /groups/{group_id}/git-sync/config | Update the sync config |
POST | /groups/{group_id}/git-sync/test | Test the connection can read/write |
GET | /groups/{group_id}/git-sync/health | Connection + repo access status |
GET | /groups/{group_id}/git-sync/repos | List repos the connection can see |
POST | /groups/{group_id}/git-sync/dry-run | Preview the changes a sync would make |
POST | /groups/{group_id}/git-sync/run | Trigger a full sync now |
GET | /groups/{group_id}/git-sync/runs | Last 50 sync runs |
GET | /groups/{group_id}/git-sync/runs/{run_id} | One run with its per-artifact changes |
GET | /groups/{group_id}/git-sync/artifacts/{kind}/{id} | Sync status of one artifact |
POST | /groups/{group_id}/git-sync/artifacts/{kind}/{id}/push | Push one artifact (?with_dependencies) |
POST | /groups/{group_id}/git-sync/artifacts/{kind}/{id}/pull | Pull one artifact (?with_dependencies) |
Health and run history
GET …/health reports one of healthy, connection_missing, connection_invalid,
or repo_unreachable, along with whether the repo exists and is can_read /
can_write. If the connection breaks, sync degrades to local-only rather than
erroring, and the failure is recorded as a categorised run instead of a 500.
Every sync produces a run with a per-artifact change list (kind, slug, name,
action, direction, and any error). Runs are the audit trail for GitOps — they’re
recorded in dedicated git_sync_runs / git_sync_changes tables, separate from the
web audit log. Use dry-run to preview exactly what a sync would change before
committing to it.
Safety controls
GitOps defaults are deliberately conservative. The knobs worth knowing:Secret scanning — secretPolicy
Secret scanning — secretPolicy
Before pushing, each artifact’s
spec is scanned for secret-like values. block
(default) refuses the push, warn records a warning, off disables it. File
payloads (base64 content) are skipped — they’re the intended data and would
always false-positive.Matching — matchStrategy
Matching — matchStrategy
by_id (default) matches artifacts only by their id, so importing can never
silently bind, say, a credential to the wrong connection by name. Other strategies
(id_then_slug, by_slug, clone) trade safety for flexibility.Import behaviour — writeMode & dependencies
Import behaviour — writeMode & dependencies
writeMode controls what an import does to a matched entity: replace (default)
overwrites in place, create mints fresh ids, version appends a version row.
onMissingDependency (warn / fail) decides what happens when an artifact
references something not present.Deletions — onUpstreamDelete & removeUntracked
Deletions — onUpstreamDelete & removeUntracked
onUpstreamDelete (ignore_warn default, or soft_delete) governs what happens
when something disappears upstream. removeUntracked is a destructive mirror:
a git→db sync deletes in-scope database artifacts that are absent from the repo.
Off by default — enable only when the repo is truly the source of truth.Same-deployment sync — allowSameDeployment
Same-deployment sync — allowSameDeployment
When two workspaces share one database, an imported artifact’s canonical id may
already exist elsewhere.
allowSameDeployment clones it under a fresh local id
instead of failing, and GitOps refuses cross-workspace references so tenants stay
isolated. Generic-Git targets are also SSRF-guarded.Using GitOps in CI/CD
Because the repository holds your configuration as reviewable YAML, you can wrap it in your normal delivery process:- Review config as code — enable
db_to_git, and every flow/agent change lands as a Git diff you can open a pull request against. - Promote across environments — export from staging, then
git_to_dbinto production (orbidirectionalwithauthoritative: git) to roll a reviewed set of artifacts forward.by_idmatching keeps entities stable across the hop. - Back up and restore — a scheduled
db_to_gitsync gives you a point-in-time, versioned snapshot of a workspace you can restore by pulling.