Repository navigation
feat: email passive checks (spec 02) - #27
Merged
Merged
Conversation
Step 2 of email-passive-checks: mirror checkheartbeat structure. EmailConfig holds a 48-char hex token (24 random bytes from crypto/rand). EmailChecker.Validate auto-generates the token if missing and fills sane name/slug defaults. Execute returns ErrNotExecutable — the worker will treat email checks passively the same way it treats heartbeats. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Step 3 of email-passive-checks: hook checkemail into the factory switches so spec/config parsing finds it. Same single-line pattern as every other check type.
Step 4 of email-passive-checks: rename executeHeartbeatJob →
executePassiveJob and dispatch on isPassiveCheckType so email
checks share the same overdue/grace-period logic. The result
output noun ("Heartbeat" / "Email") is parameterized on the
check type — the rest of the messages are protocol-agnostic.
Step 5 of email-passive-checks: the JMAP email handler needs to resolve a recipient token to a check across all orgs. Tokens are 24 random bytes (48 hex chars) so collisions are negligible — no org scoping is required. Postgres uses config->>'token'; sqlite uses json_extract. Both get a partial index on (token) WHERE type = 'email' AND deleted_at IS NULL so the lookup stays cheap as the checks table grows. Mock service in notifications gets the new method too.
Step 6 of email-passive-checks: a jmap.Handler implementation that turns inbound emails into check results. Recipient resolution: regex on the local-part for the 48-hex token, with an optional +status suffix (token+down@…). Status priority is plus-addressing > X-SolidPing-Status header > [STATUS] subject tag > default "up". Cc/Bcc are scanned in addition to To. Token-shaped recipients with no matching check return OutcomeRejected so the email moves to Processed and the chain stops; non-token recipients return OutcomeIgnored to let other handlers try. Once a check is resolved, recordResult mirrors the heartbeat service: SaveResultWithStatusTracking + ProcessCheckResult, skipping incident processing for "running". Output keeps the envelope (from, subject, messageId, receivedAt) for debugging.
Step 7 of email-passive-checks: register the email-check handler on the jmap.Manager during server bootstrap. The manager idles until the email_inbox system parameter is configured, so adding this handler is safe in all deployments — without a JMAP server configured nothing changes.
Spec 02 implemented across 7 commits on this branch: checkemail package, registry hook, worker passive generalization, db lookup + migration, JMAP handler, app wiring. Backend builds, lints clean, tests pass in isolation.
fclairamb
enabled auto-merge (squash)
May 2, 2026 02:02
Merged
fclairamb
added a commit
that referenced
this pull request
Jun 23, 2026
🤖 I have created a release *beep* *boop* --- ## [0.1.0](v0.0.0...v0.1.0) (2026-06-22) ### Features * add check type registry, sample configs, notification senders, and observability integrations ([eea049e](eea049e)) * batch — Warning/Degraded statuses, SSL/Docker check upgrades, MCP OAuth 2.1, config-as-code ([#85](#85)) ([936d830](936d830)) * batch spec implementations (dashboard UI, jobs, notifications, SaaS, perf) ([#78](#78)) ([fe003e4](fe003e4)) * bulk feature integration (discovery, web push, badges, webhooks, and more) ([#73](#73)) ([a976856](a976856)) * custom user-agent, refresh checks, vite plugin switch, CI updates ([#1](#1)) ([cc00858](cc00858)) * documentation site served at /docs (co-located in web/docs) ([#88](#88)) ([27d9ccd](27d9ccd)) * email check frontend (spec 03) ([#30](#30)) ([aebd106](aebd106)) * email inbox foundation via JMAP (spec 01) ([#24](#24)) ([0f3b0c1](0f3b0c1)) * email passive checks (spec 02) ([#27](#27)) ([ba5536f](ba5536f)) * **entitlements:** trim to MaxSSOUsers + MaxChecksPerMinute + status page history fixes ([#50](#50)) ([a007de0](a007de0)) * **entitlements:** trim to MaxSSOUsers + MaxChecksPerMinute, enforce both ([#49](#49)) ([d25cc5f](d25cc5f)) * group incident correlation (spec 04 backend v1) ([#31](#31)) ([1883986](1883986)) * **observability:** instrument hot path + add bench harness ([#60](#60)) ([c6604cc](c6604cc)) * **oncall:** searchable timezone dropdown + sidebar logo polish ([#46](#46)) ([4396898](4396898)) * per-IP HTTP rate limiting and concurrency limiting ([#59](#59)) ([f9cc273](f9cc273)) * soften HTTP rate limits with bounded queues + request timeout ([#63](#63)) ([025501b](025501b)) * SolidPing — distributed uptime monitoring platform ([eef4383](eef4383)) * **statuspages:** drag-and-drop resource reordering + dash conventions ([#41](#41)) ([cf910d4](cf910d4)) ### Bug Fixes * **deps:** update dependency i18next to v26 ([#26](#26)) ([d5d1b7e](d5d1b7e)) * **deps:** update dependency lucide-react to v1 ([#28](#28)) ([cdc8e8c](cdc8e8c)) * **deps:** update dependency react-i18next to v17 ([#29](#29)) ([13135d8](13135d8)) * **deps:** update dependency recharts to v3.8.1 ([#14](#14)) ([5d95a3b](5d95a3b)) * **deps:** update go dependencies (non-major) ([#16](#16)) ([2450f13](2450f13)) * **deps:** update go dependencies (non-major) ([#19](#19)) ([f842310](f842310)) * **deps:** update go dependencies (non-major) ([#37](#37)) ([c020eca](c020eca)) * **deps:** update go dependencies (non-major) ([#44](#44)) ([ff0bcd0](ff0bcd0)) * **deps:** update go dependencies (non-major) ([#52](#52)) ([2e3bc28](2e3bc28)) * **deps:** update go dependencies (non-major) ([#69](#69)) ([a65ed4c](a65ed4c)) * **deps:** update go dependencies (non-major) ([#77](#77)) ([a0920ff](a0920ff)) * **deps:** update go dependencies (non-major) to v1.4.2 ([#86](#86)) ([1eb7504](1eb7504)) * **deps:** update module github.com/aws/aws-sdk-go-v2/config to v1.32.18 ([#72](#72)) ([6406537](6406537)) * **deps:** update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.101.0 ([#39](#39)) ([f9c9563](f9c9563)) * **deps:** update module github.com/go-webauthn/webauthn to v0.17.3 ([#45](#45)) ([593931a](593931a)) * **deps:** update module github.com/go-webauthn/webauthn to v0.17.4 ([#70](#70)) ([fca1c97](fca1c97)) * **deps:** update module github.com/ibm/sarama to v1.48.1 ([#47](#47)) ([93ccf3a](93ccf3a)) * **deps:** update module github.com/ibm/sarama to v1.48.2 ([#54](#54)) ([e84d868](e84d868)) * **deps:** update module github.com/ibm/sarama to v1.49.0 ([#64](#64)) ([fd61bd7](fd61bd7)) * **deps:** update module github.com/oapi-codegen/runtime to v1.4.1 ([#65](#65)) ([a3a3e9f](a3a3e9f)) * **deps:** update module github.com/prometheus/common to v0.67.5 ([#61](#61)) ([095dd6f](095dd6f)) * **deps:** update module github.com/slack-go/slack to v0.24.0 ([#75](#75)) ([6e86d5b](6e86d5b)) * **deps:** update module golang.org/x/sys to v0.44.0 ([#42](#42)) ([a245730](a245730)) * **deps:** update module golang.org/x/sys to v0.45.0 ([#67](#67)) ([49c3d90](49c3d90)) * **deps:** update module golang.org/x/term to v0.43.0 ([#43](#43)) ([4a934f1](4a934f1)) * **deps:** update module google.golang.org/grpc to v1.81.0 ([#36](#36)) ([1be183a](1be183a)) * **deps:** update module google.golang.org/grpc to v1.81.1 ([#55](#55)) ([6bfac2d](6bfac2d)) * status lifecycle improvements and created/running result handling ([#5](#5)) ([fc64c7d](fc64c7d)) * **status0:** white page header and de-duplicated footer ([#48](#48)) ([77506a7](77506a7)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
This was referenced Sep 8, 2026
Closed
Closed
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
Implements spec 02 — email passive checks: a new
emailcheck type where a unique per-check email address (<48-hex-token>@<inbox-domain>) is monitored via the JMAP supervisor wired up in spec 01. Inbound mail is turned into check results the same way heartbeat pings are.checkemailpackage mirrorscheckheartbeat: 24-byte random token in config,ErrNotExecutableonExecute, registered in the checker registry.executePassiveJobpath; output messages are parameterized on a noun ("Heartbeat" / "Email").db.Service.GetCheckByEmailToken(postgres + sqlite) plus a partial index migration so the JMAP handler can resolve a token to a check globally.internal/handlers/emailcheck/JMAP handler. Recipient regex captures the token and an optional+statusplus-suffix; status priority is plus-addressing >X-SolidPing-Statusheader >[STATUS]subject tag > defaultup. Token-shaped recipients with no matching check areOutcomeRejectedso they move to Processed.email_inboxsystem parameter is configured, so this is safe in all deployments.Test plan
🤖 Generated with Claude Code