From fee7f253b8f78a3e8a9df60093b74a6c76fcbbee Mon Sep 17 00:00:00 2001 From: Brandon Corbett Date: Tue, 28 Jul 2026 16:50:09 -0400 Subject: [PATCH] chore!: drop the bootstrap invite flow from the messaging contract The Seamless Auth API removed the admin bootstrap invite flow, and the Express adapter no longer requests that delivery, so nothing in the ecosystem calls sendBootstrapInviteEmail. The first admin is granted through OWNER_EMAIL now. Remove the operation from AuthMessagingService and AuthMessagingHandlers, its SendBootstrapInviteEmailInput type, the bootstrapInviteEmail override, and the default template builders. The product boundary in the README, AGENTS.md, and docs is three auth flows rather than four. --- AGENTS.md | 4 +- README.md | 4 +- docs/adopter-user-story.md | 1 - docs/architecture.md | 4 +- docs/provider-model.md | 14 ++---- docs/roadmap.md | 2 +- .../seamless-review-api/src/localSmoke.ts | 5 -- .../src/withAuthMessageAudit.ts | 22 +-------- packages/core/README.md | 1 - packages/core/src/messages.ts | 7 --- packages/core/src/service.ts | 47 ------------------- packages/core/src/transports.ts | 14 +----- 12 files changed, 11 insertions(+), 114 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7e3807f..0a69e3d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -62,7 +62,6 @@ The auth server currently needs messaging support for exactly these flows: - email OTP - SMS OTP - magic link email -- bootstrap invite email Treat those as the primary domain API for `0.1.0`. @@ -106,7 +105,7 @@ Keep the first release intentionally narrow. - a provider-agnostic core package - AWS email and SMS support - Twilio SMS support -- the four auth flows already used by SeamlessAuth +- the three auth flows already used by SeamlessAuth - docs and examples that show how an adopter API should consume the package ### Out of scope @@ -133,7 +132,6 @@ Bias toward APIs that would let the auth server keep call sites similar to: - `sendOtpEmail(...)` - `sendOtpSms(...)` - `sendMagicLinkEmail(...)` -- `sendBootstrapInviteEmail(...)` ## Style Guidance diff --git a/README.md b/README.md index 93c05cd..48cebb1 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ It gives SeamlessAuth a small, explicit way to deliver auth emails and SMS witho For `0.1.0`, the scope is intentionally narrow: - TypeScript packages only -- the four auth flows already used by SeamlessAuth +- the three auth flows already used by SeamlessAuth - AWS SES email - AWS SNS SMS - Twilio SMS @@ -25,7 +25,6 @@ SeamlessAuth needs to send exactly these auth-related messages today: - email OTP - SMS OTP - magic link email -- bootstrap invite email This repo packages that responsibility into: @@ -51,7 +50,6 @@ The core service keeps the auth-domain API small: - `sendOtpEmail(...)` - `sendOtpSms(...)` - `sendMagicLinkEmail(...)` -- `sendBootstrapInviteEmail(...)` ## Install diff --git a/docs/adopter-user-story.md b/docs/adopter-user-story.md index 407d617..8ad66dc 100644 --- a/docs/adopter-user-story.md +++ b/docs/adopter-user-story.md @@ -37,7 +37,6 @@ The intended flow is: - OTP email - OTP SMS - magic link email - - bootstrap invite email 5. SeamlessAuth delivers a good default experience without forcing the adopter to own message rendering. ## Why This Model Works diff --git a/docs/architecture.md b/docs/architecture.md index 9bf5bee..d3fd5b4 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -6,12 +6,11 @@ Provide a public auth-messaging package system for SeamlessAuth that sits behind ## Product Boundary -SeamlessAuth currently needs messaging support for four auth workflows: +SeamlessAuth currently needs messaging support for three auth workflows: - OTP email - OTP SMS - magic link email -- bootstrap invite email That is the right boundary for this repo. It should stay focused on auth delivery, not expand into a generic notification platform. @@ -112,7 +111,6 @@ Public operations: - `sendOtpEmail` - `sendOtpSms` - `sendMagicLinkEmail` -- `sendBootstrapInviteEmail` This keeps the API aligned with current SeamlessAuth responsibilities. diff --git a/docs/provider-model.md b/docs/provider-model.md index cc5bf5a..9aad07c 100644 --- a/docs/provider-model.md +++ b/docs/provider-model.md @@ -20,12 +20,11 @@ The provider model should separate: ## Launch Matrix -| Flow | Channel | AWS | Twilio | -| ---------------- | ------- | --- | ------ | -| Email OTP | Email | Yes | No | -| SMS OTP | SMS | Yes | Yes | -| Magic link | Email | Yes | No | -| Bootstrap invite | Email | Yes | No | +| Flow | Channel | AWS | Twilio | +| ---------- | ------- | --- | ------ | +| Email OTP | Email | Yes | No | +| SMS OTP | SMS | Yes | Yes | +| Magic link | Email | Yes | No | This is why a channel-based composition model is safer than a single `provider: "aws" | "twilio"` switch. @@ -46,14 +45,12 @@ export interface AuthMessagingHandlers { sendOtpEmail?: (input: SendOtpEmailInput) => Promise; sendOtpSms?: (input: SendOtpSmsInput) => Promise; sendMagicLinkEmail?: (input: SendMagicLinkEmailInput) => Promise; - sendBootstrapInviteEmail?: (input: SendBootstrapInviteEmailInput) => Promise; } export interface AuthMessagingService { sendOtpEmail(input: SendOtpEmailInput): Promise; sendOtpSms(input: SendOtpSmsInput): Promise; sendMagicLinkEmail(input: SendMagicLinkEmailInput): Promise; - sendBootstrapInviteEmail(input: SendBootstrapInviteEmailInput): Promise; } ``` @@ -96,7 +93,6 @@ Today that means: - OTP email subject/body - OTP SMS body - magic link email subject/body -- bootstrap invite email subject/body Transports deliver rendered content. They do not own auth business logic. diff --git a/docs/roadmap.md b/docs/roadmap.md index 0e8b25d..964d253 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -40,7 +40,7 @@ Ship and stabilize a realistic `0.1.0` for TypeScript users integrating Seamless - TypeScript packages publishable to npm - official support for AWS email/SMS and Twilio SMS -- support for the four current SeamlessAuth message flows +- support for the three current SeamlessAuth message flows - documented integration path for SeamlessAuth server-side adapters - tests for core logic and provider adapters - README and package docs that make setup understandable without reading source diff --git a/examples/seamless-review-api/src/localSmoke.ts b/examples/seamless-review-api/src/localSmoke.ts index 345776b..f24abe4 100644 --- a/examples/seamless-review-api/src/localSmoke.ts +++ b/examples/seamless-review-api/src/localSmoke.ts @@ -48,11 +48,6 @@ async function main() { to: "reviewer@example.com", magicLinkUrl: "https://review.example.com/verify-magic-link?token=xyz", }); - - await authMessaging.sendBootstrapInviteEmail({ - to: "admin@example.com", - inviteUrl: "https://review.example.com/login?bootstrapToken=bootstrap-token", - }); } void main(); diff --git a/examples/seamless-review-api/src/withAuthMessageAudit.ts b/examples/seamless-review-api/src/withAuthMessageAudit.ts index 2e2673c..04dd4ec 100644 --- a/examples/seamless-review-api/src/withAuthMessageAudit.ts +++ b/examples/seamless-review-api/src/withAuthMessageAudit.ts @@ -1,6 +1,5 @@ import type { MessagingClient, - SendBootstrapInviteEmailInput, SendMagicLinkEmailInput, SendOtpEmailInput, SendOtpSmsInput, @@ -32,15 +31,7 @@ export type AuthMessageAuditEntry = messageId?: string; } | { - operation: "sendBootstrapInviteEmail"; - status: "sent"; - to: string; - provider: string; - channel: "email" | "sms"; - messageId?: string; - } - | { - operation: "sendOtpEmail" | "sendOtpSms" | "sendMagicLinkEmail" | "sendBootstrapInviteEmail"; + operation: "sendOtpEmail" | "sendOtpSms" | "sendMagicLinkEmail"; status: "failed"; to: string; error: unknown; @@ -115,16 +106,5 @@ export function withAuthMessageAudit( throw error; } }, - - async sendBootstrapInviteEmail(input: SendBootstrapInviteEmailInput) { - try { - const result = await messaging.sendBootstrapInviteEmail(input); - await auditSuccess(audit, "sendBootstrapInviteEmail", input.to, result); - return result; - } catch (error) { - await auditFailure(audit, "sendBootstrapInviteEmail", input.to, error); - throw error; - } - }, }; } diff --git a/packages/core/README.md b/packages/core/README.md index 6998ba3..1809349 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -7,7 +7,6 @@ This package owns the shared messaging contracts and the auth-focused service su - OTP email - OTP SMS - magic link email -- bootstrap invite email It exposes: diff --git a/packages/core/src/messages.ts b/packages/core/src/messages.ts index 6fae3e5..7602d28 100644 --- a/packages/core/src/messages.ts +++ b/packages/core/src/messages.ts @@ -18,10 +18,3 @@ export interface SendMagicLinkEmailInput { from?: string; subject?: string; } - -export interface SendBootstrapInviteEmailInput { - to: string; - inviteUrl: string; - from?: string; - subject?: string; -} diff --git a/packages/core/src/service.ts b/packages/core/src/service.ts index 2d9323c..6340246 100644 --- a/packages/core/src/service.ts +++ b/packages/core/src/service.ts @@ -69,25 +69,6 @@ function buildMagicLinkHtml(appName: string, magicLinkUrl: string): string { ].join("\n"); } -function buildBootstrapInviteText(appName: string, inviteUrl: string): string { - return [ - `You have been invited to bootstrap ${appName}.`, - "", - "Use the link below to continue:", - inviteUrl, - ].join("\n"); -} - -function buildBootstrapInviteHtml(appName: string, inviteUrl: string): string { - return [ - "
", - `

