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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ WebhookEngine is a self-hosted webhook delivery platform. Single ASP.NET Core ho
## Tech Stack

- **Backend:** C# / .NET 10, ASP.NET Core (Controllers), Entity Framework Core, PostgreSQL 17+
- **Frontend (Dashboard):** React 19 + TypeScript 5.9 + Vite 7 + Tailwind CSS 4 + Recharts 3 + Lucide React (in `src/dashboard/`)
- **Frontend (Dashboard):** React 19 + TypeScript 6 + Vite 8 + Tailwind CSS 4 + Recharts 3 + Lucide React (in `src/dashboard/`)
- **Testing:** xUnit, FluentAssertions, NSubstitute, Testcontainers (real PostgreSQL)
- **Logging:** Serilog (structured, JSON output)
- **Validation:** FluentValidation
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
### Fixed
- **`@webhookengine/endpoint-manager` client realigned with the live engine.** The published `0.1.0` portal client drifted from the engine contract and the drift was invisible in CI because the package's vitest suite and the `samples/portal-host` mock both reproduced an idealized shape, never the engine's real DTOs. Three concrete defects: `updateEndpoint()` issued `PUT` while the engine route is `[HttpPatch]` (every endpoint update failed against a real engine — the route switched to PATCH in v0.2.1 but the client was never updated); the client read a non-existent `isActive` boolean instead of the engine's `status` string, so the status badge always rendered "Disabled"; and it read a non-existent `customHeaders` map instead of the names-only `customHeaderNames`, which started the editor empty and sent `customHeaders: {}` on every save — silently wiping an endpoint's custom headers even when only the URL changed. Types now mirror the engine (`status: PortalEndpointStatus`, `customHeaderNames: string[]`), the editor preserves stored headers unless the operator explicitly enters new ones, and a contract test now asserts the client speaks PATCH and deserializes the real response shape so this class of drift cannot ship silently again.
- **Dependabot lockfile-sync commit can now re-trigger CI.** `sync-bun-lock.yml` pushed the regenerated `bun.lock` with `GITHUB_TOKEN`, and GitHub deliberately does not let `GITHUB_TOKEN` pushes trigger new workflow runs (recursion guard) — so the sync commit became the PR's HEAD with no CI run on it, leaving the PR blocked on required status checks until a manual close/reopen (as happened on #113). The push now uses a short-lived GitHub App installation token (`actions/create-github-app-token`, gated on the optional `vars.LOCK_BOT_APP_ID`) so the lockfile commit re-triggers the required checks automatically; it falls back to `GITHUB_TOKEN` (manual re-trigger) when the App is not configured. One-time App setup is documented in `docs/RELEASE.md` §1.
- **Documentation accuracy pass.** README: the manual `docker run` example used `ConnectionStrings__WebhookDb` (the engine reads `ConnectionStrings__Default`), and the general curl / SDK / Prometheus examples used the local-dev port `5128` while the Docker Quick Start is `5100` — so a Docker user copy-pasting them hit the wrong target; the examples now use `5100` (matching Quick Start and the SDK's default base URL) and the test count is no longer a hard-coded number that drifts. `docs/PRD.md`: payload transformation removed from the MVP non-goals (it shipped via ADR-003). `docs/ROADMAP.md`: synced to v0.2.2 and the `@webhookengine/endpoint-manager` package marked released (`portal-v0.1.0`). `docs/ARCHITECTURE.md`: the §2 controller list now includes the AuditLogs / Portal / DashboardPortal controllers and the duplicate `### 3.5` heading is fixed (HTTP Delivery Service is `### 3.8`). `AGENTS.md`: frontend stack line corrected to TypeScript 6 / Vite 8.

### Security
- **Repository security baseline hardened.** Added a root `SECURITY.md` (supported-versions policy + GitHub private-vulnerability-reporting disclosure flow), which also resolves the dangling `/SECURITY.md` reference in `CODEOWNERS`. Added explicit least-privilege `permissions: contents: read` blocks to `ci.yml` and `release.yml` — previously the two highest-privilege workflows ran with the broad default token (Docker Hub / NuGet auth uses repository secrets, not `GITHUB_TOKEN`, so no write scope is needed; `id-token: write` is deliberately omitted while provenance is disabled). Extended Dependabot to the previously-uncovered `/packages/endpoint-manager` npm workspace (vite, tailwindcss, tsup, vitest, typescript, … now tracked). Added `timeout-minutes` to the CI Backend/Frontend/Docker jobs so a hung Testcontainers run can't consume the 6-hour default.
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ If you already operate a PostgreSQL instance, run the image directly and point i
```bash
docker run -d --name webhook-engine \
-p 8080:8080 \
-e ConnectionStrings__WebhookDb="Host=your-postgres;Port=5432;Database=webhookengine;Username=postgres;Password=secret" \
-e ConnectionStrings__Default="Host=your-postgres;Port=5432;Database=webhookengine;Username=postgres;Password=secret" \
-e Dashboard__AdminEmail="admin@example.com" \
-e Dashboard__AdminPassword="StrongPassword123!" \
voyvodka/webhook-engine:latest
Expand Down Expand Up @@ -221,7 +221,7 @@ Base URL: `/api/v1/`
### Send a Message

```bash
curl -X POST http://localhost:5128/api/v1/messages \
curl -X POST http://localhost:5100/api/v1/messages \
-H "Authorization: Bearer whe_abc123_your-api-key" \
-H "Content-Type: application/json" \
-d '{
Expand Down Expand Up @@ -249,7 +249,7 @@ Response:
```csharp
using WebhookEngine.Sdk;

using var client = new WebhookEngineClient("whe_abc_your-api-key", "http://localhost:5128");
using var client = new WebhookEngineClient("whe_abc_your-api-key", "http://localhost:5100");

await client.Messages.SendAsync(new SendMessageRequest
{
Expand Down Expand Up @@ -306,7 +306,7 @@ All configuration is via `appsettings.json` or environment variables (double-und
# Build
dotnet build WebhookEngine.sln

# Run all tests (215 tests)
# Run all tests
dotnet test WebhookEngine.sln

# Run specific test project
Expand Down Expand Up @@ -343,7 +343,7 @@ dotnet run --project src/WebhookEngine.API
WebhookEngine exposes Prometheus metrics at `GET /metrics`. No authentication required.

```bash
curl http://localhost:5128/metrics
curl http://localhost:5100/metrics
```

### Custom Metrics
Expand Down Expand Up @@ -374,7 +374,7 @@ scrape_configs:
- job_name: webhookengine
scrape_interval: 15s
static_configs:
- targets: ["localhost:5128"]
- targets: ["localhost:5100"]
```

## Message Lifecycle
Expand Down
7 changes: 5 additions & 2 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,18 @@ WebhookEngine/
│ │ │ └── PasswordHasher.cs
│ │ ├── Controllers/
│ │ │ ├── ApplicationsController.cs
│ │ │ ├── AuditLogsController.cs # Audit log query (v0.1.6)
│ │ │ ├── AuthController.cs # Dashboard login/logout/me
│ │ │ ├── DashboardAnalyticsController.cs # Overview stats, timeline
│ │ │ ├── DashboardEndpointController.cs # Dashboard endpoint management
│ │ │ ├── DashboardMessagesController.cs # Dashboard message operations
│ │ │ ├── DashboardPortalController.cs # Portal access lifecycle (v0.2.0)
│ │ │ ├── DevTrafficController.cs # Dev traffic generator controls
│ │ │ ├── EndpointsController.cs
│ │ │ ├── EventTypesController.cs
│ │ │ ├── HealthController.cs
│ │ │ └── MessagesController.cs
│ │ │ ├── MessagesController.cs
│ │ │ └── PortalEndpointsController.cs # Embeddable consumer portal (v0.2.0)
│ │ ├── Hubs/
│ │ │ └── DeliveryHub.cs # SignalR hub + SignalRDeliveryNotifier
│ │ ├── Middleware/
Expand Down Expand Up @@ -355,7 +358,7 @@ Optional CIDR positive-list per endpoint (`Endpoint.AllowedIpsJson`). When confi

Admin actions write to the append-only `audit_logs` table with `actor_user_id`, `actor_email`, `application_id`, `entity_type`, `entity_id`, `action`, `before_snapshot`, `after_snapshot`, and `request_id` (mirroring `X-Request-Id` for log cross-correlation). The table holds **no foreign keys** so rows survive the cascade when an application or endpoint is deleted. `ApplicationsController.Delete` reads `MessageRepository.CountAsync(...)` *before* the delete and folds the count into the audit `before_snapshot` so post-incident reconstruction can recover scale. `GET /api/v1/dashboard/audit` exposes the table with cursor pagination and per-app, per-entity, per-action filters.

### 3.5 HTTP Delivery Service
### 3.8 HTTP Delivery Service

```csharp
public class HttpDeliveryService : IDeliveryService
Expand Down
5 changes: 3 additions & 2 deletions docs/PRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# WebhookEngine — Open-Source Webhook Delivery Infrastructure

**Version:** 1.0
**Last Updated:** 2026-03-02
**Last Updated:** 2026-05-29
**Status:** Active — Phase 2 (Traction & Feedback)

---
Expand Down Expand Up @@ -65,13 +65,14 @@ Regulated industries (fintech, healthtech, government) and GDPR/KVKK-conscious c

### 3.2 Non-Goals (explicitly out of scope for MVP)
- Incoming webhook gateway (receiving webhooks from third parties)
- Webhook payload transformation/mapping
- GraphQL API (REST only for MVP)
- Kafka/RabbitMQ as queue backend (PostgreSQL-based queue for MVP)
- Email/SMS/push notification channels (that's the Notification Infrastructure project)
- Mobile SDK
- Kubernetes Helm charts (Docker Compose only for MVP)

> **Update:** Webhook payload transformation/mapping was an MVP non-goal but has since been delivered post-MVP as opt-in per-endpoint JMESPath transformation (see [ADR-003](adr/adr-003-payload-transformation-proposal.md)). It is no longer out of scope.

---

## 4. User Personas
Expand Down
10 changes: 5 additions & 5 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# WebhookEngine — Strategic Roadmap

**Last Updated:** 2026-05-10
**Last Updated:** 2026-05-29
**Status:** Active — Phase 2 (Traction & Feedback)
**Latest release:** v0.2.0 (2026-05-10) — first minor release; embeddable customer portal (B1 Steps 1-5): portal engine surface (`/api/v1/portal/*`), HS256 JWT middleware, per-app dynamic CORS, operator dashboard controls, and `<PortalAccessModal />` UI.
**Latest release:** v0.2.2 (2026-05-29) — maintenance patch (dependency refresh + CI Bun/lockfile fix). The embeddable customer portal landed in v0.2.0 (B1 Steps 1-5: `/api/v1/portal/*` surface, HS256 JWT middleware, per-app dynamic CORS, operator dashboard controls, `<PortalAccessModal />`); v0.2.1 hardened it (portal CORS deny-cache, PUT→PATCH, validator consolidation); and the `@webhookengine/endpoint-manager` npm package shipped in the `portal-v0.1.0` tag (2026-05-11).

> **Note:** Phase 1 is complete (launch posts and the engineering blog post remain deferred). Phase 2 core tasks (2.2, 2.3, 2.4, **2.5 payload transformation across all three rollout phases**, 2.7 application layer cleanup) are done. v0.1.6 added eight follow-on features (endpoint test webhook, validate-time URL guard, per-resource overrides, IP allowlist, audit log, SignalR endpoint health, TanStack Query data layer). v0.2.0 adds the embeddable customer portal (engine + dashboard half). The `@webhookengine/endpoint-manager` npm package (B1 Step 7 / Step 11) lands in a follow-up `portal-v0.1.0` tag. Remaining Phase 2 items (TypeScript SDK gated on demand signal, comparison / best-practice blog posts) are planned.
> **Note:** Phase 1 is complete (launch posts and the engineering blog post remain deferred). Phase 2 core tasks (2.2, 2.3, 2.4, **2.5 payload transformation across all three rollout phases**, 2.7 application layer cleanup) are done. v0.1.6 added eight follow-on features (endpoint test webhook, validate-time URL guard, per-resource overrides, IP allowlist, audit log, SignalR endpoint health, TanStack Query data layer). v0.2.0 adds the embeddable customer portal (engine + dashboard half). The `@webhookengine/endpoint-manager` npm package (B1 Step 7 / Step 11) shipped in the `portal-v0.1.0` tag (2026-05-11). Remaining Phase 2 items (TypeScript SDK gated on demand signal, comparison / best-practice blog posts) are planned.

---

Expand Down Expand Up @@ -140,7 +140,7 @@ Eight new features, three rounds of dashboard polish, three reviewer-finding fix

## v0.2.0 — Embeddable Customer Portal (2026-05-10)

First minor release. The engine half and operator dashboard half of the embeddable customer portal land here. The `@webhookengine/endpoint-manager` React package lands in a follow-up `portal-v0.1.0` tag.
First minor release. The engine half and operator dashboard half of the embeddable customer portal land here. The `@webhookengine/endpoint-manager` React package shipped in the follow-up `portal-v0.1.0` tag (2026-05-11).

| # | Task | Status |
|---|------|--------|
Expand All @@ -153,7 +153,7 @@ First minor release. The engine half and operator dashboard half of the embeddab
| — | Tailwind 4.2.4 → 4.3.0 | done |
| — | Dependabot npm `bun.lock` auto-sync workflow | done |
| — | Documentation drift sync (ADR-003 Accepted, stack version lines) | done |
| B1 Step 7 | `@webhookengine/endpoint-manager` npm package | planned (`portal-v0.1.0` tag) |
| B1 Step 7 | `@webhookengine/endpoint-manager` npm package | done (`portal-v0.1.0`, 2026-05-11) |

---

Expand Down
Loading