Skip to content

feat(webhooks): webhook framework — inbound verify + outbound dispatch + spec block (#184)#191

Merged
tonydspaniard merged 6 commits into
masterfrom
feat/184-webhooks
May 31, 2026
Merged

feat(webhooks): webhook framework — inbound verify + outbound dispatch + spec block (#184)#191
tonydspaniard merged 6 commits into
masterfrom
feat/184-webhooks

Conversation

@tonydspaniard
Copy link
Copy Markdown
Member

Webhook framework — epic #184

Ships the univeros/webhooks sub-package and its scaffolder integration: storage contracts + signing primitives, an inbound PSR-15 verify middleware, an outbound dispatcher with retry / dead-letter / replay, and a webhook: spec block the scaffolder wires automatically. Closes the second "Laravel AI-era property-gap" (alongside idempotency, #171).

Sub-issues delivered in this PR

  • univeros/webhooks: storage contracts + adapters + signing primitives #185univeros/webhooks foundation (1581c18)
    HMAC-SHA256 / HMAC-SHA512 / Ed25519 signers (constant-time verify via hash_equals / sodium), SignerRegistry, EnvSecretResolver, inbound InboundDeduplicatorInterface + InMemory/Redis adapters, outbound Delivery value object + DeliveryStatus enum + DeliveryStoreInterface with InMemory/Redis adapters. No coupling to univeros/http.

  • WebhookVerifyMiddleware: inbound PSR-15 (signature + timestamp window + dedupe) #186WebhookVerifyMiddleware + ActionAwareWebhookVerifyMiddleware (4c45b24)
    Inbound PSR-15: signature check, timestamp replay window (past + future), event-id dedupe with synthetic fallback, idempotent replay (200 + Webhook-Replayed: true). Handler throw or 5xx releases the dedupe claim so the sender can retry. Body rebuilt for downstream handlers. Auto-wiring variant reads the Action's static webhook() accessor.

  • WebhookDispatcher + Messenger handler: outbound POST with retry / dead-letter / replay #187WebhookDispatcher + Messenger handler + retry/DLQ + replay CLI (7a740dc)
    Application-facing dispatcher records a Delivery and dispatches a WebhookMessage via univeros/messaging. Worker-side WebhookHandler performs the signed POST (X-Signature / X-Timestamp / X-Event-Id / X-Delivery-Id), retries transient 5xx/network via RecoverableMessageHandlingException, dead-letters after RetryPolicy::maxAttempts via UnrecoverableMessageHandlingException. bin/altair webhook:replay <id> (full id or unique prefix) + bin/altair webhook:show-failed.

  • webhook: spec block + scaffolder integration (AST + parser + validator + emitter wiring) #188webhook: spec block + scaffolder integration (cb28d48)
    WebhookSpec AST node, parser + validator (direction / signer / secret-name / duration / retry rules), webhook() accessor emitted on inbound Actions, WebhookDispatcherBindingEmitter emitting app/Webhooks/<Name>WebhookDispatcher.php for outbound specs. Byte-identical scaffold output when the block is absent.

Verification

  • Webhooks + scaffold webhook tests: 214 passing (6 Redis-gated skips).
  • Full tests/Scaffold + tests/Determinism: 250 passing — no regressions, determinism intact.
  • PHPStan (level 8) on src/Altair/Webhooks + src/Altair/Scaffold: 0 errors.
  • CS-Fixer clean.

Run tests locally with --no-coverage (no xdebug/pcov here; failOnWarning trips on the missing coverage driver). CI has the driver.

Still open (tracked, not in this PR yet)

Test plan

  • vendor/bin/phpunit tests/Webhooks --no-coverage
  • vendor/bin/phpunit tests/Scaffold tests/Determinism --no-coverage
  • vendor/bin/phpstan analyse src/Altair/Webhooks src/Altair/Scaffold --memory-limit=1G
  • CI composer qa (cs + stan + test with coverage driver)

Part of #184.

Antonio Ramirez added 6 commits May 31, 2026 09:21
…pters (#185)

Foundation sub-package for the webhook epic (#184): HMAC-SHA256/512 +
Ed25519 signers (constant-time verify), SignerRegistry, EnvSecretResolver,
inbound dedupe contract + InMemory/Redis adapters, and the outbound
Delivery value object + DeliveryStatus enum + store contract with
InMemory/Redis adapters. No coupling to univeros/http.
Greens the PR's Static Analysis and Determinism gates:
- Rector: catch-var rename, never return types on always-throwing helpers,
  import ordering / quote-escape autofixes across the webhooks code + tests.
- Determinism (#74): regenerate .agent/ on PHP 8.3 (the gate's minor) so the
  new univeros/webhooks package + scaffold additions are indexed
  (codemaps/webhooks.md, packages/webhooks.md, manifest.json, scaffold codemap).
- WebhookHandler: onTransientFailure/deadLetter always throw, so they're
  typed never; drop the now-unreachable return statements PHPStan flagged
  (deadCode.unreachable at WebhookHandler.php:125).
- Regenerate .agent/ manifests so the determinism gate sees the new
  univeros/webhooks package (MANIFEST.md, packages/webhooks.md,
  packages/scaffold.md).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant