# Git sync

Point Opafra at a repository and it imports plans, scripts and templates from it. Sync is one-way, and synced objects are read-only in the builder.

Source: https://opafra.com/docs/security/git-sync

---

A git source points Opafra at a repository and branch. On each sync, Opafra reads plans,
scripts and config templates from it and creates or updates them in your workspace.

Sync is **one-way**: Opafra reads from git and never writes back. That is what makes the
repository the single source of truth rather than one of two.

## Why sync at all

Keeping plans in git means changes go through review before they reach production, the
history of what changed is the repository's history, and a plan can be reverted the way
code is.

A plan owned by a repository is **read-only in the builder**. Edits go through a pull
request and arrive on the next sync, which is the property that makes the review real
rather than advisory.

## Connect a repository

Choose the repository, the branch, and how Opafra authenticates:

| Auth | Use it when |
|---|---|
| **Public** | The repository needs no credential |
| **Token** | A personal access token, stored encrypted |
| **GitHub App** | Preferred. Clone credentials are minted fresh at clone time rather than stored |

The GitHub App path is better where it is available: there is no long-lived token at
rest, because an installation token is minted for each clone and expires on its own.

## Layout

Opafra looks in three directories, relative to an optional base path:

```text
plans/        plans
scripts/      shell scripts a plan can upload and run
templates/    config templates
```

Those defaults follow the convention most teams already use. Any of them can be changed,
and blanking one stops that kind being synced at all.

## Sync modes

| Mode | Behaviour |
|---|---|
| **Manual** | Syncs when you ask it to |
| **Webhook** | The repository notifies Opafra on push, and it syncs immediately |
| **Poll** | Opafra checks periodically |

Webhook is the right default for an active repository: changes land quickly and nothing
is polled that has not changed. The webhook is authenticated with a shared secret, so an
unsigned request cannot trigger a sync.

## What a sync does

Each file is hashed. A file whose hash is unchanged is skipped, so a sync where nothing
moved is a no-op rather than a rewrite of every object.

The result reports how many objects were created, updated and removed, plus any files that
failed to parse. The commit that was synced is recorded, so you can tell exactly which
revision of the repository your workspace reflects.

> **Note**
>
> A file that fails to parse is reported by name rather than as a count. One malformed plan
> does not stop the rest of the sync.

## Deleted files

A file removed from the repository does not delete the object. It is marked **orphaned**.

That is deliberate. A branch mistake, a bad merge or a mistyped path would otherwise
silently remove working automation, and a deletion is much harder to notice than an
orphan flag. Restoring the file un-orphans the object on the next sync; deleting it for
real is a decision you make in Opafra.

## Grouping

A synced plan can carry a group, which files it into a collection. You can also file one
by hand, and a manual filing is pinned so re-syncs do not overwrite it unless the file's
own group changes.

Grouping and git source are independent axes: where a plan came from and how it is
organised are different questions.

## What is recorded

Connections, sources, every sync, and webhook enable and disable are all written to the
[audit log](/docs/governance/audit-log), including which commit a sync brought in.

## Next steps

- [Plan structure](/docs/plans/structure) for what a plan file contains
- [Audit log](/docs/governance/audit-log) for what a sync records