Bootstrap invite for ${appName}

`, - "

Use the link below to continue:

", - `

${inviteUrl}

`, - "
", - ].join("\n"); -} - function buildOtpSmsText(appName: string, token: string | number): string { return `Your ${appName} verification code is: ${token}. No one will ever ask you for this code. Do not share it.`; } @@ -207,34 +188,6 @@ export function createAuthMessagingService( return email.send(message); }, - - async sendBootstrapInviteEmail(input) { - if (handlers?.sendBootstrapInviteEmail) { - return handlers.sendBootstrapInviteEmail(input); - } - - if (!email) { - throw new UnsupportedChannelError("email"); - } - - assertLikelyEmail(input.to, "input.to"); - assertLikelyUrl(input.inviteUrl, "input.inviteUrl"); - - const message = applyEmailOverride( - overrides?.bootstrapInviteEmail, - input, - { - to: input.to, - from: input.from ?? defaults?.emailFrom, - subject: input.subject ?? `${appName} - Bootstrap invite`, - text: buildBootstrapInviteText(appName, input.inviteUrl), - html: buildBootstrapInviteHtml(appName, input.inviteUrl), - }, - context, - ); - - return email.send(message); - }, }; } diff --git a/packages/core/src/transports.ts b/packages/core/src/transports.ts index 06da7d5..1f56964 100644 --- a/packages/core/src/transports.ts +++ b/packages/core/src/transports.ts @@ -1,9 +1,4 @@ -import type { - SendBootstrapInviteEmailInput, - SendMagicLinkEmailInput, - SendOtpEmailInput, - SendOtpSmsInput, -} from "./messages.js"; +import type { SendMagicLinkEmailInput, SendOtpEmailInput, SendOtpSmsInput } from "./messages.js"; import type { DeliveryResult, EmailMessage, SmsMessage } from "./types.js"; export interface EmailTransport { @@ -20,7 +15,6 @@ export interface AuthMessagingHandlers { sendOtpEmail(input: SendOtpEmailInput): Promise; sendOtpSms(input: SendOtpSmsInput): Promise; sendMagicLinkEmail(input: SendMagicLinkEmailInput): Promise; - sendBootstrapInviteEmail(input: SendBootstrapInviteEmailInput): Promise; } export interface AuthMessageOverrideContext { @@ -43,18 +37,12 @@ export interface AuthMessageOverrides { defaults: EmailMessage, context: AuthMessageOverrideContext, ) => EmailMessage; - bootstrapInviteEmail?: ( - input: SendBootstrapInviteEmailInput, - defaults: EmailMessage, - context: AuthMessageOverrideContext, - ) => EmailMessage; } export interface AuthMessagingService { sendOtpEmail(input: SendOtpEmailInput): Promise; sendOtpSms(input: SendOtpSmsInput): Promise; sendMagicLinkEmail(input: SendMagicLinkEmailInput): Promise; - sendBootstrapInviteEmail(input: SendBootstrapInviteEmailInput): Promise; } export type EmailProvider = EmailTransport;