{"id":49572662,"url":"https://github.com/dorianneto/hookyard","last_synced_at":"2026-05-03T15:09:59.375Z","repository":{"id":347550904,"uuid":"1194442595","full_name":"dorianneto/hookyard","owner":"dorianneto","description":"The Open Source WaaS that never drops a package.","archived":false,"fork":false,"pushed_at":"2026-04-17T19:52:24.000Z","size":532,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-17T21:32:09.112Z","etag":null,"topics":["monolith","react","symfony","waas","webhook","webhooks"],"latest_commit_sha":null,"homepage":"https://hookyard.dorianneto.com","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dorianneto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-28T11:11:46.000Z","updated_at":"2026-04-17T19:52:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dorianneto/hookyard","commit_stats":null,"previous_names":["dorianneto/webhookapp","dorianneto/hookyard"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/dorianneto/hookyard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorianneto%2Fhookyard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorianneto%2Fhookyard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorianneto%2Fhookyard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorianneto%2Fhookyard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dorianneto","download_url":"https://codeload.github.com/dorianneto/hookyard/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorianneto%2Fhookyard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32573440,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T06:36:36.687Z","status":"ssl_error","status_checked_at":"2026-05-03T06:36:09.306Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["monolith","react","symfony","waas","webhook","webhooks"],"created_at":"2026-05-03T15:09:58.779Z","updated_at":"2026-05-03T15:09:59.366Z","avatar_url":"https://github.com/dorianneto.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003ca href=\"https://hookyard.dorianneto.com\" target=\"_blank\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/dorianneto/hookyard-art/refs/heads/main/h-white-logo.png\" width=\"400\" alt=\"HookYard Logo\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n# About HookYard\n\nOpen Source **Webhook-as-a-Service (WaaS)** platform that receives webhooks from third-party services and fans them out to user-defined destination URLs with automatic retries and a delivery dashboard.\n\n\u003cdetails\u003e\n\u003csummary\u003eThe metaphor\u003c/summary\u003e\n\n\u003e _A rail yard is a place where trains (incoming webhooks) arrive, get sorted by destination, and dispatched out on different tracks (endpoints). Cars can be re-routed if a track fails — just like retries. The yard master (the worker) orchestrates everything without the sender needing to know the details._\n\n| Rail yard concept | WaaS concept |\n|-------------------|--------------|\n| Incoming train | Inbound webhook (POST /in/{uuid}) |\n| Rail yard | HookYard platform |\n| Track / destination | Endpoint URL |\n| Dispatch | Fan-out to endpoints |\n| Re-routing on failure | Automatic retry (5 attempts) |\n| Yard master | Queue worker |\n| Cargo manifest | Delivery dashboard |\n\n\u003c/details\u003e\n\n## Stack\n\n| Layer | Technology |\n|-------|-----------|\n| Backend | PHP 8.4 / Symfony 7 |\n| Frontend | React 18 + TypeScript + Vite + shadcn/ui + Tailwind CSS v4 |\n| Database | PostgreSQL 17 |\n| Queue | Symfony Messenger (AWS SQS) |\n| Deployment | AWS Elastic Beanstalk (monolith) |\n\n## Architecture\n\nThe backend follows **Hexagonal Architecture** (Ports \u0026 Adapters), keeping business logic completely isolated from the framework.\n\n\u003cdetails\u003e\n\u003csummary\u003eSee diagram\u003c/summary\u003e\n\n```mermaid\ngraph TD\n    subgraph Driving[\"Driving Adapters (input)\"]\n        HTTP[\"HTTP Controllers\\nsrc/Controller/\"]\n        Worker[\"Queue Worker\\n(Messenger Handlers)\"]\n    end\n\n    subgraph Application[\"Application Layer\\nsrc/Application/\"]\n        UC[\"Use Cases\\nUseCase/\"]\n        Ports[\"Ports (interfaces)\\nPort/\"]\n    end\n\n    subgraph Domain[\"Domain Layer\\nsrc/Domain/\"]\n        Entities[\"Entities \u0026 Value Objects\\nUser, Source, Endpoint\\nEvent, DeliveryAttempt\"]\n        Rules[\"Business Rules\\n\u0026 Exceptions\"]\n    end\n\n    subgraph Driven[\"Driven Adapters (output)\"]\n        DB[\"Doctrine Repositories\\nsrc/Infrastructure/Persistence/\"]\n        Security[\"Security Handlers\\nsrc/Security/\"]\n        Ext[\"External HTTP\\n(outbound deliveries)\"]\n    end\n\n    HTTP --\u003e UC\n    Worker --\u003e UC\n    UC --\u003e Ports\n    UC --\u003e Entities\n    Ports -.-\u003e|implemented by| DB\n    Ports -.-\u003e|implemented by| Ext\n    DB --\u003e Security\n\n    style Domain fill:#1a3a2a,color:#fff,stroke:#2d6a4f\n    style Application fill:#1a2a3a,color:#fff,stroke:#2d4f6a\n    style Driving fill:#2a1a1a,color:#fff,stroke:#6a2d2d\n    style Driven fill:#2a2a1a,color:#fff,stroke:#6a5d2d\n```\n\u003c/details\u003e\n\n### Layer Rules\n\n- **Domain** (`src/Domain/`) — pure PHP: entities, value objects, domain exceptions. Zero imports from `Symfony\\` or `Doctrine\\`.\n- **Application** (`src/Application/`) — use cases that orchestrate domain objects through port interfaces.\n- **Infrastructure** (`src/Infrastructure/`, `src/Controller/`, `src/Security/`) — Symfony/Doctrine adapters that implement the ports and expose HTTP endpoints.\n\n### Webhook Delivery Flow\n\n```\nPOST /in/{uuid}\n    → persist Event\n    → enqueue one Messenger message per active Endpoint\n    → return 200 OK immediately\n\nWorker:\n    → POST raw body + headers to Endpoint URL\n    → adds X-Webhook-Event-Id header\n    → 5 attempts: immediate → 30s → 5m → 30m → 2h\n    → recomputes events.status atomically (pending / delivered / failed)\n```\n\n## Development\n\n### Start all services\n\n```bash\ndocker compose up -d\n```\n\n### Backend (inside container or host)\n\n```bash\nphp bin/console doctrine:migrations:diff      # generate migration from entity changes\nphp bin/console doctrine:migrations:migrate   # apply pending migrations\nphp bin/phpunit                               # run all tests\n```\n\n### Frontend\n\n```bash\nnpm run build  # production build → public/\nnpm run watch  # Vite dev watch\n```\n\nUI is built with **shadcn/ui** components and **Tailwind CSS v4**. All components live in `frontend/src/components/ui/`.\n\n## Data Model\n\n\u003cdetails\u003e\n\u003csummary\u003eSee diagram\u003c/summary\u003e\n\n\n```mermaid\nerDiagram\n    users {\n        int id PK\n        string email\n        string password\n        timestamp created_at\n    }\n\n    sources {\n        int id PK\n        int user_id FK\n        string name\n        uuid inbound_uuid\n        timestamp created_at\n    }\n\n    endpoints {\n        int id PK\n        int source_id FK\n        string url\n        timestamp created_at\n    }\n\n    events {\n        int id PK\n        int source_id FK\n        string method\n        json headers\n        text body\n        enum status\n        timestamp received_at\n    }\n\n    event_endpoint_deliveries {\n        int id PK\n        int event_id FK\n        int endpoint_id FK\n        enum status\n        timestamp created_at\n        timestamp updated_at\n    }\n\n    delivery_attempts {\n        int id PK\n        int event_id FK\n        int endpoint_id FK\n        smallint attempt_number\n        smallint status_code\n        text response_body\n        int duration_ms\n        timestamp attempted_at\n    }\n\n    users ||--o{ sources : owns\n    sources ||--o{ endpoints : has\n    sources ||--o{ events : receives\n    events ||--o{ event_endpoint_deliveries : tracks\n    endpoints ||--o{ event_endpoint_deliveries : tracks\n    event_endpoint_deliveries ||--o{ delivery_attempts : logs\n```\n\u003c/details\u003e\n\n### Key relationships\n\n- A **Source** belongs to one user and has many **Endpoints** and many **Events**.\n- When an Event arrives, one **`event_endpoint_deliveries`** row is created per active Endpoint (unique on `(event_id, endpoint_id)`).\n- Each delivery row accumulates up to 5 **`delivery_attempts`** (exponential backoff: immediate → 30s → 5m → 30m → 2h).\n- `events.status` (`pending` / `delivered` / `failed`) is a denormalized cache recomputed atomically from all delivery rows every time a delivery row changes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdorianneto%2Fhookyard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdorianneto%2Fhookyard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdorianneto%2Fhookyard/lists"}