Conversation
Auto-registration always POSTed a new hook instead of checking whether one with the same delivery URL already existed, so re-adds, reindexes, and restarts accumulated duplicate hooks on a repo — GitHub then fanned every push out N times. Add githubapi.ListWebhooks + EnsureWebhook: list existing hooks, match on config.url, PATCH the match (and prune same-URL duplicates) instead of creating, and only POST when none match. Wire both registration paths (tryAutoRegisterWebhook, reconciler.reconcileOne) through it. The reconciler now reuses an existing hook even when the stored WebhookID was lost, so a reregister sweep never leaves old + new hooks side by side. Covered by new githubapi tests (create / reuse / prune-duplicates / ignore-non-matching-URL) and a reconciler reuse test. Docs updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…doc caveat Address PR review: - When the matched hook is deleted between list and PATCH (404), the create-replacement branch now also prunes any other same-URL duplicates we listed, instead of leaving them behind. - WEBHOOKS.md §6: note that a reconcile sweep does NOT prune pre-existing same-URL duplicates when the stored hook id is still valid (it PATCHes that one and returns); re-adding the repo collapses them via EnsureWebhook. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(server): make GitHub webhook registration idempotent (#68)
Self-hosting cix behind an authenticating reverse proxy / Zero-Trust
gateway (Cloudflare Access, oauth2-proxy, Authelia) blocks the CLI and
AI-agent tooling at the edge: they send only the cix Bearer and have no
way to satisfy the proxy off-VPN, so they get a 302/403 before reaching
cix. The browser dashboard passes via interactive SSO and is unaffected.
Add opt-in per-server custom headers attached to every outbound request
(including the previously header-less /health probe) in addition to the
cix Bearer — e.g. a Cloudflare Access service token. The proxy validates
and strips them at the edge, so cix needs no knowledge of the proxy.
No server-side changes.
- config: ServerEntry.Headers (sensitive, omitempty) + SetServerHeader /
UnsetServerHeader + validateHeader (RFC 7230 token names, anti-CRLF).
- CLI: `cix config set/unset server.<name>.header.<Name>`; values never
echoed; `cix config show` and TUI surface a count only.
- client: SetCustomHeaders + applyCustomHeaders, applied in do(), Health()
and the streaming path, always BEFORE cix-managed headers so a stray
config can't clobber Authorization.
- getClient: ${ENV}-expand values into a throwaway copy (never written
back), validate after expansion, fail without echoing the value.
- docs: CLI_CONFIG.md section + SECURITY_DEPLOYMENT.md cross-link.
Tests: config round-trip/validation, client apply + Authorization
precedence, and an e2e getClient test proving ${ENV} reaches the wire
while the config file keeps the placeholder.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address two footguns in the ${ENV} expansion of custom request headers,
both found in review of #59:
1. An unset (typo'd / unexported) variable used to expand to "" via
os.ExpandEnv and be sent as an empty header — bouncing at the proxy
with an opaque 403 and no hint. Now ExpandEnvHeaderValue treats a
referenced-but-unset variable as a hard error that NAMES the variable
(never the value). A set-but-empty var is still honored as intentional.
2. os.ExpandEnv mangled a literal `$` in a value (e.g. `pa$$word` →
`pa`). The new expander supports `$$` as an escape for a literal `$`,
so values containing `$` survive intact.
Also documents header-name canonicalization (CF- → Cf-, harmless since
HTTP header names are case-insensitive) in CLI_CONFIG.md.
Tests: ExpandEnvHeaderValue table (escapes, set/empty/unset) and a
getClient test proving an unset header env var fails loudly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(cli): support custom HTTP request headers per server (#59)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes the changes currently on
developtomain.Included
/health) so the CLI/agent tooling can pass an authenticating reverse proxy / Zero-Trust gateway (e.g. a Cloudflare Access service token) in front of cix.${ENV}expansion at request time (strict — unset var is a hard error,$$escapes a literal$), values never persisted or printed, cix-managed headers always win, anti-CRLF + RFC 7230 token validation. CLI-only, no server changes. (PR feat(cli): support custom HTTP request headers per server (#59) #72)EnsureWebhookno longer creates duplicates on the race-create path; prunes duplicates and documents the caveat. (PR fix(server): make GitHub webhook registration idempotent (#68) #71)Verification
go build ./...,go vet ./...,go test ./...green on the CLI module; both source PRs reviewed and merged intodevelop.🤖 Generated with Claude Code