Summary
cix's GitHub push-webhook auto-registration is not idempotent. When a repo's webhook is (re)registered — re-adding the project, a reindex, or a server upgrade — cix POSTs a new hook to GitHub instead of checking whether one with the same target URL already exists. Repeated registrations therefore accumulate multiple identical webhooks on the same repo, all pointing at the same /api/v1/webhooks/github/<HASH> endpoint.
GitHub then delivers every push N times to the same endpoint, so cix processes each push N times (redundant clone/reindex), and the repo's Settings → Webhooks fills up with duplicates.
Observed (cix v0.7.0)
One repo ended up with 3 identical webhooks:
| created_at |
events |
url |
last delivery |
| 15:16 |
push |
https://cix.example.com/api/v1/webhooks/github/<HASH> |
OK |
| 17:46 |
push |
https://cix.example.com/api/v1/webhooks/github/<HASH> |
OK |
| 17:49 |
push |
https://cix.example.com/api/v1/webhooks/github/<HASH> |
OK |
<HASH> is identical on all three (same project), so these are true duplicates — not distinct projects/refs. They were created minutes-to-hours apart, i.e. each register attempt added another hook rather than reusing the existing one. All three are active and deliver successfully → every push fans out 3×.
(The server startup log also surfaces the auto-registration path, e.g. webhooks: ... manual_webhooks:N auto_webhooks:M.)
Expected
Registration should be idempotent:
- Before creating,
GET /repos/{owner}/{repo}/hooks and skip (or PATCH) if a hook already targets the same cix webhook URL.
- Never POST a second hook with a URL that already exists on the repo.
Why it matters
- N× duplicate push deliveries → N× redundant reindex/clone per push (wasted CPU + embedding-provider API calls).
- Every connected repo's webhook list accumulates duplicates over time.
- Compounds with the v0.7 webhook-URL format change (
workspace_repos → git_repos + workspace_projects): the startup WARN advises re-registering old-format hooks, but if re-registration doesn't dedupe, a repo ends up with old and new and repeated hooks instead of a single migrated one.
Suggested fix
- Make webhook registration idempotent — match existing hooks on
config.url; reuse or PATCH instead of creating a new one.
- On the v0.7 URL migration, update the existing hook's URL in place rather than adding a new hook.
- Optional: a reconcile/cleanup that removes duplicate (and stale-URL) cix hooks from a repo.
Summary
cix's GitHub push-webhook auto-registration is not idempotent. When a repo's webhook is (re)registered — re-adding the project, a reindex, or a server upgrade — cix POSTs a new hook to GitHub instead of checking whether one with the same target URL already exists. Repeated registrations therefore accumulate multiple identical webhooks on the same repo, all pointing at the same
/api/v1/webhooks/github/<HASH>endpoint.GitHub then delivers every push N times to the same endpoint, so cix processes each push N times (redundant clone/reindex), and the repo's Settings → Webhooks fills up with duplicates.
Observed (cix v0.7.0)
One repo ended up with 3 identical webhooks:
pushhttps://cix.example.com/api/v1/webhooks/github/<HASH>pushhttps://cix.example.com/api/v1/webhooks/github/<HASH>pushhttps://cix.example.com/api/v1/webhooks/github/<HASH><HASH>is identical on all three (same project), so these are true duplicates — not distinct projects/refs. They were created minutes-to-hours apart, i.e. each register attempt added another hook rather than reusing the existing one. All three are active and deliver successfully → every push fans out 3×.(The server startup log also surfaces the auto-registration path, e.g.
webhooks: ... manual_webhooks:N auto_webhooks:M.)Expected
Registration should be idempotent:
GET /repos/{owner}/{repo}/hooksand skip (orPATCH) if a hook already targets the same cix webhook URL.Why it matters
workspace_repos→git_repos+workspace_projects): the startup WARN advises re-registering old-format hooks, but if re-registration doesn't dedupe, a repo ends up with old and new and repeated hooks instead of a single migrated one.Suggested fix
config.url; reuse orPATCHinstead of creating a new one.