# Auto-sync

Six ways to keep your repowise index current: a post-commit hook, a file watcher, GitHub, GitLab and Gitea or Forgejo webhooks, and a polling safety net.

A typical single-commit [`repowise update`](https://docs.repowise.dev/cli/indexing#repowise-update)
touches 3 to 10 pages and completes in
about 30-60 seconds. The question isn't *whether* to auto-sync; it's
which method fits your workflow. Pick one (or combine them).

| Method | Command / setup | Best for |
|---|---|---|
| [Post-commit hook](https://docs.repowise.dev/auto-sync#post-commit-hook) | `repowise hook install` | Solo dev, set-and-forget |
| [File watcher](https://docs.repowise.dev/auto-sync#file-watcher) | `repowise watch` | Active iteration without committing |
| [GitHub webhook](https://docs.repowise.dev/auto-sync#github-webhook) | server + `REPOWISE_GITHUB_WEBHOOK_SECRET` | Teams, CI/CD |
| [GitLab webhook](https://docs.repowise.dev/auto-sync#gitlab-webhook) | server + `REPOWISE_GITLAB_WEBHOOK_TOKEN` | Teams on GitLab |
| [Gitea / Forgejo webhook](https://docs.repowise.dev/auto-sync#gitea-and-forgejo-webhook) | server + `REPOWISE_GITEA_WEBHOOK_SECRET` | Teams on Gitea or Forgejo |
| [Polling fallback](https://docs.repowise.dev/auto-sync#polling-fallback) | automatic with `repowise serve` | Safety net for missed webhooks |

## Post-commit hook

Runs `repowise update` in the background after every local commit
without blocking the terminal. Every flag is on [Hooks](https://docs.repowise.dev/cli/hook).

```bash
repowise hook install              # current repo
repowise hook install --workspace  # all workspace repos
repowise hook status               # check if installed
repowise hook uninstall            # remove the hook
```

The hook is marker-delimited, so it coexists safely with other hooks
(linters, formatters). If it doesn't fire, make sure
`.git/hooks/post-commit` is executable: `chmod +x .git/hooks/post-commit`.

## File watcher

Watches the working directory for file saves and triggers an update
without requiring a commit ([`repowise watch`](https://docs.repowise.dev/cli/server#repowise-watch)). Best when you want the wiki current
between commits. Unlike the post-commit hook, the watcher indexes the
working tree, so staged, unstaged, and untracked files all reach the
index without a commit.

```bash
repowise watch                  # current dir
repowise watch --debounce 5000  # wait 5s after the last change (default 2s)
repowise watch --workspace      # all repos in a workspace
repowise watch --index-only     # no model calls per save
```

Changes inside `.repowise/` are ignored to prevent loops. Press `Ctrl+C`
to stop. Best for short bursts, not a long-running daemon. On a repo
indexed with docs, each trigger regenerates the changed files' pages
with a model; use `--index-only` to keep the index current for free and
leave the prose for a later `repowise update`.

## GitHub webhook

Self-hosted teams point GitHub at [`repowise serve`](https://docs.repowise.dev/cli/server#repowise-serve)'s webhook endpoint;
push events trigger incremental updates. HMAC-SHA256 signature
verification is mandatory.

```bash
repowise serve                                  # starts on :7337
export REPOWISE_GITHUB_WEBHOOK_SECRET="your-secret"
```

Then in GitHub: **Settings → Webhooks → Add webhook**

- **Payload URL**: `https://your-server.example.com/api/webhooks/github`
- **Secret**: same value as `REPOWISE_GITHUB_WEBHOOK_SECRET`
- **Events**: Push events only

Successful deliveries return `200` with `{"status": "accepted"}`. With a
secret configured, a missing or invalid signature returns `401`.

If you leave `REPOWISE_GITHUB_WEBHOOK_SECRET` unset, the endpoint fails
closed for anything remote: non-local callers get `403`. Local callers on
loopback are still accepted, which keeps development convenient, so treat
an unset secret as dev-only and always set one on an exposed server.

Check **Settings → Webhooks → Recent Deliveries** for diagnostics.

## GitLab webhook

Same idea, token-based auth via the `X-Gitlab-Token` header.

```bash
export REPOWISE_GITLAB_WEBHOOK_TOKEN="your-token"
```

In GitLab: **Settings → Webhooks**

- **URL**: `https://your-server.example.com/api/webhooks/gitlab`
- **Secret token**: same value as `REPOWISE_GITLAB_WEBHOOK_TOKEN`
- **Trigger**: Push events

Tokens are verified via constant-time comparison.

## Gitea and Forgejo webhook

One endpoint serves both forges. Forgejo is a Gitea fork and still sends
the `X-Gitea-*` headers beside its own `X-Forgejo-*` ones.

```bash
export REPOWISE_GITEA_WEBHOOK_SECRET="your-secret"
```

In your repository: **Settings → Webhooks → Add Webhook → Gitea**
(**Forgejo** on Forgejo)

- **Target URL**: `https://your-server.example.com/api/webhooks/gitea`
- **HTTP method**: `POST`, content type `application/json`
- **Secret**: same value as `REPOWISE_GITEA_WEBHOOK_SECRET`
- **Trigger on**: Push events

Every request must carry a valid HMAC-SHA256 digest of the raw body in
`X-Gitea-Signature` or `X-Forgejo-Signature`. Both forges send it as bare
lowercase hex with no `sha256=` prefix, which is the one way their
delivery differs from GitHub's; a prefixed value or a mismatch gets
`401`. All three webhook endpoints match the payload's repository to a
registered one by a canonical key, so clone, SSH and web URLs of the
same repository match.

## Polling fallback

When `repowise serve` is running, a background job polls registered
repos every 15 minutes. If a webhook delivery fails, polling catches
the missed push and triggers an update.

No configuration needed. Just don't rely on this alone: it has a
15-minute lag.

## CLI update vs. server sync

The CLI (`repowise update`) and the server (`POST /api/repos/{id}/sync`,
which webhooks and polling trigger) take different paths, and the
difference is cost-relevant:

- **`repowise update`** diffs against the last synced commit, rebuilds
  the dependency graph, re-indexes git metadata for changed files, runs
  partial dead-code analysis, re-scans for decisions, then calls the LLM
  to regenerate only the affected pages.
- **Server sync** re-traverses and re-parses every file, rebuilds the
  graph, re-indexes git metadata, and runs a full dead-code and decision
  scan, but it does **not** regenerate wiki pages, so it makes no LLM
  calls. Use **Full Re-index** (`POST /api/repos/{id}/full-resync`) to
  also regenerate all wiki pages after a server sync.

So a webhook-triggered sync keeps the graph and metadata current for
free; the prose only catches up once you run a CLI update or trigger a
full re-index.

## Troubleshooting

**"No previous sync found"**: run `repowise init` first to create the
initial wiki before using any auto-sync method.

**"Already up to date"**: the wiki is already synced to the latest
commit. Nothing to do.

**Hook doesn't fire**: make sure the hook file is executable:
`chmod +x .git/hooks/post-commit`.

**Webhook returns 401**: check that the secret or token matches between
your git host and the environment variable on the server, and check the
prefix: GitHub's `X-Hub-Signature-256` needs `sha256=`, while Gitea's and
Forgejo's signatures must not have one.

**Webhook returns 403**: the server received a request with no
signature at all. This usually means `REPOWISE_GITHUB_WEBHOOK_SECRET`,
`REPOWISE_GITLAB_WEBHOOK_TOKEN` or `REPOWISE_GITEA_WEBHOOK_SECRET` is not
set on the server. Set the
environment variable, restart the server, and re-deliver the payload
from your provider's webhook settings page.

## Hosted version

If you're on [repowise.dev](https://repowise.dev), webhook setup is
automatic. Installing the GitHub App registers the webhook, and pushes
auto-sync the index without you doing anything. A GitLab.com project
syncs on push through a project hook Repowise adds when you turn
auto-sync on, with polling as the fallback.

> **Note**
>
> **Belt and braces**: combine the post-commit hook (local) with a
> GitHub webhook (server). Local commits sync instantly; pushes from
> other branches keep the server in sync. The polling fallback catches
> the rare miss.

On Hosted, from the Pro plan up, each GitHub or GitLab.com repo re-indexes on push, with no hook, watcher or webhook server to run.

[Hosted platform](https://docs.repowise.dev/hosted/overview)

[Try hosted](https://repowise.dev/login?mode=signup)
