The prior model claimed git-sync's add-set is scoped to the configured `folders` (pages/config/themes), so accounts/ and user/env/ were "safe by construction." That was wrong: prod auto-commit 9337003 pushed the whole user/env/<host>/config tree (JWT secret, CSRF salt, git-sync token + webhook secret), accounts/mischa.yaml, and system.yaml to Gitea — all outside the configured folders. git-sync stages everything under user/ not gitignored; .gitignore is the only reliable exclusion. - Rewrite the architecture-patterns doc around the corrected predicate and document the incident + remediation (gitignore /env/, rotate token+webhook). - Correct git-sync-notes.md: env/ is NOT safe on folder scope; drop the "never reaches Gitea" claim; fix the secret-files table intro + add /env/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Np4cMQLF77i664CAQXySzU
4.8 KiB
Git Sync Plugin — Setup Notes
⚠️ Config lives in the ENVIRONMENT tree, not user/config/ (IMPORTANT)
Prod has a per-environment override directory user/env/<hostname>/config/
(created for Twig prod-mode — see CLAUDE.md §1). A crucial Grav side effect:
once that env dir exists, the Admin panel saves ALL config changes — system and
plugin — into the active environment's config tree, not user/config/.
So on prod, git-sync.yaml (configured via Admin) lives at:
user/env/intotheeast.com/config/plugins/git-sync.yaml ← here (env tree)
user/config/plugins/git-sync.yaml ← NOT here
Why this matters:
- ⚠️
user/env/is NOT safe unless gitignored — it is NOT scoped out by thefolderssetting. An earlier version of this note claimeduser/env/"never reaches Gitea" because it is outside git-sync's synced folders. That is wrong and caused a live secret leak (2026-07-05). git-sync's auto-commit stages files outside the configuredfolders; on prod it pushed the wholeuser/env/intotheeast.com/config/tree — JWT secret, CSRF salt, and the git-sync token + webhook secret — to Gitea. The fix was to gitignore/env/(commit6e8eadb). So: prod Admin config edits stay server-only only because/env/is now gitignored, not because of folder scope. Author durable config in the repo, not prod Admin. Full analysis:docs/solutions/architecture-patterns/git-sync-secret-exposure-and-tracked-file-boomerang.md. - Look in both places. When inspecting/toggling server config, check
user/config/plugins/<name>.yamlanduser/env/<host>/config/plugins/<name>.yaml(env wins). - Tooling is env-path-aware.
scripts/git-sync-toggle.shtakes aWEBROOTand searchesuser/env/*/config/plugins/git-sync.yamlfirst, thenuser/config/plugins/git-sync.yaml.make remote-git-sync-disable/enable-<env>andmake remote-diag-<env>use it.
Folders format
Older plugin versions' UI saved the folders field as a single comma-string
(- 'pages,config,themes'), which the plugin iterated as one path, so sync
silently did nothing. git-sync v3.4.4 (installed on prod 2026-07-04) saves it
correctly as separate list items:
folders:
- pages
- config
- themes
If you see the comma-string form on an older version, fix it by editing
git-sync.yaml directly (at whichever path it lives — see above); do not
re-save folders via the Admin UI on the buggy version.
Per-install / secret files — must be gitignored (gitignore is the boundary)
git-sync's auto-commit stages everything under user/ that is not
gitignored — the folders setting does not scope the commit add-set (a
2026-07-05 leak proved this by pushing user/env/**, outside the configured
folders). So .gitignore — not folder scope — is the only thing keeping a
per-install or secret file off Gitea. Keep all of these gitignored in
user/.gitignore:
| Path | Why |
|---|---|
env/ |
whole per-host env tree — holds the live git-sync token, JWT secret, CSRF salt + all server-side Admin config. Gitignored + untracked 2026-07-05 (commit 6e8eadb) after it leaked to Gitea. NOT safe on folder scope alone. |
config/plugins/git-sync.yaml |
encrypted token; server-specific (also lives at env path on prod) |
config/plugins/api-private.php |
API JWT secret |
config/security.yaml |
Grav nonces/salts (legacy location) |
config/versions.yaml |
per-install Grav schema-migration state — differs per env (dev 2.0.4, prod 2.0.7); Grav regenerates it. Untracked 2026-07-04. |
config/security-private.php |
CSRF/nonce + admin rate-limit signing salt; gitignored + untracked 2026-07-05 (commit 2840018). Each env keeps its own; untracking regenerates prod's salt (one-time admin re-login). |
Why a key inside a tracked config file (e.g.
popularity.saltinapi.yaml) can't just be stripped — it regenerates at runtime and boomerangs back via git-sync'sgit add -A. Seedocs/solutions/architecture-patterns/git-sync-secret-exposure-and-tracked-file-boomerang.mdfor the full round-trippable-set model.
git-sync config summary (prod, 2026-07-04)
repository: https://git.gorinskat.nl/m038/intotheeast-com-content.git,branch: main, HTTPS + token auth (SSH is Tailscale-only).sync.direction: both,on_save/on_delete/on_media: true→ prod Admin edits and/postpush to Gitea; content-repo pushes pull to prod via webhook (/_git-sync). The webhook is configured in Gitea repo settings (same secret as the test instance).- Before enabling on a fresh server, reset the synced folders clean
(
make remote-fetch-content-<env>) so no install-time drift (e.g. a staleversions.yaml) gets pushed on the first sync. Toggle withmake remote-git-sync-disable/enable-<env>.