Skip to content

Add Resend email capability tool - #1

Merged
shadowbrush merged 3 commits into
mainfrom
codex/resend-email-tool
Jul 14, 2026
Merged

Add Resend email capability tool#1
shadowbrush merged 3 commits into
mainfrom
codex/resend-email-tool

Conversation

@shadowbrush

@shadowbrush shadowbrush commented Jul 14, 2026

Copy link
Copy Markdown
Member

What changed

  • add a stateless, DB-less, internal-only Resend capability service
  • expose bearer-gated POST /ops/send-email plus health and discovery routes
  • accept an organization API key and fixed sender inline for one authenticated internal request, without persisting or logging them
  • retain optional platform RESEND_API_KEY + RESEND_FROM fallback configuration for core-approved pilot organizations
  • reject partial or malformed inline/platform credential pairs
  • restrict v1 to one recipient, a fixed sender, a subject, and a plain-text body
  • forward caller-provided idempotency keys to Resend without local retries
  • map provider failures to a stable typed error catalog without logging email content or credentials
  • add a production Dockerfile, CI, documentation, and comprehensive fake-fetch/Supertest coverage

Why

Headless Hadron runs need a provider-isolated way to send outbound email without making the platform Resend account the normal multi-tenant path. hadron-server owns encrypted organization credentials, identity, policy, quota, ticket authorization, and platform-fallback admission; this sidecar only uses the selected credentials for an already-authorized Resend request.

Validation

  • npm test — 25 tests passed
  • npm run typecheck
  • npm run build

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces hadrontool-resend, a stateless microservice designed to send emails via Resend. The implementation includes Express-based routing, Zod configuration and input validation, a custom structured logger, and robust error handling. Feedback on the changes highlights critical runtime bugs in Zod usage, specifically the incorrect use of z.email() and z.flattenError. Additionally, improvements are suggested to prevent prototype pollution when looking up operations, and to increase the Express JSON body parser limit to 128kb to safely accommodate large multi-byte email bodies.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/ops/index.ts
Comment thread src/config.ts
Comment thread src/routes/ops.ts Outdated
Comment thread src/ops/index.ts Outdated
Comment thread src/app.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9b556f3457

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/app.ts Outdated
@shadowbrush

Copy link
Copy Markdown
Member Author

From Claude Code:

Code Review: PR #1 — Add Resend email capability tool

Overview

Introduces hadrontool-resend, a stateless, DB-less internal microservice that sends email via Resend on behalf of hadron-server-authorized requests. Well-scoped MVP: single POST
/ops/send-email op, bearer gate, all-or-nothing inline credential override, optional platform fallback, typed error catalog, 25 fake-fetch/supertest tests, Dockerfile, CI. Zero
persistence, zero retries, zero credential logging — the security model matches the stated design.

Prior review feedback — my take

The auto-reviewers flagged several items. Verifying against package.json (zod ^4.3.6) and the tests-pass status:

  • Gemini "critical" — z.email() doesn't exist: False positive. Zod v4 added top-level z.email(). Tests would fail on load if this were wrong. src/ops/index.ts:17 is correct.
  • Gemini "high" — z.flattenError doesn't exist: False positive. Also new in Zod v4 (z.flattenError(err)). src/config.ts:24 is correct.
  • Gemini "medium" — prototype pollution via OPERATIONS[name] (src/routes/ops.ts:38, src/ops/index.ts:79): Worth fixing. POST /ops/toString currently resolves to
    Object.prototype.toString, passes the truthy check, then crashes with a 500 when .run is invoked (caught by handler, but ugly). Switching to Object.hasOwn(OPERATIONS, name) in the
    router turns it into a clean 404 unknown_operation. Trivial change, worth taking.
  • Gemini "medium" — 64KB body-parser cap vs 20 000-char text: Worth taking. UTF-8 lets 20 000 chars reach ~80KB, so a valid payload can be rejected by the parser before Zod ever sees
    it. Bump express.json({ limit: '128kb' }) in src/app.ts:25.
  • Codex P2 — parser errors return unstable bad_request (src/app.ts:56): Worth taking. hadron-server passes the error code through verbatim, and bad_request isn't in src/errors.ts. Map
    parser failures to validation_error (and avoid echoing the raw parser message, which can quote payload bytes) — either extend the catalog explicitly or wrap in ValidationError.

Additional observations

  • Silent error swallowing in resend.ts:59-60: when res.json() throws for a non-abort reason (e.g. non-JSON provider response), payload becomes null, the request continues, and only the
    id extraction at line 76-77 catches it — mapping to upstream_unreachable. Fine, but a logger.warn on the parse failure would help debugging. Optional.
  • extraFields() returns without override: src/errors.ts:24, 43, 53, 81 — these override the base method but don't declare override. TypeScript's noImplicitOverride isn't on, so it
    compiles; if it ever gets enabled these would fail. Minor.
  • Idempotency-key regex /^[A-Za-z0-9._:-]+$/ (src/ops/index.ts:20) — solid, but worth noting Resend's own Idempotency-Key header spec allows a broader charset. Constraining tighter than
    the provider is fine defensively; just be aware if callers ever want UUIDs with slashes.
  • Log-leak assertion in tests: ops.test.ts:114-127 is a great pattern — it asserts recipient/subject/body/apiKey never appear in body or logs. Would strengthen further by also spying on
    stdout.write in case a future logger switches transports, but current form is good.
  • No integration/live smoke test: acceptable for a v1 sidecar with fake-fetch coverage, but the README should mention how to run against real Resend in staging before cutting a release.

Style / conventions

Consistent with the "capability tool" pattern: injectable ResendDeps, no state, typed catalog, DB-free. Comments explain why (security intent, non-obvious constraints), not what.
AGENTS.md codifies the invariants for future contributors. Dockerfile note that the service is internal-only (no Traefik router) is a valuable deployment hint.

Recommendation

Ship after taking the four legitimate items (prototype-safe lookup, 128kb body limit, parser-error mapping, and closing out the false-positive reviews so they don't sit as noise on the
PR). Everything else is polish and can go in a follow-up.

@shadowbrush
shadowbrush merged commit 2fe4bf1 into main Jul 14, 2026
1 check passed
@shadowbrush
shadowbrush deleted the codex/resend-email-tool branch July 14, 2026 17:12
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