Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Release

on:
push:
tags:
- 'v*'

permissions:
contents: write
packages: write

jobs:
release:
name: Build and release
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache-dependency-path: go.sum

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: internal/web/ui/package-lock.json

- name: Build UI
run: make web-build

- name: Build binaries
run: |
VERSION=${GITHUB_REF_NAME}
LDFLAGS="-s -w -X main.version=${VERSION}"
platforms=(
"linux/amd64"
"linux/arm64"
"darwin/amd64"
"darwin/arm64"
"windows/amd64"
)
mkdir -p dist
for platform in "${platforms[@]}"; do
GOOS="${platform%/*}"
GOARCH="${platform#*/}"
output="dist/webhix_${GOOS}_${GOARCH}"
if [ "$GOOS" = "windows" ]; then output="${output}.exe"; fi
GOOS=$GOOS GOARCH=$GOARCH go build -ldflags "$LDFLAGS" -o "$output" ./cmd/webhix
done

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: dist/*
generate_release_notes: true

docker:
name: Build and push Docker image
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
146 changes: 146 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
[![RU](https://img.shields.io/badge/lang-ru-blue)](docs/CONTRIBUTING.ru.md)

# Contributing to Webhix

Thanks for your interest. This document covers everything you need to get started.

## Prerequisites

- Go 1.24+
- [sqlc](https://sqlc.dev) — regenerating database queries after schema changes
- [goose](https://github.com/pressly/goose) — running migrations manually
- Node.js 20+ — only if touching the UI

## Project structure

```
cmd/webhix/ entry point (main.go)
internal/
app/ application wiring (dependencies, services, HTTP setup)
cli/ CLI commands (serve, forward)
config/ environment-based configuration
core/ business logic, repository interfaces
domain/ domain types and errors
hub/ SSE event hub
server/ HTTP handlers, routing, middleware
store/ SQLite implementation (sqlc-generated queries)
migrations/ goose migration files
query/ raw SQL queries
sqlc/ generated Go code — do not edit by hand
web/ embedded static assets and UI source
ui/src/ TypeScript/CSS frontend source
pkg/ shared utilities
docs/ documentation
config/ example env files
```

## Running locally

```sh
# Copy example config
cp config/.env.example config/.env

# Run the server
go run ./cmd/webhix serve

# Run with a custom address
go run ./cmd/webhix serve --addr :9090 --base-url http://localhost:9090
```

## Running all checks

```sh
make ci
```

This runs formatting, linting, vetting, and tests. All checks must pass before opening a PR.

Individual commands:

```sh
make fmt # format Go code
make lint # run golangci-lint
make test # run tests
make web-check # TypeScript check + ESLint + Prettier
```

## Branches

Follow the naming from [docs/branch-patterns.md](docs/branch-patterns.md).

```
feature/my-feature
fix/some-bug
refactor/cleanup-handler
```

- Branch off `main`
- Keep branches short-lived
- One feature or fix per branch

## Commits

Follow the conventions from [docs/commit-patterns.md](docs/commit-patterns.md).

```
feat(server): add replay endpoint
fix(store): handle null body on insert
refactor(core): extract token generation
```

- English only
- Imperative mood: `add`, `fix`, `remove`
- No period at the end
- No `//nolint` comments — fix the lint issue instead

## Pull requests

- PR title follows the same format as commit messages
- Keep PRs small and focused — one thing per PR
- Add a short description of what changed and why
- All CI checks must pass before merging

## Architecture

The project follows a layered architecture. Dependencies go in one direction only:

```
domain ← store
domain ← core
domain ← server
core ← server
```

- `domain` has no dependencies on other internal packages
- `core` defines repository interfaces — it does not import `store`
- `server` depends on `core` interfaces, not on `store` directly
- SQL errors must not leak into `server` — wrap them in `domain` errors at the `store` layer

## Database changes

If you change the schema:

1. Add a migration in `internal/store/migrations/` using goose format
2. Update SQL queries in `internal/store/query/`
3. Regenerate: `sqlc generate`
4. Never edit files in `internal/store/sqlc/` by hand

## UI changes

The frontend is in `internal/web/ui/src/` — vanilla TypeScript, no framework.

```sh
make web-dev # start Vite dev server
make web-build # build and embed into binary
make web-check # lint + type check + prettier
```

## Environment

| Variable | Default | Description |
|----------|---------|-------------|
| `WEBHIX_BASE_URL` | `http://localhost:8080` | Public base URL for generated endpoint links |
| `WEBHIX_ADDR` | `:8080` | Listen address |
| `WEBHIX_DB_PATH` | `./data` | SQLite database directory |
| `WEBHIX_PASSWORD` | — | Basic auth password |
| `WEBHIX_SECRET_KEY` | — | API secret key (Bearer / X-Webhix-Key) |
46 changes: 35 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,56 @@
# Webhix

[![RU](https://img.shields.io/badge/lang-ru-blue)](docs/README.ru.md) [![Contributing](https://img.shields.io/badge/contributing-guide-brightgreen)](CONTRIBUTING.md)
[![Release](https://img.shields.io/github/v/release/gaisbax/webhix)](https://github.com/gaisbax/webhix/releases)
[![License](https://img.shields.io/badge/license-AGPL--3.0-blue)](LICENSE)
[![Go Report Card](https://goreportcard.com/badge/github.com/gaisbax/webhix)](https://goreportcard.com/report/github.com/gaisbax/webhix)
[![Docker](https://img.shields.io/badge/docker-ghcr.io-blue)](https://github.com/gaisbax/webhix/pkgs/container/webhix)
[![RU](https://img.shields.io/badge/lang-ru-blue)](docs/README.ru.md)
[![Contributing](https://img.shields.io/badge/contributing-guide-brightgreen)](CONTRIBUTING.md)

Self-hosted webhook inspector. Single binary, SQLite, no external dependencies.

webhook.site is the go-to tool for debugging webhooks, but it sends your data to someone else's server. Stripe payloads, OAuth tokens, PII — all of it leaves your network. A lot of companies block it outright for that reason. Webhix runs on your own infrastructure, stores everything locally, and stays out of your way.
webhook.site is the go-to tool for debugging webhooks, but it sends your data to someone else's server. Stripe payloads, OAuth tokens, PII — all of it leaves your network. A lot of companies block it outright for that reason. Webhix runs on your own infrastructure and stores everything locally.

## What it does
![Webhix UI](docs/screenshot.png)

You create an endpoint, point your webhook source at it, and watch requests come in. Every request is captured in full — headers, body, query params, IP, timestamp, content type, size. The UI updates live without a page refresh.
## Features

Beyond just inspecting requests, you can:
- 📡 Capture any HTTP method — headers, body, query params, IP, timestamp, content type, size
- 🔴 Live UI updates via SSE — no page refresh needed
- 🪞 Replay any request with one click
- 🎭 Custom responses — configure status, headers, and body (lightweight mock server)
- 🔁 CLI forwarding to localhost: `webhix forward <token> --to localhost:3000`
- 📋 Export as curl — copy any request as a runnable command
- 🔍 Full-text search and filter by HTTP method
- 🔒 Basic auth out of the box
- 🐳 Docker, Compose, or standalone binary
- 💾 SQLite by default — no Redis or Postgres required

- **Replay** any captured request with one click
- **Custom responses** — configure the status code, headers, and body your endpoint returns to senders (useful as a lightweight mock server)
- **CLI forwarding** — pipe incoming requests to a local port: `webhix forward <token> --to localhost:3000`
- **Export as curl** — copy any request as a runnable curl command
- **Search and filter** — filter requests by text or HTTP method
## Why not webhook.site / smee.io / webhook-tester?

| | Webhix | webhook.site (self-hosted) | smee.io | tarampampam/webhook-tester |
| --------------- | ------------- | ----------------------------- | -------------- | -------------------------- |
| Self-hosted | ✅ | ✅ | ❌ | ✅ |
| Single binary | ✅ | ❌ PHP + Composer + MySQL | ❌ | ❌ Redis or fs driver |
| Request history | ✅ | ✅ | ❌ | ✅ |
| Live UI | ✅ | ✅ | ❌ | ✅ |
| Replay | ✅ | ❌ | ❌ | ❌ |
| CLI forwarding | ✅ built-in | ❌ | ✅ only this | ❌ needs ngrok |
| Custom responses| ✅ | ❌ | ❌ | ❌ |

Webhix is the only tool combining single-binary deployment, request replay, and custom responses — no Redis, no PHP, no external tunnel services.

## Quick start

### Binary

```sh
curl -fsSL https://webhix.dev/install.sh | sh
curl -fsSL https://webhix.online/install.sh | sh
webhix serve --base-url https://hooks.yourdomain.com
```

Or download manually from [releases](https://github.com/gaisbax/webhix/releases/latest).

### Docker

```sh
Expand Down
Loading