From 69ff628f74131028a23e7f44e09f964fceb7e5ed Mon Sep 17 00:00:00 2001 From: Francis Date: Tue, 12 May 2026 20:48:30 -0400 Subject: [PATCH 1/2] better docs, starting on dokploy provider, updating to golang 1.26.3 --- .env.example | 4 +- .github/workflows/release.yml | 46 ++++++- Dockerfile | 4 +- README.md | 171 +++++++++++++++------------ docs/CONTRIBUTE.md | 6 +- docs/OBSERVABILITY.md | 18 ++- docs/providers/README.md | 5 + docs/providers/dokploy/README.md | 7 ++ docs/providers/fly.io/README.md | 26 ++++ docs/providers/{ => fly.io}/fly.toml | 0 docs/providers/fly.md | 23 ---- go.mod | 42 +++---- go.sum | 87 +++++++------- internal/processor/webhook.go | 14 ++- 14 files changed, 267 insertions(+), 186 deletions(-) create mode 100644 docs/providers/README.md create mode 100644 docs/providers/dokploy/README.md create mode 100644 docs/providers/fly.io/README.md rename docs/providers/{ => fly.io}/fly.toml (100%) delete mode 100644 docs/providers/fly.md diff --git a/.env.example b/.env.example index b1b7ebd..a67b46a 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ -LISTEN=0.0.0.0:25 -LISTEN_TLS=0.0.0.0:587 +LISTEN_ADDR=0.0.0.0:25 +LISTEN_ADDR_TLS=0.0.0.0:587 WEBHOOK_URL=http://localhost:8080/webhook WEBHOOK_USER= WEBHOOK_PASS= diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0455ca4..609ebaa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,7 @@ on: permissions: contents: write + packages: write jobs: build-and-upload: @@ -39,7 +40,7 @@ jobs: BIN_NAME=inboundparse if [ "${{ matrix.goos }}" = "windows" ]; then BIN_NAME="${BIN_NAME}.exe"; fi mkdir -p dist - go build -trimpath -ldflags "-s -w" -o "dist/${BIN_NAME}-${{ matrix.goos }}-${{ matrix.goarch }}" ./ + go build -trimpath -ldflags "-s -w" -o "dist/${BIN_NAME}-${{ matrix.goos }}-${{ matrix.goarch }}" ./cmd/inboundparse - name: Upload artifact uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 @@ -70,3 +71,46 @@ jobs: files: assets/* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + build-and-push-image: + name: Build and push Docker image (GHCR) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6.0.2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4.0.0 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v4.1.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker image name (GHCR requires lowercase) + id: image + run: echo "name=ghcr.io/$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" + + - name: Docker metadata (tags and labels) + id: meta + uses: docker/metadata-action@v6.0.0 + with: + images: ${{ steps.image.outputs.name }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable=true + + - name: Build and push + uses: docker/build-push-action@v6.10 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile index 4b6c04e..0f5521f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # Build stage -# Official images publish 1.26.2-alpine / 1.26.2-alpine3.22+ β€” not alpine3.21. -FROM golang:1.26.2-alpine AS builder +# Official images publish 1.26.3-alpine / 1.26.3-alpine3.22+ β€” not alpine3.21. +FROM golang:1.26.3-alpine AS builder WORKDIR /app diff --git a/README.md b/README.md index 18125a0..e807afb 100644 --- a/README.md +++ b/README.md @@ -9,44 +9,44 @@ A SMTP server that receives emails from any domain without authentication and fo ### Core SMTP Functionality -- βœ‰οΈ **No SMTP Auth Required**: Accepts emails from any domain without username/password -- πŸ”’ **TLS Support**: Automatic SSL/TLS certificate management with Let's Encrypt -- πŸ”Œ **Multiple Ports**: Listens on 25 (SMTP) and 587 (Submission) with optional TLS -- πŸ›‘οΈ **PROXY Protocol**: Accepts PROXY protocol connections (e.g., from load balancers) -- πŸ“ **Message Size Limits**: Configurable maximum message size (default: 10MB) +- **No SMTP Auth Required**: Accepts emails from any domain without username/password +- **TLS Support**: Automatic SSL/TLS certificate management with Let's Encrypt +- **Multiple Ports**: Listens on 25 (SMTP) and 587 (Submission) with optional TLS +- **PROXY Protocol**: Accepts PROXY protocol connections (e.g., from load balancers) +- **Message Size Limits**: Configurable maximum message size (default: 10MB) ### Email Authentication (RFC Compliant) -- 🧐 **SPF Validation (RFC 7208)**: Validates Sender Policy Framework with HELO and envelope sender -- πŸ–ŠοΈ **DKIM Validation (RFC 6376)**: Verifies DomainKeys Identified Mail signatures with multi-signature support -- πŸ•΅οΈβ€β™‚οΈ **DMARC Validation (RFC 7489)**: Evaluates DMARC policy with hierarchical domain lookup and subdomain policy inheritance -- πŸ“ **Comprehensive Results**: Detailed authentication results with domain, mechanism, and alignment data -- πŸ” **Domain Hierarchy Tracking**: Tracks all attempted DMARC lookups with detailed per-domain results +- **SPF Validation (RFC 7208)**: Validates Sender Policy Framework with HELO and envelope sender +- **DKIM Validation (RFC 6376)**: Verifies DomainKeys Identified Mail signatures with multi-signature support +- **DMARC Validation (RFC 7489)**: Evaluates DMARC policy with hierarchical domain lookup and subdomain policy inheritance +- **Comprehensive Results**: Detailed authentication results with domain, mechanism, and alignment data +- **Domain Hierarchy Tracking**: Tracks all attempted DMARC lookups with detailed per-domain results ### Observability & Monitoring -- πŸ“‹ **Structured Logging**: JSON-formatted logs with configurable levels -- πŸ“Š **Prometheus Metrics**: Comprehensive metrics collection and monitoring -- πŸ›ŽοΈ **Sentry Integration**: Error tracking and performance monitoring -- ❀️ **Health Checks**: Built-in health endpoint for load balancers -- πŸ“ˆ **Grafana Dashboards**: Pre-configured monitoring dashboards +- **Structured Logging**: JSON-formatted logs with configurable levels +- **Prometheus Metrics**: Comprehensive metrics collection and monitoring +- **Sentry Integration**: Error tracking and performance monitoring +- **Health Checks**: Built-in health endpoint for load balancers +- **Grafana Dashboards**: Pre-configured monitoring dashboards ### Deployment & Operations -- 🐳 **Docker Support**: Multi-stage Docker builds with Alpine Linux -- 🧩 **Docker Compose**: Complete development stack with monitoring -- πŸš€ **Fly.io Ready**: Pre-configured for Fly.io deployment (more to come!) -- πŸ”‘ **Automatic Certificates**: Let's Encrypt integration with DNS validation -- βš™οΈ **Environment Configuration**: Flexible configuration via environment variables +- **Docker Support**: Multi-stage Docker builds with Alpine Linux +- **Docker Compose**: Complete development stack with monitoring +- **Fly.io Ready**: Pre-configured for Fly.io deployment (more to come!) +- **Automatic Certificates**: Let's Encrypt integration with DNS validation +- **Environment Configuration**: Flexible configuration via environment variables ### Webhook Integration -- πŸ” **HTTP Basic Auth**: Secure webhook authentication -- πŸ“¨ **Rich Payload**: Complete email data including headers and attachments -- πŸ” **Reliability Features**: Automatic retry with exponential backoff, rate limiting, and circuit breaker -- πŸ›‘οΈ **Fault Tolerance**: Circuit breaker pattern prevents cascading failures -- ⚑ **Rate Limiting**: Configurable requests per second to protect downstream endpoints -- βœ‚οΈ **Content Processing**: Parsed text and HTML content extraction +- **HTTP Basic Auth**: Secure webhook authentication +- **Rich Payload**: Complete email data including headers and attachments +- **Reliability Features**: Automatic retry with exponential backoff, rate limiting, and circuit breaker +- **Fault Tolerance**: Circuit breaker pattern prevents cascading failures +- **Rate Limiting**: Smooth per-second webhook rate cap (`WEBHOOK_RATE_LIMIT`) +- **Content Processing**: Parsed text and HTML content extraction ## Quick Start @@ -54,7 +54,7 @@ Welcome to **InboundParse**! Whether you're a developer, sysadmin, or open sourc --- -### 1. ⚑ Fastest Start with Make +### 1. Fastest start with Make Most development and deployment tasks are automated with `make` for simplicity. @@ -73,20 +73,20 @@ make run-dev --- -### 2. πŸ‘ Manual Build & Run (for non-Make users) +### 2. Manual build and run (for non-Make users) ```bash -# 1. Ensure Go β‰₯ 1.20 is installed +# 1. Install a Go toolchain that matches go.mod (see the `go` directive there). go mod tidy # Download dependencies go build -o inboundparse ./cmd/inboundparse/ -# 2. Start the server with a custom webhook endpoint +# 2. Start the server (set WEBHOOK_URL or -webhook for real deliveries; omit for no-op / dev) ./inboundparse -webhook=https://your-app.com/webhook ``` --- -### 3. 🐳 Full Local Dev Stack (Docker Compose, Monitoring, Visual Dashboards) +### 3. Full local dev stack (Docker Compose, monitoring, dashboards) Spin up the whole ecosystem: SMTP, metrics, dashboards, and log viewing, all with familiar tooling. @@ -95,7 +95,7 @@ Spin up the whole ecosystem: SMTP, metrics, dashboards, and log viewing, all wit make up # View logs (all services) -docker-compose -f docker-compose.dev.yml logs -f +docker compose -f docker-compose.dev.yml logs -f # Stop all services make down @@ -112,7 +112,7 @@ With the dev stack running, you'll have: --- -### 4. πŸ› οΈ Command Line Reference +### 4. Command line reference You can configure nearly everything via CLI flags (or use environment variables). Use `./inboundparse -h` to see all options. @@ -120,19 +120,20 @@ You can configure nearly everything via CLI flags (or use environment variables) Usage of ./inboundparse: -listen string SMTP server listen address (default "0.0.0.0:25") -listen-tls string SMTP TLS listen address (default "0.0.0.0:587") - -webhook string Webhook URL for email payloads (required) + -webhook string Webhook URL for email payloads (optional; empty disables delivery) -webhook-user string Basic auth username for webhook (optional) -webhook-pass string Basic auth password for webhook (optional) -webhook-max-retries int Maximum webhook retry attempts (default 3) -webhook-retry-delay int Initial webhook retry delay in seconds (default 1) -webhook-max-retry-delay int Maximum webhook retry delay in seconds (default 30) -webhook-retry-multiplier float Webhook retry delay multiplier (default 2.0) - -webhook-rate-limit int Webhook requests per second rate limit (default 10) - -webhook-rate-burst int Webhook rate limit burst capacity (default 20) + -webhook-rate-limit int Webhook requests per second (smooth limiter; default 10) + -webhook-rate-burst int Parsed for API compatibility; smooth limiter uses rate only (default 20) -name string SMTP server name (default "mx.inboundparse.com") -max-size int Max message size bytes (default 10485760) -read-timeout int Read timeout (seconds, default 30) -write-timeout int Write timeout (seconds, default 30) + -message-processing-timeout int Per-message processing timeout in seconds, including auth and webhook (default 300) -enable-spf Enable SPF validation (default true) -enable-dkim Enable DKIM validation (default true) -enable-dmarc Enable DMARC validation (default true) @@ -142,21 +143,21 @@ Usage of ./inboundparse: -enable-metrics Enable Prometheus metrics (default false) -metrics-addr string Metrics server listen addr (default "0.0.0.0:9090") -metrics-api-key string Metrics API key (optional) - -metrics-username string Metrics username (optional) - -metrics-password string Metrics password (optional) + -metrics-username string Metrics username (optional) + -metrics-password string Metrics password (optional) -enable-sentry Enable Sentry error tracking (default false) -sentry-dsn string Sentry DSN for errors (optional) -sentry-env string Sentry env (default "production") -sentry-release string Sentry release version (optional) ``` -### πŸ”’ Environment Variables +### Environment variables You can configure InboundParse using environment variables or command line flags: #### Core Environment Variables -- `WEBHOOK_URL` Webhook URL to receive email payloads (**required**) +- `WEBHOOK_URL` Webhook URL to receive email payloads (optional; if unset, messages are processed but not POSTed) - `WEBHOOK_USER` Basic auth username for the webhook (optional) - `WEBHOOK_PASS` Basic auth password for the webhook (optional) @@ -164,13 +165,21 @@ You can configure InboundParse using environment variables or command line flags - `WEBHOOK_MAX_RETRIES` Maximum retry attempts (default: `3`) - `WEBHOOK_RETRY_DELAY` Initial retry delay in seconds (default: `1`) -- `WEBHOOK_MAX_RETRY_DELAY` Maximum retry delay in seconds (default: `30`) +- `WEBHOOK_MAX_RETRY_DELAY` Maximum retry delay cap in seconds (default: `30`) - `WEBHOOK_RETRY_MULTIPLIER` Exponential backoff multiplier (default: `2.0`) -- `WEBHOOK_RATE_LIMIT` Requests per second rate limit (default: `10`) -- `WEBHOOK_RATE_BURST` Rate limit burst capacity (default: `20`) +- `WEBHOOK_RATE_LIMIT` Target average webhook requests per second for the smooth limiter (default: `10`) +- `WEBHOOK_RATE_BURST` Parsed for compatibility; not used by the current smooth rate limiter (default: `20`) + +#### Server & Processing + - `LISTEN_ADDR` SMTP listen address (default: `0.0.0.0:25`) - `LISTEN_ADDR_TLS` TLS listen address (default: `0.0.0.0:587`) - `SERVER_NAME` SMTP service name for banner (default: `mx.inboundparse.com`) +- `MAX_SIZE` Maximum message size in bytes (default: `10485760`) +- `READ_TIMEOUT` Read timeout in seconds (default: `30`) +- `WRITE_TIMEOUT` Write timeout in seconds (default: `30`) +- `MESSAGE_PROCESSING_TIMEOUT` Upper bound in seconds for processing one message (parse, auth, webhook; default: `300`) +- `TRUSTED_PROXY_CIDRS` Comma-separated CIDRs trusted when interpreting PROXY protocol and client IP (optional) #### TLS Settings @@ -189,8 +198,10 @@ You can configure InboundParse using environment variables or command line flags - `ENABLE_METRICS` Enable Prometheus metrics endpoint (`true`/`false`, default: `false`) - `METRICS_ADDR` Metrics endpoint listen address (default: `0.0.0.0:9090`) - `METRICS_API_KEY` Bearer token used for metrics endpoint authentication (optional) -- `METRICS_USERNAME` Username for metrics endpoint basic auth (optional) -- `METRICS_PASSWORD` Password for metrics endpoint basic auth (optional) +- `METRICS_USERNAME` Username for metrics endpoint basic auth (optional; must be set together with `METRICS_PASSWORD`) +- `METRICS_PASSWORD` Password for metrics endpoint basic auth (optional; must be set together with `METRICS_USERNAME`) + +When metrics are enabled on a non-loopback address (`0.0.0.0`, public IP, etc.), you must configure either `METRICS_API_KEY` or both `METRICS_USERNAME` and `METRICS_PASSWORD`. Unauthenticated `/metrics` is only allowed when bound to loopback (for example `127.0.0.1:9090`). #### Sentry Error Tracking @@ -201,73 +212,82 @@ You can configure InboundParse using environment variables or command line flags ## Webhook Payload -The service sends a JSON payload to your webhook with comprehensive email data: +The webhook receives a JSON document shaped like `domain.WebhookPayload`: message metadata and `authentication_results` for SPF/DKIM/DMARC. `from` and `to` are `net/mail.Address` values (JSON fields **`Name`** and **`Address`**). `timestamp` is taken from the parsed `Date` header when present. Attachments and inline parts use the structures from [`letters`](https://github.com/mnako/letters). ```json { "timestamp": "2024-01-01T12:00:00Z", - "from": [{ "address": "sender@example.com", "name": "Sender" }], - "to": [{ "address": "recipient@example.com", "name": "Recipient" }], + "from": [{ "Name": "Sender", "Address": "sender@example.com" }], + "to": [{ "Name": "", "Address": "recipient@example.com" }], "subject": "Email Subject", - "body": "Plain text body content", + "message_id": "", "headers": { - "From": "sender@example.com", - "To": "recipient@example.com", - "Subject": "Email Subject", - "Message-ID": "", - "Date": "Mon, 01 Jan 2024 12:00:00 +0000" + "X-Custom": ["value"] }, - "remote_ip": "192.168.1.100", - "auth": { + "text_body": "Plain text body", + "enriched_text_body": "", + "html_body": "

