Skip to content

Releases: jmarbutt/InngestDotNet

v1.4.4

10 Feb 22:30

Choose a tag to compare

Fix: Shorten x-inngest-sdk header

  • Shortened x-inngest-sdk header value from inngest-dotnet:v1.4.3 (21 chars) to cs:v1.4.4 (9 chars) to comply with Inngest's 10-character limit
  • Updated header in response headers, outbound registration requests, and sync payload

v1.4.3

28 Jan 01:57

Choose a tag to compare

Fix: Throw error when INNGEST_EVENT_KEY missing in production

Previously, SendEventsWithIdsAsync would silently fail and return an empty array when the event key was not configured in production mode. This made debugging difficult as events appeared to "send" but the response was just { "ids": [] }.

Now the SDK throws a clear InvalidOperationException explaining that INNGEST_EVENT_KEY is required for production event sending.

This fixes issues where fan-out patterns (cron → per-item workers) would silently fail because the parent job couldn't send events.

v1.4.2

16 Jan 01:28

Choose a tag to compare

Bug Fix

Fixed: PUT requests no longer require signature verification

Problem

The SDK was incorrectly requiring signature verification for PUT requests (sync/introspection), causing {"error":"Invalid signature"} when manually testing endpoints via curl or browser.

Solution

Moved signature verification to only apply to POST requests (function calls). According to the Inngest SDK spec:

  • PUT (sync) - Does NOT require signatures
  • GET (introspection) - Does NOT require signatures
  • POST (function calls) - Requires valid signature in production

This allows manual testing of sync endpoints while maintaining security for actual function invocations.

Full Changelog

v1.4.1...v1.4.2

v1.4.1

06 Jan 16:46

Choose a tag to compare

What's New

