Skip to content

Git-backed Versioning

With Git versioning on, every connection / environment / flow / template / secret-reference change is committed to a local git repository and (optionally) pushed to a remote. You get an audit trail, one-call config rollback, and branch-based staging.

The working tree where commits are written. Three hard requirements, each enforced at startup:

  • Owned by the service user. libgit2 refuses a working tree owned by a different OS user (its safe.directory equivalent). Running as zenvara, the repo must be owned by zenvara.
  • Writable under systemd hardening. With ProtectSystem=strict, the path must sit under a ReadWritePaths entry. The data dir (/var/lib/zenvara) already qualifies.
  • Outside Storage.Directory. A nested path logs an overlap warning — keep it in a sibling directory.
/etc/systemd/system/zenvara.service.d/git-versioning.conf
install -d -o zenvara -g zenvara /var/lib/zenvara-versions
[Service]
ReadWritePaths=/var/lib/zenvara-versions
# then: systemctl daemon-reload

libgit2 push/clone with an interactive account password trips server-side bot protection (e.g. Bitbucket’s CAPTCHA lockout) and will lock the account. Create an HTTP access token or a dedicated service account with repository write, and put those in Remote.Username / Remote.Password. Leave Remote.Url empty for local-only versioning.

Step 3 — Seed the repository before first start

Section titled “Step 3 — Seed the repository before first start”

Pre-create the working tree so the platform simply opens it:

Terminal window
cd /var/lib/zenvara-versions
sudo -u zenvara git init -b main .
sudo -u zenvara git -c user.email=ops@example.com -c user.name=Zenvara \
commit --allow-empty -m "Initialize git-backed config versioning"
sudo -u zenvara git remote add origin https://git.example.com/scm/team/zenvara-config.git

If the remote already has a main branch with history, skip the manual init — the platform clones it on first start.

Git:
Enabled: true
LocalPath: "/var/lib/zenvara-versions" # owned by service user, outside Storage.Directory
BaselineOnEnable: false # best-effort startup storage→git baseline
Remote:
Url: "https://git.example.com/scm/team/zenvara-config.git"
Branch: main
AutoPush: true
Username: "zenvara-ci" # service account or token owner
Password: "<http-access-token>" # NOT a personal account password
CommitIdentity:
Name: "Zenvara"
Email: "ops@example.com"

After systemctl restart zenvara, the log shows Opening existing git config repository at <LocalPath>. Verify end to end by editing any entity: a commit Update entity: <name> appears in the repo and, with AutoPush, lands on the remote.

URLs carry the branch as a path segment: /api/v1/main/flows/... runs against production, /api/v1/staging/flows/... against the staging branch. Stage a change on a branch, validate it, then promote — the production flows are untouched until promotion. On git-disabled installs, the live alias resolves to the production branch so the same URLs work.

With git versioning plus a shared remote, Git.Sync.Enabled: true makes an instance:

  1. fetch the remote,
  2. reverse-sync changed config into storage,
  3. invalidate caches.

Trigger it via PollInterval and/or the POST /api/v1/platform/git/sync HMAC webhook. The same remote keeps multiple instances in sync, with ConflictPolicy: manual | remote | local.