HTML body

", + "attachments": [], + "inline_files": [], + "authentication_results": { "spf": { "result": "pass", - "domain": "example.com", - "mechanism": "ip4", - "explanation": "" + "raw": "v=spf1 ...", + "mechanism": "include", + "qualifier": "~", + "explanation": "", + "problem": "", + "sender": "sender@example.com", + "ip_address": "192.0.2.1" }, "dkim": { - "result": "pass", - "signatures": [ + "valid": true, + "signatures": ["example.com"], + "details": [ { "domain": "example.com", - "result": "pass", - "canonicalization": "signed headers: 8" + "headers_signed": ["from", "to", "subject"], + "timestamp": 1704110400, + "expiration": 0, + "valid": true, + "error": "" } ], - "body": "at least one signature passed verification" + "raw": "", + "error": "" }, "dmarc": { "result": "pass", + "raw": "v=DMARC1; p=reject; ...", "policy": "reject", - "subdomain_policy": "quarantine", + "domain": "example.com", "percentage": 100, + "subdomain_policy": "quarantine", "spf_aligned": true, "dkim_aligned": true, "spf_domain": "example.com", "dkim_domain": "example.com", "details": [ - { - "domain": "sub.example.com", - "record_found": false, - "error": "dmarc: no policy found for domain" - }, { "domain": "example.com", "record_found": true, - "policy_used": "sp" + "policy_used": "p", + "error": "" } ], "spf_alignment": "relaxed", "dkim_alignment": "relaxed", - "failure_options": "0", - "report_uris": ["mailto:dmarc@example.com"], - "failure_uris": ["mailto:dmarc-fail@example.com"] + "failure_options": "", + "report_uris": [], + "failure_uris": [] } } } ``` -## 🀝 Contributing +Omitted optional fields are elided from the JSON (`omitempty`). If all auth checks are disabled, `authentication_results` may be empty. + +## Contributing Contributions of all kinds are welcome! See [CONTRIBUTE.md](./docs/CONTRIBUTE.md) for guidelines, and don’t hesitate to open issues, fork, or create PRs. @@ -275,4 +295,3 @@ Contributions of all kinds are welcome! See [CONTRIBUTE.md](./docs/CONTRIBUTE.md > **Need help or want to suggest new features?** > Open an issue or start a discussion on GitHub! - diff --git a/docs/CONTRIBUTE.md b/docs/CONTRIBUTE.md index 2c36a6e..59e16f0 100644 --- a/docs/CONTRIBUTE.md +++ b/docs/CONTRIBUTE.md @@ -34,7 +34,7 @@ We appreciate all kinds of contributions: ### 2. Set Up Your Environment -- **Ensure you have Go >= 1.24 installed.** +- **Ensure you have a Go toolchain that matches `go.mod` (currently Go 1.26.x).** - Install dependencies: ```bash make deps @@ -56,9 +56,9 @@ We appreciate all kinds of contributions: ```bash make test ``` -- For coverage and additional checks: +- For coverage summary: ```bash - make test-coverage + make coverage ``` --- diff --git a/docs/OBSERVABILITY.md b/docs/OBSERVABILITY.md index 11a84a5..a4cba54 100644 --- a/docs/OBSERVABILITY.md +++ b/docs/OBSERVABILITY.md @@ -139,11 +139,7 @@ Example retry sequence with defaults: ### Rate Limiting -Smooth rate limiting using token bucket algorithm: - -- **Rate Limit**: Requests per second (default: 10) -- **Burst Capacity**: Burst allowance (default: 20) -- **Algorithm**: Token bucket for smooth rate limiting +Webhook delivery uses failsafe-go’s **smooth** rate limiter: it enforces an average of `WEBHOOK_RATE_LIMIT` executions per second. `WEBHOOK_RATE_BURST` / `-webhook-rate-burst` are still read for compatibility but are **not** applied by the current limiter implementation. ### Circuit Breaker @@ -264,9 +260,11 @@ Or via flags: #### Webhook Metrics -- `webhook_requests_total{status_code="200|500|..."}` - Webhook requests by status (counter) +- `webhook_requests_total{status_code}` - Webhook outcomes; `status_code` is the HTTP status **text** from Go's `http.StatusText` (for example `OK` for 200, `Internal Server Error` for 500), not the numeric code - `webhook_duration_seconds` - Webhook request duration histogram +Additional histograms and counters (payload sizes, per-stage message timing, session metrics, etc.) are registered in `internal/observability/metrics.go`. + ### Authentication The /metrics endpoint supports three authentication methods: @@ -281,7 +279,7 @@ The /metrics endpoint supports three authentication methods: curl -u admin:secret http://localhost:9090/metrics ``` -3. **No Auth**: If no credentials are configured, the endpoint is open +3. **No auth on loopback only**: If you bind the metrics listener to `localhost` / `127.0.0.1` / `::1` and do not set API key or basic auth, `/metrics` is open (intended for local development). Binding to `0.0.0.0` or a public interface **requires** credentials β€” the process fails validation at startup otherwise. ### Health Endpoint @@ -371,8 +369,8 @@ rate(smtp_messages_total[5m]) # Authentication pass rate rate(spf_results_total{result="pass"}[5m]) / rate(spf_results_total[5m]) -# Webhook success rate -rate(webhook_requests_total{status_code="OK"}[5m]) / rate(webhook_requests_total[5m]) +# Webhook success rate (label is HTTP status text, not numeric) +sum(rate(webhook_requests_total{status_code="OK"}[5m])) / sum(rate(webhook_requests_total[5m])) # P95 webhook latency histogram_quantile(0.95, rate(webhook_duration_seconds_bucket[5m])) @@ -423,5 +421,5 @@ The observability features have minimal performance impact: - [zerolog Documentation](https://github.com/rs/zerolog) - [Sentry Go SDK](https://docs.sentry.io/platforms/go/) - [Prometheus Best Practices](https://prometheus.io/docs/practices/naming/) -- [Grafana Dashboards](./docs/grafana-dashboards.md) +- [Grafana provisioning in this repo](../_data/grafana/) (dashboards mounted by `docker-compose.dev.yml`) diff --git a/docs/providers/README.md b/docs/providers/README.md new file mode 100644 index 0000000..880122d --- /dev/null +++ b/docs/providers/README.md @@ -0,0 +1,5 @@ +# Providers + +This directory is just our source of truth that we have tested and found to work with launching this project on a service. We will try and expand this out as much as we can. + +- **Fly.io** β€” For setup instructions, refer to [fly.md](./fly.io/README.md) and see the configuration in [fly.toml](./fly.io/fly.toml). diff --git a/docs/providers/dokploy/README.md b/docs/providers/dokploy/README.md new file mode 100644 index 0000000..78ec67e --- /dev/null +++ b/docs/providers/dokploy/README.md @@ -0,0 +1,7 @@ +# Dokploy + +Dokploy is a deployment automation platform designed to help developers and teams streamline the deployment process for their applications. With Dokploy, users can easily configure, manage, and scale their deployments across various environments without the overhead of managing infrastructure manually. Whether you're deploying microservices, web applications, or backend services, Dokploy provides simple workflows and robust integrations to ensure your software gets delivered quickly and reliably. Learn more about [Dokploy here](https://dokploy.com) + +## Steps + +WIP \ No newline at end of file diff --git a/docs/providers/fly.io/README.md b/docs/providers/fly.io/README.md new file mode 100644 index 0000000..0493aa1 --- /dev/null +++ b/docs/providers/fly.io/README.md @@ -0,0 +1,26 @@ +# Fly.io deployment + +The sample Fly config in this directory is [`fly.toml`](./fly.toml). Adjust app name, region, and sizing for your project. + +```bash +# From the directory that contains fly.toml +fly deploy + +# Core secrets (adjust for your webhook and DNS/API provider β€” see `start.sh` / `Dockerfile` for ACME) +fly secrets set WEBHOOK_URL=https://your-webhook-url.com/api/webhook +fly secrets set CF_Token=your-cloudflare-token +fly secrets set CF_Zone_ID=your-zone-id +fly secrets set MXDOMAIN=your-domain.com + +# Optional: error tracking +fly secrets set SENTRY_DSN=https://your-dsn@sentry.io/project-id +``` + +Typical elements in the sample `fly.toml`: + +- TCP services on **25** and **587** with **PROXY protocol** handlers (load balancers / Fly proxies) +- **`[metrics]`** exposing `/metrics` on port **9090** for Fly’s Prometheus integration +- **`[mounts]`** volume for `/cert` so certificate material can persist +- **1 shared CPU / 1GB** VM sizing (adjust as needed) + +For Prometheus metrics bound to non-loopback addresses, set `METRICS_API_KEY` or `METRICS_USERNAME` + `METRICS_PASSWORD` in secrets; the app requires credentials in that case. diff --git a/docs/providers/fly.toml b/docs/providers/fly.io/fly.toml similarity index 100% rename from docs/providers/fly.toml rename to docs/providers/fly.io/fly.toml diff --git a/docs/providers/fly.md b/docs/providers/fly.md deleted file mode 100644 index 9c32663..0000000 --- a/docs/providers/fly.md +++ /dev/null @@ -1,23 +0,0 @@ -# Fly.io Deployment - -InboundParse is pre-configured for Fly.io deployment with automatic SSL certificate management: - -```bash -# Deploy to Fly.io -fly deploy - -# Set environment variables -fly secrets set WEBHOOK_URL=https://your-webhook-url.com/api/webhook -fly secrets set CF_Token=your-cloudflare-token -fly secrets set CF_Zone_ID=your-zone-id -fly secrets set MXDOMAIN=your-domain.com - -# Enable monitoring -fly secrets set SENTRY_DSN=https://your-dsn@sentry.io/project-id -``` - -The `fly.toml` configuration includes: -- PROXY protocol support for load balancers -- Automatic certificate management -- Health checks -- Resource allocation (1GB RAM, 1 CPU) \ No newline at end of file diff --git a/go.mod b/go.mod index a0e4328..ea7bbac 100644 --- a/go.mod +++ b/go.mod @@ -1,45 +1,45 @@ module inboundparse -go 1.26.2 +go 1.26.3 require ( github.com/emersion/go-msgauth v0.7.0 github.com/emersion/go-smtp v0.24.0 - github.com/failsafe-go/failsafe-go v0.9.1 - github.com/getsentry/sentry-go v0.36.0 + github.com/failsafe-go/failsafe-go v0.9.6 + github.com/getsentry/sentry-go v0.46.2 github.com/google/uuid v1.6.0 github.com/joho/godotenv v1.5.1 - github.com/mnako/letters v0.2.6 - github.com/pires/go-proxyproto v0.8.1 + github.com/mnako/letters v0.2.8 + github.com/pires/go-proxyproto v0.12.0 github.com/prometheus/client_golang v1.23.2 - github.com/rs/zerolog v1.34.0 + github.com/rs/zerolog v1.35.1 github.com/stretchr/testify v1.11.1 github.com/zaccone/spf v0.0.0-20170817004109-76747b8658d9 - golang.org/x/sync v0.17.0 + golang.org/x/sync v0.20.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.24.0 // indirect + github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect github.com/kr/text v0.2.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/miekg/dns v1.1.68 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.22 // indirect + github.com/miekg/dns v1.1.72 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.66.1 // indirect - github.com/prometheus/procfs v0.16.1 // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/crypto v0.43.0 // indirect - golang.org/x/mod v0.29.0 // indirect - golang.org/x/net v0.46.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.30.0 // indirect - golang.org/x/tools v0.38.0 // indirect - google.golang.org/protobuf v1.36.8 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.20.1 // indirect + go.yaml.in/yaml/v2 v2.4.4 // indirect + golang.org/x/crypto v0.51.0 // indirect + golang.org/x/mod v0.36.0 // indirect + golang.org/x/net v0.54.0 // indirect + golang.org/x/sys v0.44.0 // indirect + golang.org/x/text v0.37.0 // indirect + golang.org/x/tools v0.45.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 48fc8bc..6ac03db 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,9 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.24.0 h1:H4x4TuulnokZKvHLfzVRTHJfFfnHEeSYJizujEZvmAM= -github.com/bits-and-blooms/bitset v1.24.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= +github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -14,13 +13,12 @@ github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3 github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-smtp v0.24.0 h1:g6AfoF140mvW0vLNPD/LuCBLEAdlxOjIXqbIkJIS6Wk= github.com/emersion/go-smtp v0.24.0/go.mod h1:ZtRRkbTyp2XTHCA+BmyTFTrj8xY4I+b4McvHxCU2gsQ= -github.com/failsafe-go/failsafe-go v0.9.1 h1:PkKSKLSOPRyJMjx35SfuwQeDuPLB6lBhD+zpQcSe7NU= -github.com/failsafe-go/failsafe-go v0.9.1/go.mod h1:sX5TZ4HrMLYSzErWeckIHRZWgZj9PbKMAEKOVLFWtfM= -github.com/getsentry/sentry-go v0.36.0 h1:UkCk0zV28PiGf+2YIONSSYiYhxwlERE5Li3JPpZqEns= -github.com/getsentry/sentry-go v0.36.0/go.mod h1:p5Im24mJBeruET8Q4bbcMfCQ+F+Iadc4L48tB1apo2c= +github.com/failsafe-go/failsafe-go v0.9.6 h1:vPSH2cry0Ee5cnR9wc9qshCDO6jdrMA9elBJNwyo4Uk= +github.com/failsafe-go/failsafe-go v0.9.6/go.mod h1:IeRpglkcwzKagjDMh90ZhN2l4Ovt3+jemQBUbThag54= +github.com/getsentry/sentry-go v0.46.2 h1:1jhYwrKGa3sIpo/y5iDNXS5wDoT7I1KNzMHrnK6ojns= +github.com/getsentry/sentry-go v0.46.2/go.mod h1:evVbw2qotNUdYG8KxXbAdjOQWWvWIwKxpjdZZIvcIPw= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -37,21 +35,20 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= -github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= -github.com/mnako/letters v0.2.6 h1:r6YSNUupRjuiZHKdINxge5eYOaUnxiq+LzcaBa2zk0c= -github.com/mnako/letters v0.2.6/go.mod h1:0gm/Bmk4B5g0iEE7BNxFevMs8qqdF6Gbh050JEM7L24= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= +github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= +github.com/mnako/letters v0.2.8 h1:w4H57g8360ShQ9G+ELUtz1apBBwkYazGYRgr70KF/2c= +github.com/mnako/letters v0.2.8/go.mod h1:BFhGZBaawfeHmghK8q5Q71A3G9SqVSVE7pDlSNdjErg= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= -github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= +github.com/pires/go-proxyproto v0.12.0 h1:TTCxD66dU898tahivkqc3hoceZp7P44FnorWyo9d5vM= +github.com/pires/go-proxyproto v0.12.0/go.mod h1:qUvfqUMEoX7T8g0q7TQLDnhMjdTrxnG0hvpMn+7ePNI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -60,46 +57,42 @@ github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= -github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= -github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI= +github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/zaccone/spf v0.0.0-20170817004109-76747b8658d9 h1:NugUf62Z6Yzn//u/MT+cuaFX1AFzfuIR9QVywUQX18E= github.com/zaccone/spf v0.0.0-20170817004109-76747b8658d9/go.mod h1:AL91TJsHKIaWR16S1IaxTSZfBRMr3/dOdiN1OZ1m9RM= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/internal/processor/webhook.go b/internal/processor/webhook.go index 70d1385..2527029 100644 --- a/internal/processor/webhook.go +++ b/internal/processor/webhook.go @@ -69,9 +69,21 @@ func NewWebhookSender(config WebhookConfig, logger observability.Logger, metrics if maxRetries < 0 { maxRetries = 0 // Disable retries } + retryDelay := config.RetryDelay + if retryDelay <= 0 { + retryDelay = time.Second + } + maxRetryDelay := config.MaxRetryDelay + if maxRetryDelay <= 0 { + maxRetryDelay = 30 * time.Second + } + retryMultiplier := config.RetryMultiplier + if retryMultiplier <= 0 { + retryMultiplier = 2.0 + } retryPolicy := retrypolicy.NewBuilder[any](). WithMaxRetries(maxRetries). - WithDelay(config.RetryDelay). + WithBackoffFactor(retryDelay, maxRetryDelay, retryMultiplier). Build() // Create rate limiter From f656ec1f15a2b28cdbb4eec81e99fd2f08ded136 Mon Sep 17 00:00:00 2001 From: Francis Date: Tue, 12 May 2026 20:51:17 -0400 Subject: [PATCH 2/2] Update sentry.go --- internal/observability/sentry.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/internal/observability/sentry.go b/internal/observability/sentry.go index 13ded23..7ace65d 100644 --- a/internal/observability/sentry.go +++ b/internal/observability/sentry.go @@ -75,9 +75,13 @@ func (s *sentryClient) CaptureError(err error, context map[string]interface{}) { hub := sentry.CurrentHub() hub.WithScope(func(scope *sentry.Scope) { - // Add context to the scope as extras - for key, value := range context { - scope.SetExtra(key, value) + if len(context) > 0 { + // sentry-go v0.46+ removed Scope.SetExtra; attach arbitrary data as a context object. + c := make(sentry.Context, len(context)) + for key, value := range context { + c[key] = value + } + scope.SetContext("app", c) } hub.CaptureException(err) }) @@ -92,8 +96,12 @@ func (s *sentryClient) CaptureMessage(message string, level sentry.Level, contex hub := sentry.CurrentHub() hub.WithScope(func(scope *sentry.Scope) { scope.SetLevel(level) - for key, value := range context { - scope.SetExtra(key, value) + if len(context) > 0 { + c := make(sentry.Context, len(context)) + for key, value := range context { + c[key] = value + } + scope.SetContext("app", c) } hub.CaptureMessage(message) })