SDK Parity Features for Node.js Migration (#10)

Five critical features to enable seamless migration from the Node.js SDK:

  • Multi-constraint concurrency - Support both per-key serialization and global caps simultaneously
  • Idempotency TTL/Period support - Explicit duration control (24h, 1h, etc.) instead of implicit Inngest defaults
  • Retry/attempt helpers - IsFinalAttempt property and MaxAttempts tracking in function context
  • Structured logging scopes - function_id, run_id, event_id, and attempt automatically included in all logs
  • First-class onFailure handlers - Companion functions triggered on inngest/function.failed events via [OnFailure] attribute

Security & Auth Hardening (#9)

  • Fixed INNGEST_DEV parsing so false/0 no longer incorrectly disables signature checks
  • Signature verification now properly enforced only for PUT/POST requests
  • Returns 401 on invalid signatures with future-timestamp guard
  • Pinned .NET SDK version via global.json for consistent builds

Other Improvements

  • Cleaned all remaining build warnings
  • Aligned CI to .NET 9
  • Removed user IDE settings from repo

Stats

  • 23 files changed
  • 1,445 additions, 64 deletions
  • 20 new tests added (147 total)

Full Changelog: v1.4.0...v1.4.1

v1.4.0 - Flow Control Attributes

05 Jan 23:17
ac9998a

Choose a tag to compare

New Features

This release adds three new attributes for production-grade function configuration, specifically designed for payment processing and other critical workflows where data integrity is paramount.

[Throttle] Attribute

QUEUES events at a rate limit (unlike [RateLimit] which DROPS events). Critical for payment processing where losing events is unacceptable.

// Limit to 20 executions per minute per customer, queuing excess events
[Throttle(20, "1m", Key = "event.data.customerId")]

[Idempotency] Attribute

Prevents duplicate processing using a CEL expression key. Ensures only one execution per unique key (e.g., contribution ID).

// One receipt per contribution - prevents duplicate emails on retry
[Idempotency("event.data.contributionId")]

[Timeout] Attribute

Cancels hanging functions to prevent queue buildup. Supports both Start (queue timeout) and Finish (execution timeout).

// Cancel if function takes longer than 30 seconds to complete
[Timeout(Finish = "30s")]

// Cancel if queued longer than 1 minute or runs longer than 2 minutes
[Timeout(Start = "1m", Finish = "2m")]

Complete Example

[InngestFunction("payment-processor", Name = "Process Payment Webhook")]
[EventTrigger("payment/received")]
[Throttle(20, "1m", Key = "event.data.customerId")]  // Queue, don't drop
[Concurrency(1, Key = "event.data.paymentId")]       // Serialize per payment
[Idempotency("event.data.paymentId")]                // One execution per payment
[Timeout(Finish = "30s")]                            // Cancel if hanging
public class PaymentProcessorFunction : IInngestFunction
{
    // ...
}

Why These Features Matter

These features are required for migrating Node.js Inngest handlers to .NET, particularly for payment processing that cannot tolerate:

  • Duplicate charges from webhook retries → solved by [Idempotency]
  • Lost payments from rate limiting → solved by [Throttle]
  • Race conditions creating duplicate records → solved by [Concurrency] with key
  • Queue buildup from hanging requests → solved by [Timeout]

Closes #7

Full Changelog: v1.3.7...v1.4.0

v1.3.7 - Add JCS canonicalization for signature verification

15 Dec 21:27

Choose a tag to compare

What's Changed

Bug Fix: Signature verification now properly matches Inngest's algorithm using JCS canonicalization.

The Problem

The Go SDK (and likely the server) applies JSON Canonicalization Scheme (RFC 8785) before computing signatures. Our SDK was signing raw bytes without canonicalization, causing signature mismatches for event-triggered functions (while cron jobs with simpler payloads worked).

The Fix

  • Added JsonCanonicalizer class implementing RFC 8785 (JCS)
  • Signature verification now canonicalizes the JSON body before HMAC computation
  • Falls back to raw bytes verification for compatibility

JCS Canonicalization ensures:

  • Object keys are sorted lexicographically
  • No extraneous whitespace
  • Consistent number formatting
  • Deterministic output regardless of input formatting

Files Changed

  • Inngest/Internal/JsonCanonicalizer.cs (new)
  • Inngest/InngestClient.cs (updated signature verification)
  • Added 10 new tests for JCS canonicalizer

Full Changelog: v1.3.6...v1.3.7

v1.3.6 - Fix gzip-compressed POST signature verification

15 Dec 20:52

Choose a tag to compare

What's Changed

Bug Fix: Signature verification now works correctly for gzip-compressed POST requests.

The Problem

Inngest computes signatures on raw wire bytes (potentially gzip-compressed). When ASP.NET Core or a proxy decompresses the body before our code reads it, signature verification fails because we compute the HMAC on the decompressed bytes instead of the original compressed bytes.

The Fix

  • Added RawBodyMiddleware that captures raw request bytes early in the pipeline before any decompression
  • Updated VerifySignature() to use raw bytes when available for HMAC verification
  • Falls back to string-based verification for uncompressed requests

No User Changes Required

The middleware is automatically added when calling UseInngest(), so existing code works without modification.

Files Changed

  • Inngest/Internal/RawBodyMiddleware.cs (new)
  • Inngest/InngestMiddlewareExtensions.cs
  • Inngest/InngestClient.cs

Tests

  • Added 14 new tests for gzip signature verification
  • All 114 tests passing

Full Changelog: v1.3.5...v1.3.6

v1.3.5

15 Dec 20:33

Choose a tag to compare

Changes

  • Add enhanced logging for signature verification failures
  • Helps diagnose webhook signature issues by logging body details, timestamp, and key prefix
  • Detect and log empty body conditions that may indicate buffering issues

v1.3.4

13 Dec 23:58

Choose a tag to compare

Bug Fix

StepError now returns 500 to trigger Inngest retry mechanism

Previously, when a step threw an exception, the SDK wrapped it in StepInterruptException with StepOpCode.StepError and returned HTTP 206. Inngest interpreted 206 as "steps need scheduling" but couldn't handle StepError as a schedulable operation.

Changes

  • InngestClient now checks for StepError in StepInterruptException and returns HTTP 500 with proper error format to trigger retries
  • StepTools.Run now re-throws NonRetriableException and RetryAfterException instead of wrapping them, so they're handled by dedicated catch blocks
  • Added comprehensive tests for step error scenarios
  • Added example functions demonstrating error handling patterns

Behavior Summary

Exception Type HTTP Status X-Inngest-No-Retry Retry-After
Regular step error 500 false -
NonRetriableException 400 true -
RetryAfterException 500 false {seconds}

Full Changelog: v1.3.3...v1.3.4

v1.3.3

13 Dec 20:01

Choose a tag to compare

Bug Fixes

Major Fix: Step.SendEvent Now Works Correctly

This release fixes a critical issue where step.sendEvent would cause functions to restart indefinitely instead of properly progressing through steps.

Root Cause: The previous implementation used StepPlanned op code and didn't actually send events - it just told Inngest to handle it. But sendEvent is a "sync" step (like step.run) that should execute immediately and return results.

The Fix:

  • SendEvent now actually calls the Inngest Event API to send events
  • Returns StepRun op code with the result { ids: [...] } (like step.run does)
  • Added SendEventsDelegate to StepTools for event sending capability
  • Added SendEventsWithIdsAsync to InngestClient to return event IDs from the API

Changes

  • Inngest/Steps/StepTools.cs - Complete rewrite of SendEvent to match TypeScript SDK behavior
  • Inngest/Steps/StepOperation.cs - Added Name property for step type identification
  • Inngest/InngestClient.cs - Added SendEventsWithIdsAsync method, updated StepTools creation
  • Updated tests for new behavior

Expected Behavior After Fix

  1. First call: Function executes steps, hits sendEvent, SDK calls Event API, returns 206 with StepRun result
  2. Inngest memoizes the { ids: [...] } result
  3. Second call: Function receives memoized steps, sendEvent returns cached IDs, function continues to completion

🤖 Generated with Claude Code