Verify official Resend Go SDK compatibility - #55
Draft
yhay81 wants to merge 32 commits into
Draft
Conversation
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
## What changed - centralize bounded error classification for application, provider, network, timeout, and invalid-data failures - replace raw exception text in API, worker, inbound, SES-event, and local queue logs with stable categories - sanitize retained email and webhook failure fields while preserving retry behavior - generate API request IDs server-side and stop logging caller-controlled paths or correlation headers - remove addresses and subjects from local acceptance logs and suppress non-JSON CLI response bodies - expose the new application-failure metrics on the operations dashboard and document the privacy/diagnostic contract ## Why Provider, network, parser, and queue exception strings are untrusted. They can include recipient data, webhook endpoints, response fragments, or credentials. Copying them into CloudWatch or long-lived failure fields conflicts with HayaSend's privacy boundary and makes public support workflows harder to operate safely. ## Impact Operators now correlate failures with server-generated request IDs, opaque email or queue IDs, allowlisted job/HTTP metadata, and stable error categories. Detailed provider diagnostics remain available from the originating AWS service under controlled access. `Email.error` and `WebhookDelivery.last_error` intentionally no longer reproduce external exception strings. Retry, partial-batch failure, DLQ, and alarm behavior is preserved. SES-event and inbound Lambda rethrows are sanitized so the Lambda runtime cannot emit the original exception text. ## Validation - `npm run check` - `npm test` — 21 files, 141 tests - `npm run build` - `cfn-lint==1.53.2 template.yaml` - `npx @redocly/cli@2.12.5 lint openapi.yaml` - Lambda entry-point bundling with esbuild - `actionlint .github/workflows/*.yml` - `gitleaks detect --source . --redact` - `npm audit --omit=dev --audit-level=high` - `npm pack --dry-run` - Dockerfile check, image build, and read-only/capability-dropped container smoke test with adversarial metadata; no injected sender, recipient, subject, body, credential token, URL, or request ID appeared in container logs Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
## Outcome Complete HayaSend's GitHub Community Profile and give contributors one concise structure for explaining user impact, implementation tradeoffs, compatibility, operations, and verification. ## Approach - add a repository-wide pull request template rather than separate overlapping templates - lead with a private-vulnerability-reporting warning - ask for exact validation and explicitly gated checks - cover API/SDK compatibility, AWS/IAM/cost impact, migrations, rollback, security, and privacy without forcing empty boilerplate - include focused-change, test, documentation, sensitive-data, and DCO checks ## Impact New pull requests opened through GitHub receive the template automatically after the release branch reaches the default branch. There is no runtime, API, deployment, or dependency change. ## Verification - `npm run check` - `npm test` — 21 files, 141 tests - `actionlint .github/workflows/*.yml` - `gitleaks git --staged --redact` - `git diff --cached --check` Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
## Outcome Reject malformed HayaSend scoped API keys as unauthorized before consulting DynamoDB or the bootstrap-secret provider. ## Root cause `ApiKeyService.authenticate` previously treated any value beginning with `re_hs_` and containing a dot as a scoped key. The attacker-controlled substring before the dot became a DynamoDB partition-key component without first matching the format HayaSend actually issues. API Gateway HTTP APIs accept up to 10,240 bytes across the request line and headers, while a DynamoDB partition key is limited to 2,048 bytes. A long malformed Bearer value could therefore turn an authentication failure into a DynamoDB validation error, API 500, and `ApiErrors` alarm instead of a bounded 401. References: - https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-quotas.html - https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Constraints.html ## Approach - accept only the exact issued shape: lowercase `key_<32 hex>` ID plus a 43-character base64url secret - reject malformed `re_hs_*.*` values before Store or Secrets Manager access - preserve valid scoped-key lookup, revocation/expiry checks, constant-time hash comparison, and existing bootstrap behavior - move release-bound cost and telemetry entries from `Unreleased` into the v0.1.0 section and record this fix there ## Impact Valid keys and API responses are unchanged. Invalid scoped-looking credentials now consistently return the existing 401 error without consuming a DynamoDB read, surfacing a storage exception, or triggering internal-error telemetry. ## Validation - `npm run check` - `npm test` — 21 files, 142 tests - `npm run build` - `cfn-lint==1.53.2 template.yaml` - `npx @redocly/cli@2.12.5 lint openapi.yaml` - Lambda entry-point bundling with esbuild - `actionlint .github/workflows/*.yml` - `npm audit --omit=dev --audit-level=high` — 0 vulnerabilities - `gitleaks git --staged --redact` - Dockerfile check, image build, and read-only/capability-dropped container smoke test using a 2,048-character malformed key Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
## What changed - split email creation into side-effect-free preparation and commit phases - preflight every message in a strict batch before persisting or queueing any sibling - document strict-only batch validation and add service/API regression coverage ## Why `createBatch` previously called `create` for every message concurrently. If one message referenced a missing or unpublished template, or an invalid uploaded attachment, another valid message could already be stored and queued even though the batch request returned an error. A caller retry could then duplicate delivery. The official Resend API uses strict batch validation by default, where one invalid message prevents the batch from being sent. HayaSend now matches that safety behavior for its service-level validation. ## Impact Successful single-email and batch response shapes are unchanged. Invalid batches now fail before any valid sibling is accepted. Permissive batch mode and distributed transaction handling for infrastructure failures remain out of scope. ## Validation - `npm run check` - `npm test` — 21 files, 144 tests - `npm run build` - `npm audit --omit=dev --audit-level=high` — 0 vulnerabilities - `cfn-lint==1.53.2 template.yaml` - Redocly 2.12.5 OpenAPI lint - Lambda entry-point bundles - `actionlint` - `gitleaks git --staged` - signed commit with DCO sign-off Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
## What changed - pin AWS integration credentials to the exact API Gateway ID and Region returned for the ephemeral CloudFormation stack - remove response bodies, API key identifiers, and arbitrary error messages from cleanup logs - read bounded template files through one `O_NOFOLLOW` file descriptor for both metadata checks and content reads - add endpoint-confusion regression tests and release notes ## Why CodeQL's first scan of the complete v0.1.0 pull request identified three new flows: a dynamic integration-test destination, clear-text cleanup logging, and a `stat(path)` / `readFile(path)` race. Even though the integration destination originates from a dedicated test stack, it carries a bootstrap credential and should fail closed if the URL differs from that exact stack. The logging and file race findings are actionable directly. ## Impact The AWS integration workflow now independently reads the `HttpApi` physical ID and the script accepts only its exact commercial API Gateway HTTPS hostname. Cleanup diagnostics remain useful without retaining response bodies or key identifiers. Template manifest behavior is unchanged, but the size/type check and read now operate on the same opened file. ## Validation - `npm run check` - `npm test` — 22 files, 146 tests - `npm run build` - endpoint-confusion and bootstrap-key output probes - `npm audit --omit=dev --audit-level=high` — 0 vulnerabilities - `cfn-lint==1.53.2 template.yaml` - Redocly 2.12.5 OpenAPI lint - `actionlint` - `gitleaks git --staged` - signed commit with DCO sign-off Closes CodeQL findings #3, #4, and #5 on release PR #30. Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
yhay81
added a commit
that referenced
this pull request
Jul 26, 2026
## Outcome Turn the accepted direction in #81 into a stage-gated implementation plan: production semantics first, then a provider-neutral Cloudflare proof, controlled FolioMCP dogfood, and only then agent-safe policy. ## What changed - define ten non-negotiable customer-owned data-plane invariants - document the current AWS gaps instead of treating aggregate status and client-triggered dispatch repair as the final model - specify canonical message, recipient, attempt, provider-event, and transactional-outbox records - define honest at-least-once boundaries and provider-acceptance ambiguity - require a versioned provider capability contract and common conformance report - add entry criteria, deliverables, exit evidence, and non-goals for five gates - ground the Cloudflare proof in current official Email Service, D1, R2, and Queues behavior - enumerate eleven focused implementation slices - classify all 23 post-v0.1 drafts exactly once as rebase/review, combine into the semantics series, or park - align the public roadmap and commercial/support boundary with a content-blind optional management plane ## Draft disposition The plan preserves production-focused work, combines #49/#51/#53 into an ordered semantics series, and parks #33/#55/#77/#82. Existing stacks #59→#61 and #73→#75 remain ordered. ## Post-release refresh - HayaSend v0.1.0 is fully released from protected `main`. - The former stacked ancestry was replaced with one GPG-signed plan commit on current `main`. - The PR now changes only `README.md`, `ROADMAP.md`, `SUPPORT.md`, `docs/commercial.md`, and the new `docs/execution-plan.md`. - No feature draft is included or authorized for premature merge. ## Validation - `npm ci` - `npm run check` - `npm test` — 22 files / 146 tests - `npm run build` - `npm audit --omit=dev --audit-level=high` — 0 vulnerabilities - `npm outdated --depth=0 --json` — no direct updates - Prettier check for all five changed Markdown files - `git diff --check` - signed commit with DCO sign-off Closes #83 Relates to #81 Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
This was referenced Jul 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
resend-gov3.11.0 SDKgo-sdkCI job with a fail-closed loopback endpoint guardWhy
Go adopters previously had no executable compatibility evidence for the official Resend SDK. That made the documented drop-in migration story weaker than the Node and Python paths and left SDK regressions undetected.
Impact
Go users can point the official SDK at HayaSend without maintaining a fork, and maintainers now get a pinned CI signal when the compatibility contract changes. The guard prevents this black-box test from sending to a non-loopback endpoint.
Validation
resend-gov3.11.0 black-box run: send/get/list/batch and single/batch idempotent replay passedgo mod verifygo vet ./...go test ./...npm run checknpm test(146 tests)npm run buildcfn-lint, Redocly OpenAPI validation, Lambda esbuild checksnpm audit --omit=dev --audit-level=high(0 vulnerabilities)npm pack --dry-runactionlintgit diff --checkCloses #54