All checks were successful
check / check (push) Successful in 8s
## Summary This PR brings the webhooker repo into full REPO_POLICIES compliance, addressing both [issue #1](#1) and [issue #2](#2). ## Changes ### New files - **`cmd/webhooker/main.go`** — The missing application entry point. Uses Uber fx to wire together all internal packages (config, database, logger, server, handlers, middleware, healthcheck, globals, session). Minimal glue code. - **`REPO_POLICIES.md`** — Fetched from authoritative source (`sneak/prompts`) - **`.editorconfig`** — Fetched from authoritative source - **`.dockerignore`** — Sensible Go project exclusions - **`.gitea/workflows/check.yml`** — CI workflow that runs `docker build .` on push to any branch (Gitea Actions format, actions/checkout pinned by sha256) - **`configs/config.yaml.example`** — Moved from root `config.yaml` ### Modified files - **`Makefile`** — Complete rewrite with all REPO_POLICIES required targets: `test`, `lint`, `fmt`, `fmt-check`, `check`, `build`, `hooks`, `docker`, `clean`, plus `dev`, `run`, `deps` - **`Dockerfile`** — Complete rewrite: - Builder: `golang:1.24` (Debian-based, pinned by `sha256:d2d2bc1c84f7...`). Debian needed because `gorm.io/driver/sqlite` pulls `mattn/go-sqlite3` (CGO) which fails on Alpine musl. - golangci-lint v1.64.8 installed from GitHub release archive with sha256 verification (v1.x because `.golangci.yml` uses v1 config format) - Runs `make check` (fmt-check + lint + test + build) as build step - Final stage: `alpine:3.21` (pinned by `sha256:c3f8e73fdb79...`) with non-root user, healthcheck, port 8080 - **`README.md`** — Rewritten with all required REPO_POLICIES sections: description line with name/purpose/category/license/author, Getting Started, Rationale, Design, TODO (integrated from TODO.md), License, Author - **`.gitignore`** — Fixed `webhooker` pattern to `/webhooker` (was blocking `cmd/webhooker/`), added `config.yaml` to prevent committing runtime config with secrets - **`static/static.go`** — Removed `vendor` from embed directive (directory was empty/missing) - **`internal/database/database_test.go`** — Fixed to use in-memory config via `afero.MemMapFs` instead of depending on `config.yaml` on disk. Test is now properly isolated. - **`go.mod`/`go.sum`** — `go mod tidy` ### Removed files - **`TODO.md`** — Content integrated into README.md TODO section - **`config.yaml`** — Moved to `configs/config.yaml.example` ## Verification - `docker build .` passes (lint ✅, test ✅, build ✅) - All existing tests pass with no modifications to assertions or test logic - `.golangci.yml` untouched closes #1 closes #2 Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de> Reviewed-on: #6 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org>
190 lines
6.9 KiB
Markdown
190 lines
6.9 KiB
Markdown
# webhooker
|
|
|
|
webhooker is a Go web application by [@sneak](https://sneak.berlin) that
|
|
receives, stores, and proxies webhooks to configured targets with retry
|
|
support, observability, and a management web UI. License: pending.
|
|
|
|
## Getting Started
|
|
|
|
```bash
|
|
# Clone the repo
|
|
git clone https://git.eeqj.de/sneak/webhooker.git
|
|
cd webhooker
|
|
|
|
# Install dependencies
|
|
make deps
|
|
|
|
# Copy example config
|
|
cp configs/config.yaml.example config.yaml
|
|
|
|
# Run in development mode
|
|
make dev
|
|
|
|
# Run all checks (format, lint, test, build)
|
|
make check
|
|
|
|
# Build Docker image
|
|
make docker
|
|
```
|
|
|
|
### Environment Variables
|
|
|
|
- `WEBHOOKER_ENVIRONMENT` — `dev` or `prod` (default: `dev`)
|
|
- `DEBUG` — Enable debug logging
|
|
- `PORT` — Server port (default: `8080`)
|
|
- `DBURL` — Database connection string
|
|
- `SESSION_KEY` — Base64-encoded 32-byte session key (required in prod)
|
|
- `METRICS_USERNAME` — Username for metrics endpoint
|
|
- `METRICS_PASSWORD` — Password for metrics endpoint
|
|
- `SENTRY_DSN` — Sentry error reporting (optional)
|
|
|
|
## Rationale
|
|
|
|
Webhook integrations between services are fragile: the receiving service
|
|
must be up when the webhook fires, there is no built-in retry for most
|
|
webhook senders, and there is no visibility into what was sent or when.
|
|
webhooker solves this by acting as a reliable intermediary that receives
|
|
webhooks, stores them, and delivers them to configured targets — with
|
|
optional retries, logging, and Prometheus metrics for observability.
|
|
|
|
Use cases include:
|
|
|
|
- Store-and-forward with unlimited retries for unreliable receivers
|
|
- Prometheus/Grafana metric analysis of webhook frequency, size, and
|
|
handler performance
|
|
- Introspection and debugging of webhook payloads
|
|
- Redelivery of webhook events for application testing
|
|
- Fan-out delivery of webhooks to multiple targets
|
|
- HA ingestion endpoint for delivery to less reliable systems
|
|
|
|
## Design
|
|
|
|
### Architecture
|
|
|
|
webhooker uses Uber's fx dependency injection library for managing
|
|
application lifecycle. It uses `log/slog` for structured logging, GORM
|
|
for database access, and SQLite (via `modernc.org/sqlite`, pure Go, no
|
|
CGO) for storage. HTTP routing uses chi.
|
|
|
|
### Rate Limiting
|
|
|
|
Global rate limiting middleware (e.g. per-IP throttling applied at the
|
|
router level) must **not** apply to webhook receiver endpoints
|
|
(`/webhook/{uuid}`). Webhook endpoints receive automated traffic from
|
|
external services at unpredictable rates, and blanket rate limits would
|
|
cause legitimate webhook deliveries to be dropped.
|
|
|
|
Instead, each webhook endpoint has its own individually configurable rate
|
|
limit, applied within the webhook handler itself. By default, no rate
|
|
limit is applied — webhook endpoints accept traffic as fast as it arrives.
|
|
Rate limits can be configured on a per-webhook basis in the application
|
|
when needed (e.g. to protect against a misbehaving sender).
|
|
|
|
### Database Architecture
|
|
|
|
webhooker uses separate SQLite database files rather than a single
|
|
monolithic database:
|
|
|
|
- **Main application database** — Stores application configuration and
|
|
all standard webapp data: users, sessions, API keys, and global
|
|
settings.
|
|
- **Per-processor databases** — Each processor (working name — a better
|
|
term is needed) gets its own dedicated SQLite database file containing:
|
|
input logs, processor logs, and all output queues for that specific
|
|
processor.
|
|
|
|
This separation provides several benefits: processor databases can be
|
|
independently backed up, rotated, or archived; a high-volume processor
|
|
won't cause lock contention or bloat affecting the main application; and
|
|
individual processor data can be cleanly deleted when a processor is
|
|
removed.
|
|
|
|
### Package Layout
|
|
|
|
All application code lives under `internal/` to prevent external imports.
|
|
The main entry point is `cmd/webhooker/main.go`.
|
|
|
|
- `internal/config` — Configuration management via `pkg/config` (Viper-based)
|
|
- `internal/database` — GORM database connection, migrations, and models
|
|
- `internal/globals` — Global application metadata (version, build info)
|
|
- `internal/handlers` — HTTP handlers using the closure pattern
|
|
- `internal/healthcheck` — Health check endpoint logic
|
|
- `internal/logger` — Structured logging setup (`log/slog`)
|
|
- `internal/middleware` — HTTP middleware (auth, CORS, logging, metrics)
|
|
- `internal/server` — HTTP server setup and routing
|
|
- `internal/session` — Session management (gorilla/sessions)
|
|
- `pkg/config` — Reusable multi-environment configuration library
|
|
- `static/` — Embedded static assets (CSS, JS)
|
|
- `templates/` — Go HTML templates
|
|
|
|
### Data Model
|
|
|
|
- **Users** — Service users with username/password (Argon2id hashing)
|
|
- **Processors** — Webhook processing units, many-to-one with users
|
|
- **Webhooks** — Inbound URL endpoints feeding into processors
|
|
- **Targets** — Delivery destinations per processor (HTTP, retry, database, log)
|
|
- **Events** — Captured webhook payloads
|
|
- **Deliveries** — Pairing of events with targets
|
|
- **Delivery Results** — Outcome of each delivery attempt
|
|
- **API Keys** — Programmatic access credentials per user
|
|
|
|
### API Endpoints
|
|
|
|
- `GET /` — Web UI index page
|
|
- `GET /.well-known/healthcheck` — Health check with uptime, version
|
|
- `GET /s/*` — Static file serving (CSS, JS)
|
|
- `GET /metrics` — Prometheus metrics (requires basic auth)
|
|
- `POST /webhook/{uuid}` — Webhook receiver endpoint
|
|
- `/pages/login`, `/pages/logout` — Authentication
|
|
- `/user/{username}` — User profile
|
|
- `/sources/*` — Webhook source management
|
|
|
|
## TODO
|
|
|
|
### Phase 1: Security & Infrastructure
|
|
- [ ] Security headers (HSTS, CSP, X-Frame-Options)
|
|
- [ ] Rate limiting middleware
|
|
- [ ] CSRF protection for forms
|
|
- [ ] Request ID tracking through entire lifecycle
|
|
|
|
### Phase 2: Authentication & Authorization
|
|
- [ ] Authentication middleware for protected routes
|
|
- [ ] Session expiration and "remember me"
|
|
- [ ] Password reset flow
|
|
- [ ] API key authentication for programmatic access
|
|
|
|
### Phase 3: Core Webhook Features
|
|
- [ ] Webhook reception and event storage at `/webhook/{uuid}`
|
|
- [ ] Event processing and target delivery engine
|
|
- [ ] HTTP target type (fire-and-forget POST)
|
|
- [ ] Retry target type (exponential backoff)
|
|
- [ ] Database target type (store only)
|
|
- [ ] Log target type (console output)
|
|
- [ ] Webhook signature verification (GitHub, Stripe formats)
|
|
|
|
### Phase 4: Web UI
|
|
- [ ] Webhook source management pages (list, create, edit, delete)
|
|
- [ ] Webhook request log viewer with filtering
|
|
- [ ] Delivery status and retry management UI
|
|
- [ ] Manual event redelivery
|
|
- [ ] Analytics dashboard (success rates, response times)
|
|
|
|
### Phase 5: API
|
|
- [ ] RESTful CRUD for processors, webhooks, targets
|
|
- [ ] Event viewing and filtering endpoints
|
|
- [ ] API documentation (OpenAPI)
|
|
|
|
### Future
|
|
- [ ] Email source and delivery target types
|
|
- [ ] SNS, S3, Slack delivery targets
|
|
- [ ] Data transformations (e.g. webhook-to-Slack message)
|
|
- [ ] JSONL file delivery with periodic S3 upload
|
|
|
|
## License
|
|
|
|
Pending — to be determined by the author (MIT, GPL, or WTFPL).
|
|
|
|
## Author
|
|
|
|
[@sneak](https://sneak.berlin)
|