2025-09-02 - 2026-09-02
Overview
109 Pull requests merged by 2 users
Merged
#302 State the UUID-is-the-credential rule as a rule (closes #301)
Merged
#111 Milestone 1.0.0: internet-facing readiness
Merged
#300 Render unknown for a zero CreatedAt in Slack/Mattermost messages (closes #298)
Merged
#297 Carry the event's receipt time into every delivery (closes #257)
Merged
#296 Correct four documentation claims ahead of the 1.0.0 tag
Merged
#292 Close the two remaining delivery terminal-state gaps (closes #107)
Merged
#287 Create every SQLite file 0600 (closes #255)
Merged
#289 Fail loudly on an unparseable SENTRY_DSN and a malformed .env (closes #283)
Merged
#286 Derive the source detail BaseURL scheme from reqtls (closes #272)
Merged
#263 Make SQLite durable under concurrent readers and stop re-delivering stranded webhooks (closes #256)
Merged
#284 Record landed 1.0.0 work and drop the removed signing roadmap in TODO.md
Merged
#277 Bind the app port deliberately and document the proxy deployment (closes #268)
Merged
#281 Remove inbound request signature verification (closes #279)
Merged
#260 Stamp the build version into the binary (closes #253)
Merged
#276 Decide request TLS in one place, per request (closes #269)
Merged
#273 Check every statement in the webhook deletion transaction (closes #262)
Merged
#266 Name a deleted target on its historical deliveries (closes #211)
Merged
#264 Bound the /metrics method label (closes #261)
Merged
#259 Validate max_retries on both target forms (closes #221)
Merged
#258 Label HTTP metrics with the chi route pattern (closes #254)
Merged
#251 Resubmit a stored event as a new undelivered event (closes #250)
Merged
#249 Record the completed 1.0.0 milestone in TODO.md
Merged
#242 Harden operator-set target headers (closes #233)
Merged
#217 Add an egress CIDR allowlist to the SSRF guard (closes #204)
Merged
#219 Render delivery attempt detail in the event log (closes #202)
Merged
#240 Add per-delivery replay to the event log (closes #203)
Merged
#239 Add a webhooker resetpw subcommand and a bootstrap banner (closes #208)
Merged
#228 Add optional inbound webhook signature verification (closes #67)
Merged
#223 Stop target credentials leaking into event databases (closes #206)
Merged
#237 Read queue depths with Find, not Scan (closes #234)
Merged
#229 Add a target edit form and reachable header/timeout fields (closes #127)
Merged
#220 Lock DATA_DIR against a second instance (closes #201)
Merged
#222 Log SQL with placeholders, never bound values (closes #207)
Merged
#224 Expose delivery metrics on /metrics (closes #209)
Merged
#218 Exit non-zero when the HTTP listener fails (closes #200)
Merged
#216 Fail startup on half-set metrics credentials (closes #205)
Merged
#214 Document backup, restore and upgrade procedures (closes #210)
Merged
#213 Record the reopened 1.0.0 milestone in TODO.md
Merged
#199 Record the 1.0.0 milestone as complete in TODO.md
Merged
#195 Raise script/test's per-package timeout to 90s (closes #194)
Merged
#197 Re-sync REPO_POLICIES.md from prompts (closes #196)
Merged
#189 Report handler panics through the logger and answer 500 (closes #187)
Merged
#182 Route GORM's logger through slog and bound it (closes #178)
Merged
#180 Bound every slog line against client-chosen text (closes #176)
Merged
#188 Stop a slow host turning a login-guard test into a segfault (closes #186)
Merged
#192 Correct TODO.md milestone state and record seventeen landed units
Merged
#181 Send the chi route pattern to Sentry, not the concrete path (closes #179)
Merged
#174 Read form fields from the POST body only (closes #160)
Merged
#171 Verify login credentials before spending rate-limit budget (closes #150)
Merged
#165 Run all linting in Docker via Dockerfile.lint (closes #109)
Merged
#167 Serve an event's full stored body over HTTP (closes #157)
Merged
#155 Bound the access log line against client-chosen text (closes #146)
Merged
#161 Mark superseded commits honestly instead of skipped (closes #152)
Merged
#159 Set fx.StopTimeout inside the container stop grace (closes #134)
Merged
#162 Bucket IPv6 rate-limit keys by /64 (closes #125)
Merged
#156 Correct release-blocking README and startup-warning inaccuracies (closes #151)
Merged
#164 Fetch Alpine.js at build time under a verified hash (closes #145)
Merged
#158 Bound the event log's rendered bodies in the query (closes #135)
Merged
#126 Mask the http target's destination URL in the UI (closes #115)
Merged
#130 Bound shutdown hooks by their stop context (closes #102)
Merged
#131 Render templates via a buffer, not the ResponseWriter (closes #123)
Merged
#132 Align session codec max-age with the 7-day cap (closes #108)
Merged
#153 Warn when production shares one rate-limit bucket (closes #149)
Merged
#148 Record the last four milestone units in TODO.md
Merged
#143 Bound the receiver rate limit per client IP across /webhook/* (closes #139)
Merged
#144 Correct release-blocking documentation inaccuracies (closes #141)
Merged
#138 Make the CI gate execute the checks it reports on (closes #119)
Merged
#142 Require a positive RETENTION_SWEEP_INTERVAL (closes #140)
Merged
#137 Update TODO.md for the completed 1.0.0 milestone
Merged
#136 Scan the X-Forwarded-For chain without splitting it (closes #133)
Merged
#129 Cap the X-Forwarded-For hop walk at 64 entries (closes #124)
Merged
#122 Gate forwarded-header trust behind trusted-proxy config (closes #88)
Merged
#116 Clarify web UI terminology, copy, and the entrypoint URL (closes #57)
Merged
#121 Mask the target URL in delivery errors, SSRF logs and log page data (closes #118)
Merged
#87 Rate-limit the public webhook receiver endpoint (closes #64)
Merged
#91 Enforce the body size limit before CSRF parses the form (closes #90)
Merged
#114 Mask target config on the source detail page (closes #113)
Merged
#96 Allow retention_days of 0 to mean retain forever (closes #79)
Merged
#105 Add inactivity-based session timeout (closes #66)
Merged
#92 Fail loudly on set-but-unparseable env config values (closes #80)
Merged
#104 Terminally fail retrying deliveries with a non-retry target type (closes #82)
Merged
#95 Evict archive writers on deletion and sweep idle archives (closes #89)
Merged
#100 Root background loops at context.Background() (closes #97)
Merged
#83 Add admin password change flow (closes #65)
Merged
#86 Update golangci-lint to v2.12.2 with canonical config
Merged
#84 Implement the database archiving target (closes #43)
Merged
#81 Refactor delivery targets to a Target interface (closes #77)
Merged
#78 Add per-webhook event retention reaper (closes #63)
Merged
#75 Add NoCache middleware for authenticated pages (closes #61)
Merged
#73 Validate Slack target URLs at creation time (closes #68)
Merged
#74 Keep the SSRF-safe transport in clientForConfig (closes #69)
Merged
#71 Wrap /user/{username} in RequireAuth middleware (closes #60)
Merged
#72 Raise HTTP WriteTimeout above the request middleware timeout (closes #62)
Merged
#59 scripts-to-rule-them-all
Merged
#58 Restore TODO.md and move TODO content out of README
Merged
#55 refactor: use pinned golangci-lint Docker image for linting
Merged
#54 fix: detect TLS per-request in CSRF middleware to fix login
Merged
#52 feat: redirect root path based on auth state
Merged
#46 fix: use absolute path for dev DATA_DIR default, clarify env docs
Merged
#49 fix: use absolute paths and static linking in Dockerfile
Merged
#42 feat: add CSRF protection, SSRF prevention, and login rate limiting
Merged
#47 feat: add Slack target type for incoming webhook notifications
Merged
#41 security: add headers middleware, session regeneration, and body size limits
Merged
#31 Remove globals.Buildarch from codebase
Merged
#32 test: add tests for delivery, middleware, and session packages
Merged
#16 feat: webhooker 1.0 MVP — entity rename, core engine, delivery, management UI
Merged
#13 docs: comprehensive README rewrite with complete service specification
Merged
#14 Replace Bootstrap with Tailwind CSS + Alpine.js
Merged
#6 feat: bring repo up to REPO_POLICIES standards
133 Issues closed from 2 users
Closed
#304 SQLite concurrency: cache=shared, no busy_timeout, no WAL, unbounded pool — concurrent writers get SQLITE_BUSY, deliveries stick pending
Closed
#301 Document that the entrypoint UUID is the authentication and shared secrets are never used
Closed
#147 CI gate follow-ups: script/cibuild has drifted from the model script, and the status-rewrite context string is hardcoded
Closed
#298 The reaped-row fallback renders the exact zero timestamp that #257 was filed against
Closed
#257 Every Slack message shows a zero timestamp of 0001-01-01T00:00:00Z
Closed
#107 Two remaining delivery terminal-state gaps: opaque failure and deletion-orphaned retries
Closed
#255 SQLite files are created 0644 with plaintext credentials, and the documented Docker deployment supplies the parent directory 0755
Closed
#283 Two config paths fail silently: an unparseable SENTRY_DSN starts anyway, and a malformed .env is discarded whole
Closed
#272 BaseURL on the source detail page uses the raw X-Forwarded-Proto value as the URL scheme
Closed
#256 A concurrent reader wedges the per-webhook database, stranding delivered webhooks at pending and re-delivering them on restart
Closed
#226 SIGTERM during startup panics on a nil httpServer, and cleanShutdown reads two fields with no happens-before edge
Closed
#268 The plaintext app port binds all interfaces and the README has no reverse-proxy deployment section
Closed
#241 README understates who can replay a signed request: forwarding X-Hub-Signature-256 hands every target a valid (body, signature) pair
Closed
#279 Remove inbound request signature verification: the entrypoint UUID is the authentication secret
Closed
#253 The shipped binary reports version "dev": no -X ldflags in the Makefile or Dockerfile
Closed
#269 The session cookie silently loses Secure in the default environment, and X-Forwarded-Proto parsing is exact-match
Closed
#262 Webhook deletion commits a partial delete and reports success when a delete statement fails
Closed
#211 Deleting a target blanks its name on every historical delivery in the event log
Closed
#261 The /metrics method label is still unbounded: 300 requests with random method tokens mint 7,525 permanent series
Closed
#221 max_retries is unbounded at target creation, and unparseable input silently becomes 0
Closed
#254 Enabling /metrics lets an unauthenticated client grow the process without bound, and publishes live entrypoint UUIDs
Closed
#247 Invalid target header name error no longer indicates which line was rejected
Closed
#250 Resubmit a stored event as a new undelivered event, so backends can be tested against real captured traffic
Closed
#243 Forwarded inbound event headers are not stripped on a cross-origin redirect, unlike operator-configured ones
Closed
#233 Operator-set target headers: credential headers survive a cross-host redirect, Trailer is not reserved, and an invalid header name error can quote a secret
Closed
#204 The SSRF blocklist has no escape hatch, so a self-hosted proxy cannot forward to your own network
Closed
#236 next ships a target edit form whose Tailwind classes are missing from the committed CSS
Closed
#202 Delivery failures are invisible in the UI: nothing renders status_code, response_body, error or attempt_num
Closed
#203 No replay: a delivery that exhausts max_retries is failed forever, and store-and-forward is the point
Closed
#208 No admin password recovery path: lose the one-time bootstrap line and the deployment is unreachable
Closed
#67 Optional inbound webhook HMAC signature verification
Closed
#206 Target credentials leak into the per-webhook event databases via GORM association upsert
Closed
#234 next is red: queue_depth.go calls (*gorm.DB).Scan, which interpolates bound values into logged SQL
Closed
#235 next is red: queue_depth.go calls (*gorm.DB).Scan, tripping the scan guard
Closed
#127 No target edit form: a target's destination URL, headers and timeout are write-once
Closed
#201 No lock on DATA_DIR: two instances both run delivery recovery and both deliver
Closed
#207 DEBUG=true prints the session encryption key and the admin password hash to the log
Closed
#209 No delivery metrics: /metrics cannot tell you whether the product is doing its job
Closed
#200 A failed listen leaves a live, non-serving process that fx still reports RUNNING
Closed
#205 METRICS_USERNAME set with an empty METRICS_PASSWORD publishes /metrics unauthenticated
Closed
#210 No backup, restore or upgrade guidance anywhere in the README
Closed
#194 internal/handlers runs at 22s against script/test's 30s per-package timeout, and has already reded a build under load
Closed
#196 REPO_POLICIES.md is a stale copy: it mandates a 30s test timeout the org policy replaced with 90s
Closed
#187 chi's Recoverer panics instead of handling a handler panic, so a panicking request drops the connection instead of answering 500
Closed
#178 GORM's default logger prints the full interpolated SQL, including the client-chosen path and username, on every record-not-found
Closed
#176 MaxBodySize logs the full client-chosen path ahead of RequireAuth, sidestepping the access-log line budget
Closed
#186 A timing-sensitive login-guard test panics on a nil release and reds the whole internal/middleware package
Closed
#179 The receiver's capability URL is sent to Sentry in full
Closed
#160 Target create reads the destination URL via r.FormValue, so a query-string credential lands in the access log and Sentry
Closed
#150 Decision: should login rate limiting survive a shared bucket, or should TRUSTED_PROXIES be mandatory in production?
Closed
#106 script/lint uses the host golangci-lint and a shared cache, so lint results can be wrong in either direction
Closed
#109 Run all linting in Docker via Dockerfile.lint + script/lint
Closed
#157 No in-app way to retrieve an event body larger than the event log's 8 KB render cap
Closed
#146 The access log writes one INFO line with the full attacker-controlled URL per request, including rejected ones
Closed
#152 Superseded-run status laundering: a never-tested commit reads green in the combined status
Closed
#134 fx.StopTimeout is never set, so the bounded-shutdown fix does not fire under a default docker stop
Closed
#125 Decision: should IPv6 rate-limit keys bucket by /64 rather than per-address?
Closed
#151 Release-blocking accuracy: README describes a build and an endpoint that do not exist, and the lockout warning misses the default environment
Closed
#145 A minified vendored bundle is committed to the repo, which REPO_POLICIES forbids
Closed
#135 The event-log page renders stored bodies untruncated, so buffered rendering can hold ~25 MB per request
Closed
#115 HTTP target destination URL is rendered in full and can itself be a bearer credential
Closed
#102 Shutdown hooks ignore their context and wg.Wait() unbounded
Closed
#123 renderTemplate streams to the ResponseWriter, so a mid-render template error ships a partial page
Closed
#108 Session codec max-age still 30 days: bring securecookie codecs in line with the 7-day cap
Closed
#149 Default config allows a remote stranger to lock the operator out of the admin UI, and nothing warns about it
Closed
#139 The receiver rate limit is per-URL-path, so an attacker gets unlimited aggregate rate against /webhook/*
Closed
#141 Release-readiness accuracy: docs contradict the code, TODO.md omits landed units, debug logging in the production asset
Closed
#119 CI can report success without running lint or tests, because script/cibuild replays a cached image
Closed
#140 RETENTION_SWEEP_INTERVAL is not range-checked, so a non-positive value panics two goroutines after startup reports success
Closed
#133 The hop cap bounds the walk but not the Split: a 1 MB X-Forwarded-For still allocates ~8 MB per request
Closed
#124 Cap the X-Forwarded-For hop walk: an unbounded chain burns CPU on every request
Closed
#88 Gate forwarded-header trust behind trusted-proxy config in rate limiters
Closed
#57 Clean up and clarify the web UI
Closed
#118 Slack webhook credential still leaks via delivery errors, SSRF logs, and the source_logs template data
Closed
#64 Rate-limit the public webhook receiver endpoint
Closed
#90 Enforce request body size limit before CSRF middleware parses the form
Closed
#113 Source detail page renders raw target config, exposing the Slack webhook URL credential
Closed
#79 RetentionDays cannot be set to 0 (retain forever) via the normal create path
Closed
#110 TOP PRIORITY: consolidate all open PRs onto one next branch, one PR
Closed
#66 Add inactivity-based session timeout
Closed
#80 Config parsing should fail loudly on set-but-unparseable env values (envInt, etc.)
Closed
#82 Recovery skips orphaned retrying deliveries whose target type changed to a non-retry type
Closed
#89 Archive writer lifecycle: evict writers on webhook deletion and sweep idle archives
Closed
#97 CRITICAL: delivery engine and retention reaper both die ~15s after startup (fx OnStart context)
Closed
#65 Add an admin password change flow in the web UI
Closed
#43 configure db target type for archiving
Closed
#77 Refactor delivery targets to a Target interface
Closed
#63 Enforce per-webhook event retention (RetentionDays reaper)
Closed
#61 Set Cache-Control: no-store on authenticated pages
Closed
#70 Implement the log delivery target
Closed
#68 Validate Slack target URLs at creation time (SSRF parity with HTTP targets)
Closed
#69 Keep the SSRF-safe Transport in clientForConfig when a per-target timeout is set
Closed
#60 Wrap the /user/{username} route in RequireAuth middleware
Closed
#62 Reconcile HTTP WriteTimeout with the request middleware timeout
Closed
#50 dockerfile for linting is doing it wrong
Closed
#53 can't log in - csrf
Closed
#51 / should redirect to the login page if not logged in, should redirect to the dashboard/list/index page if logged in
Closed
#45 README unclear
Closed
#48 container is broken
Closed
#35 [security] Implement CSRF protection for all forms
Closed
#36 [security] Add SSRF prevention for HTTP delivery targets
Closed
#37 [security] Add rate limiting on login endpoint
Closed
#44 slack target type
Closed
#39 [security] Add request body size limits on form endpoints
Closed
#34 [security] Add production security headers middleware
Closed
#38 [security] Fix session fixation: regenerate session on login
Closed
#40 [security] Fix admin password bootstrap logging
Closed
#30 remove all globals.Buildarch
Closed
#28 Very low test coverage on most packages (delivery 0%, handlers 5.7%, middleware 0%, session 0%)
Closed
#5 initial scaffolding
Closed
#7 Templates should use go:embed instead of filesystem parsing at request time
Closed
#8 Logger should use slog.LevelVar for dynamic level switching
Closed
#9 Source management routes lack authentication middleware
Closed
#10 Configuration should prefer environment variables per CODE_STYLEGUIDE
Closed
#11 Redundant godotenv/autoload import in server.go
Closed
#12 Rename "Processor" entity to "Webhook" and "Webhook" to "Entrypoint" throughout codebase
Closed
#15 1.0/mvp
Closed
#17 BLOCKER: Delivery engine crashes with nil pointer dereference on startup
Closed
#18 BLOCKER: All template-rendered pages return empty content
Closed
#19 BLOCKER: DevSessionKey constant is wrong length (35 bytes, need 32)
Closed
#20 Webhook endpoint accepts all HTTP methods, should restrict to POST
Closed
#21 Server calls cleanShutdown() twice causing potential errors
Closed
#22 DevAdminUsername/DevAdminPassword config fields are dead code
Closed
#23 CORS allows wildcard origin (*) - placeholder left from template
Closed
#24 Webhook deletion does not clean up deliveries and delivery results
Closed
#25 No UI to deactivate/delete individual entrypoints or targets
Closed
#27 Config loading logs spurious "Failed to load config" at Info level
Closed
#29 µPaaS deployment setup
Closed
#26 Admin password logged in structured JSON at Info level - credential leak risk
Closed
#3 rewrite README.md into an actual and complete service description and spec
Closed
#4 Replace Bootstrap with Tailwind CSS + Alpine.js (match µPaaS UI)
Closed
#1 bring repo up to standards
Closed
#2 Fix Dockerfile and add CI workflow
200 Issues created by 2 users
Opened
#1 bring repo up to standards
Opened
#2 Fix Dockerfile and add CI workflow
Opened
#3 rewrite README.md into an actual and complete service description and spec
Opened
#4 Replace Bootstrap with Tailwind CSS + Alpine.js (match µPaaS UI)
Opened
#5 initial scaffolding
Opened
#7 Templates should use go:embed instead of filesystem parsing at request time
Opened
#8 Logger should use slog.LevelVar for dynamic level switching
Opened
#9 Source management routes lack authentication middleware
Opened
#10 Configuration should prefer environment variables per CODE_STYLEGUIDE
Opened
#11 Redundant godotenv/autoload import in server.go
Opened
#12 Rename "Processor" entity to "Webhook" and "Webhook" to "Entrypoint" throughout codebase
Opened
#15 1.0/mvp
Opened
#17 BLOCKER: Delivery engine crashes with nil pointer dereference on startup
Opened
#18 BLOCKER: All template-rendered pages return empty content
Opened
#19 BLOCKER: DevSessionKey constant is wrong length (35 bytes, need 32)
Opened
#20 Webhook endpoint accepts all HTTP methods, should restrict to POST
Opened
#21 Server calls cleanShutdown() twice causing potential errors
Opened
#22 DevAdminUsername/DevAdminPassword config fields are dead code
Opened
#23 CORS allows wildcard origin (*) - placeholder left from template
Opened
#24 Webhook deletion does not clean up deliveries and delivery results
Opened
#25 No UI to deactivate/delete individual entrypoints or targets
Opened
#26 Admin password logged in structured JSON at Info level - credential leak risk
Opened
#27 Config loading logs spurious "Failed to load config" at Info level
Opened
#28 Very low test coverage on most packages (delivery 0%, handlers 5.7%, middleware 0%, session 0%)
Opened
#29 µPaaS deployment setup
Opened
#30 remove all globals.Buildarch
Opened
#33 1.0/mvp
Opened
#34 [security] Add production security headers middleware
Opened
#35 [security] Implement CSRF protection for all forms
Opened
#36 [security] Add SSRF prevention for HTTP delivery targets
Opened
#37 [security] Add rate limiting on login endpoint
Opened
#38 [security] Fix session fixation: regenerate session on login
Opened
#39 [security] Add request body size limits on form endpoints
Opened
#40 [security] Fix admin password bootstrap logging
Opened
#43 configure db target type for archiving
Opened
#44 slack target type
Opened
#45 README unclear
Opened
#48 container is broken
Opened
#50 dockerfile for linting is doing it wrong
Opened
#51 / should redirect to the login page if not logged in, should redirect to the dashboard/list/index page if logged in
Opened
#53 can't log in - csrf
Opened
#56 Move schema_migrations table creation into 000.sql with INTEGER version column
Opened
#57 Clean up and clarify the web UI
Opened
#60 Wrap the /user/{username} route in RequireAuth middleware
Opened
#61 Set Cache-Control: no-store on authenticated pages
Opened
#62 Reconcile HTTP WriteTimeout with the request middleware timeout
Opened
#63 Enforce per-webhook event retention (RetentionDays reaper)
Opened
#64 Rate-limit the public webhook receiver endpoint
Opened
#65 Add an admin password change flow in the web UI
Opened
#66 Add inactivity-based session timeout
Opened
#67 Optional inbound webhook HMAC signature verification
Opened
#68 Validate Slack target URLs at creation time (SSRF parity with HTTP targets)
Opened
#69 Keep the SSRF-safe Transport in clientForConfig when a per-target timeout is set
Opened
#70 Implement the log delivery target
Opened
#77 Refactor delivery targets to a Target interface
Opened
#79 RetentionDays cannot be set to 0 (retain forever) via the normal create path
Opened
#80 Config parsing should fail loudly on set-but-unparseable env values (envInt, etc.)
Opened
#82 Recovery skips orphaned retrying deliveries whose target type changed to a non-retry type
Opened
#85 Design: should delivery targets own their recovery/sweep loop, not just backoff?
Opened
#88 Gate forwarded-header trust behind trusted-proxy config in rate limiters
Opened
#89 Archive writer lifecycle: evict writers on webhook deletion and sweep idle archives
Opened
#90 Enforce request body size limit before CSRF middleware parses the form
Opened
#93 Follow-ups from the PR #91 review: body-limit docs and route-ordering test coverage
Opened
#94 Follow-ups from the PR #92 review: config test hygiene and env docs
Opened
#97 CRITICAL: delivery engine and retention reaper both die ~15s after startup (fx OnStart context)
Opened
#98 Deprecated gomodguard linter in the org-standard .golangci.yml (needs an upstream decision)
Opened
#99 Follow-ups from the PR #96 re-review: retention bound docs and untested normalisation
Opened
#101 Archive sweep repeats open/prune/close once per database target instead of once per webhook
Opened
#102 Shutdown hooks ignore their context and wg.Wait() unbounded
Opened
#103 Pin the pre-reopen close in archiveWriter.sweepExpired (unprotected connection-leak guard)
Opened
#106 script/lint uses the host golangci-lint and a shared cache, so lint results can be wrong in either direction
Opened
#107 Two remaining delivery terminal-state gaps: opaque failure and deletion-orphaned retries
Opened
#108 Session codec max-age still 30 days: bring securecookie codecs in line with the 7-day cap
Opened
#109 Run all linting in Docker via Dockerfile.lint + script/lint
Opened
#110 TOP PRIORITY: consolidate all open PRs onto one next branch, one PR
Opened
#112 Decision: TODO.md Completed Steps guarantees a merge conflict on every unit landing on next
Opened
#113 Source detail page renders raw target config, exposing the Slack webhook URL credential
Opened
#115 HTTP target destination URL is rendered in full and can itself be a bearer credential
Opened
#117 Page {{define "title"}} blocks never render; every browser tab reads "Webhooker"
Opened
#118 Slack webhook credential still leaks via delivery errors, SSRF logs, and the source_logs template data
Opened
#119 CI can report success without running lint or tests, because script/cibuild replays a cached image
Opened
#120 No JS linting, so the styleguide REPO_POLICIES binds us to is unenforced
Opened
#123 renderTemplate streams to the ResponseWriter, so a mid-render template error ships a partial page
Opened
#124 Cap the X-Forwarded-For hop walk: an unbounded chain burns CPU on every request
Opened
#125 Decision: should IPv6 rate-limit keys bucket by /64 rather than per-address?
Opened
#127 No target edit form: a target's destination URL, headers and timeout are write-once
Opened
#128 Form-error paths commit a 4xx before rendering, so a failed render there cannot report 500
Opened
#133 The hop cap bounds the walk but not the Split: a 1 MB X-Forwarded-For still allocates ~8 MB per request
Opened
#134 fx.StopTimeout is never set, so the bounded-shutdown fix does not fire under a default docker stop
Opened
#135 The event-log page renders stored bodies untruncated, so buffered rendering can hold ~25 MB per request
Opened
#139 The receiver rate limit is per-URL-path, so an attacker gets unlimited aggregate rate against /webhook/*
Opened
#140 RETENTION_SWEEP_INTERVAL is not range-checked, so a non-positive value panics two goroutines after startup reports success
Opened
#141 Release-readiness accuracy: docs contradict the code, TODO.md omits landed units, debug logging in the production asset
Opened
#145 A minified vendored bundle is committed to the repo, which REPO_POLICIES forbids
Opened
#146 The access log writes one INFO line with the full attacker-controlled URL per request, including rejected ones
Opened
#147 CI gate follow-ups: script/cibuild has drifted from the model script, and the status-rewrite context string is hardcoded
Opened
#149 Default config allows a remote stranger to lock the operator out of the admin UI, and nothing warns about it
Opened
#150 Decision: should login rate limiting survive a shared bucket, or should TRUSTED_PROXIES be mandatory in production?
Opened
#151 Release-blocking accuracy: README describes a build and an endpoint that do not exist, and the lockout warning misses the default environment
Opened
#152 Superseded-run status laundering: a never-tested commit reads green in the combined status
Opened
#154 middleware tests construct cookie stores by hand, so they no longer match the production store
Opened
#157 No in-app way to retrieve an event body larger than the event log's 8 KB render cap
Opened
#160 Target create reads the destination URL via r.FormValue, so a query-string credential lands in the access log and Sentry
Opened
#163 Consider dropping Alpine.js and writing its 19 directive uses directly
Opened
#166 static.go embeds the js directory, so a missing fetched asset silently produces a binary with no Alpine
Opened
#168 Rate-limit key collapses every peer with an empty RemoteAddr into one bucket
Opened
#169 Restrict /s/* to GET and HEAD — it currently answers POST, PUT and DELETE with 200
Opened
#170 Clamp the HTTP drain by the tail-hook reserve, not just the Sentry flush
Opened
#172 Assert the access-log encoded-byte charge against every Unicode code point
Opened
#173 The receiver's 1 MB body cap lives in the handler, not the middleware the other route groups use
Opened
#175 The CI workflow step is named "runs make check", which the image has never done
Opened
#176 MaxBodySize logs the full client-chosen path ahead of RequireAuth, sidestepping the access-log line budget
Opened
#177 Target.Webhook and Webhook.Targets form a reference cycle that will recurse once anything marshals a preloaded model
Opened
#178 GORM's default logger prints the full interpolated SQL, including the client-chosen path and username, on every record-not-found
Opened
#179 The receiver's capability URL is sent to Sentry in full
Opened
#183 Two writers bypass internal/logger: fx's console logger and the Go runtime
Opened
#184 Nothing bounds username length, and an account with a multi-KB username cannot log in at all
Opened
#185 If tracing is ever enabled, every Sentry transaction will collapse into one bucket named POST /(redacted)
Opened
#186 A timing-sensitive login-guard test panics on a nil release and reds the whole internal/middleware package
Opened
#187 chi's Recoverer panics instead of handling a handler panic, so a panicking request drops the connection instead of answering 500
Opened
#190 Last wall-clock-dependent test: two writes must land inside a real 2s debounce window or correct code reds
Opened
#191 loggingResponseWriter has no Unwrap, so http.ResponseController cannot reach the real writer through the shipped chain
Opened
#193 Headers set before an uncommitted panic survive onto the recovered 500, including Set-Cookie
Opened
#194 internal/handlers runs at 22s against script/test's 30s per-package timeout, and has already reded a build under load
Opened
#196 REPO_POLICIES.md is a stale copy: it mandates a 30s test timeout the org policy replaced with 90s
Opened
#198 make test is past the org 20s target and arguably past the 60s hard cap, driven almost entirely by internal/handlers
Opened
#200 A failed listen leaves a live, non-serving process that fx still reports RUNNING
Opened
#201 No lock on DATA_DIR: two instances both run delivery recovery and both deliver
Opened
#202 Delivery failures are invisible in the UI: nothing renders status_code, response_body, error or attempt_num
Opened
#203 No replay: a delivery that exhausts max_retries is failed forever, and store-and-forward is the point
Opened
#204 The SSRF blocklist has no escape hatch, so a self-hosted proxy cannot forward to your own network
Opened
#205 METRICS_USERNAME set with an empty METRICS_PASSWORD publishes /metrics unauthenticated
Opened
#206 Target credentials leak into the per-webhook event databases via GORM association upsert
Opened
#207 DEBUG=true prints the session encryption key and the admin password hash to the log
Opened
#208 No admin password recovery path: lose the one-time bootstrap line and the deployment is unreachable
Opened
#209 No delivery metrics: /metrics cannot tell you whether the product is doing its job
Opened
#210 No backup, restore or upgrade guidance anywhere in the README
Opened
#211 Deleting a target blanks its name on every historical delivery in the event log
Opened
#212 Encrypt credential-bearing target config at rest in webhooker.db
Opened
#215 make fmt does not format Markdown, so the org prettier settings are unenforced here
Opened
#221 max_retries is unbounded at target creation, and unparseable input silently becomes 0
Opened
#225 internal/handlers tests are load-fragile: both the fx start budget and the 90s package timeout blow under host load, so the gate reports failures unrelated to the change
Opened
#226 SIGTERM during startup panics on a nil httpServer, and cleanShutdown reads two fields with no happens-before edge
Opened
#227 go test -count=2 ./internal/server/... panics on duplicate metrics collector registration
Opened
#230 Data race in internal/handlers/logbound_test.go: fx testutil.WriteSyncer calls t.Logf from a start hook after the test goroutine has finished
Opened
#231 tailwindcss is unpinned, so the committed static/css/tailwind.css is not reproducible from the repo's own tooling
Opened
#232 Harden the (*gorm.DB).Scan guard test: three evasions, an overstated doc comment, and a weak file-count floor
Opened
#233 Operator-set target headers: credential headers survive a cross-host redirect, Trailer is not reserved, and an invalid header name error can quote a secret
Opened
#234 next is red: queue_depth.go calls (*gorm.DB).Scan, which interpolates bound values into logged SQL
Opened
#235 next is red: queue_depth.go calls (*gorm.DB).Scan, tripping the scan guard
Opened
#236 next ships a target edit form whose Tailwind classes are missing from the committed CSS
Opened
#238 The event-DB sweep marker is monotonic and trusts itself, so a downgrade re-leaks target rows permanently
Opened
#241 README understates who can replay a signed request: forwarding X-Hub-Signature-256 hands every target a valid (body, signature) pair
Opened
#243 Forwarded inbound event headers are not stripped on a cross-origin redirect, unlike operator-configured ones
Opened
#244 blockedNetworks does not cover in-cloud provider service networks on public unicast, e.g. IBM Cloud 161.26.0.0/16 and 166.8.0.0/14
Opened
#245 Default-block the public-unicast cloud metadata endpoints (Azure WireServer, Equinix Metal)
Opened
#246 A delivery can carry Content-Type twice when the inbound event also supplied one
Opened
#247 Invalid target header name error no longer indicates which line was rejected
Opened
#248 Migrate .golangci.yml from the deprecated gomodguard to gomodguard_v2
Opened
#250 Resubmit a stored event as a new undelivered event, so backends can be tested against real captured traffic
Opened
#252 Follow-ups from the PR 251 review: two comments state the wrong mechanism, and the resubmit route's middleware is untested
Opened
#253 The shipped binary reports version "dev": no -X ldflags in the Makefile or Dockerfile
Opened
#254 Enabling /metrics lets an unauthenticated client grow the process without bound, and publishes live entrypoint UUIDs
Opened
#255 SQLite files are created 0644 with plaintext credentials, and the documented Docker deployment supplies the parent directory 0755
Opened
#256 A concurrent reader wedges the per-webhook database, stranding delivered webhooks at pending and re-delivering them on restart
Opened
#257 Every Slack message shows a zero timestamp of 0001-01-01T00:00:00Z
Opened
#261 The /metrics method label is still unbounded: 300 requests with random method tokens mint 7,525 permanent series
Opened
#262 Webhook deletion commits a partial delete and reports success when a delete statement fails
Opened
#265 script/docker is no longer byte-identical to the model script, and CI-built images stamp version "unknown"
Opened
#267 webhooker_delivery_duration_seconds is absent from a scrape until the first delivery occurs
Opened
#268 The plaintext app port binds all interfaces and the README has no reverse-proxy deployment section
Opened
#269 The session cookie silently loses Secure in the default environment, and X-Forwarded-Proto parsing is exact-match
Opened
#270 The access log records the proxy's address, never the client's, so abuse cannot be traced from webhooker's own logs
Opened
#271 HSTS is a hardcoded 2-year includeSubDomains preload commitment with no opt-out and no documentation of its value
Opened
#272 BaseURL on the source detail page uses the raw X-Forwarded-Proto value as the URL scheme
Opened
#274 Split internal/handlers/source_management.go along its three CRUD seams
Opened
#275 DeleteDB returns on the first failing suffix, so it can report a leftover file when the history is already destroyed
Opened
#278 Changing the password neither regenerates the current session nor invalidates any other one
Opened
#279 Remove inbound request signature verification: the entrypoint UUID is the authentication secret
Opened
#280 Archive writers are never closed at shutdown, so their -wal survives a clean stop
Opened
#282 make check fails on a fresh clone because script/check does not fetch the vendored assets
Opened
#283 Two config paths fail silently: an unparseable SENTRY_DSN starts anyway, and a malformed .env is discarded whole
Opened
#285 The withRetry bookkeeping-failure branch is unpinned: removing its error check leaves the suite green
Opened
#288 Two constants define the data directory mode, in two packages that both create it
Opened
#290 File-sourced configuration paths have never been audited for silent defaults
Opened
#291 GetDB can open the same event database twice, and closing the loser drops the winner's file locks
Opened
#293 A pending delivery whose target was deleted is stranded forever
Opened
#294 TestProcessRetryTask_LargeBody_FetchFromDB stays green when the fetch it exists to exercise is deleted
Opened
#295 Observation, not a defect: one unreproducible SQLITE_IOERR_SHORT_READ storm under load on ZFS-backed storage
Opened
#298 The reaped-row fallback renders the exact zero timestamp that #257 was filed against
Opened
#299 Startup recovery races the receiver and can deliver a fresh event twice
Opened
#301 Document that the entrypoint UUID is the authentication and shared secrets are never used
Opened
#303 Independent code audit
Opened
#304 SQLite concurrency: cache=shared, no busy_timeout, no WAL, unbounded pool — concurrent writers get SQLITE_BUSY, deliveries stick pending
Opened
#305 Notify drops tasks on full channel; pending deliveries only recover on process restart
Opened
#306 Circuit breaker half-open state busy-loops queued tasks with a DB write per spin
Opened
#307 WEBHOOKER_ENVIRONMENT defaults to dev, silently shipping insecure prod (no Secure cookies, CORS *)
Opened
#308 Listener bind failure leaves the process running with no listener instead of exiting non-zero
Opened
#309 SSRF blocking has no opt-out: cannot deliver to RFC1918/LAN targets — needs ALLOWED_TARGET_CIDRS
Opened
#310 Replay/redelivery does not exist and the UI shows no per-attempt results, despite README promises