diff --git a/apps/docs/src/components/project-tabs.tsx b/apps/docs/src/components/project-tabs.tsx index ebcd9a2..80fb034 100644 --- a/apps/docs/src/components/project-tabs.tsx +++ b/apps/docs/src/components/project-tabs.tsx @@ -30,7 +30,7 @@ export function ProjectTabs({ projects, currentProject, switchUrls }: ProjectTab )} > {project.icon && } - {project.name} + {project.title ?? project.name} ) })} diff --git a/apps/docs/src/content/docs/cli/_meta.json b/apps/docs/src/content/docs/cli/_meta.json index 0c598ea..fac61a5 100644 --- a/apps/docs/src/content/docs/cli/_meta.json +++ b/apps/docs/src/content/docs/cli/_meta.json @@ -1,4 +1,5 @@ { "icon": "terminal", - "title": "CLI" + "title": "Cli", + "order": 5 } diff --git a/apps/docs/src/content/docs/discover/_meta.json b/apps/docs/src/content/docs/discover/_meta.json index 8f98ca8..5ee043f 100644 --- a/apps/docs/src/content/docs/discover/_meta.json +++ b/apps/docs/src/content/docs/discover/_meta.json @@ -1,4 +1,5 @@ { "icon": "book-open", - "title": "Discover" + "title": "Discover", + "order": 1 } diff --git a/apps/docs/src/content/docs/discover/default/en/guides/architecture.mdx b/apps/docs/src/content/docs/discover/default/en/guides/architecture.mdx index 98df5c5..4c7efb7 100644 --- a/apps/docs/src/content/docs/discover/default/en/guides/architecture.mdx +++ b/apps/docs/src/content/docs/discover/default/en/guides/architecture.mdx @@ -2,7 +2,7 @@ title: Architecture description: "Hexagonal architecture, domain modules, and the ports & adapters pattern in FerrisKey." icon: boxes -order: 21 +order: 22 --- # Architecture diff --git a/apps/docs/src/content/docs/discover/default/en/guides/contributing.mdx b/apps/docs/src/content/docs/discover/default/en/guides/contributing.mdx index 805e22c..c8e29a4 100644 --- a/apps/docs/src/content/docs/discover/default/en/guides/contributing.mdx +++ b/apps/docs/src/content/docs/discover/default/en/guides/contributing.mdx @@ -2,7 +2,7 @@ title: Contributing description: "How to contribute issues, documentation, code, tests, and pull requests to FerrisKey." icon: git-pull-request -order: 22 +order: 23 --- # Contributing diff --git a/apps/docs/src/content/docs/discover/default/en/guides/email.mdx b/apps/docs/src/content/docs/discover/default/en/guides/email.mdx new file mode 100644 index 0000000..8a7bf34 --- /dev/null +++ b/apps/docs/src/content/docs/discover/default/en/guides/email.mdx @@ -0,0 +1,239 @@ +--- +title: Email & Templates +description: "Configure SMTP and customize transactional email templates in FerrisKey: password reset, magic link, and email verification." +icon: mail +order: 21 +--- + +# Email & Templates + +FerrisKey sends transactional emails for password resets, magic link authentication, and email verification. Delivery is configured per realm through a dedicated SMTP setup, and each email type can be customized with your own HTML template. + +## Configure SMTP + +SMTP is stored in the database at the realm level — not as a global environment variable. Each realm can use a different mail provider independently. + +:::callout{variant="info" title="No global SMTP"} +SMTP is configured per realm, not globally. See the [Configuration guide](/en/discover/guides/configuration) for the short overview. This page covers the full field reference and API. +::: + +### SMTP Fields + +| Field | Type | Notes | +|---|---|---| +| `host` | String | SMTP server hostname | +| `port` | u16 | Port number (1–65535) | +| `username` | String | SMTP authentication username | +| `password` | String | SMTP authentication password — write-only, never returned by the API | +| `from_email` | String | Sender address, must be a valid email | +| `from_name` | String | Sender display name | +| `encryption` | Enum | `tls` \| `starttls` \| `none` | + +**Encryption modes:** + +| Mode | Port | When to use | +|---|---|---| +| `tls` | 465 | Implicit TLS from the first byte — preferred for modern providers | +| `starttls` | 587 | Plain connection upgraded to TLS — common in corporate relays | +| `none` | 25 / any | No encryption — use only on a trusted local network or for testing | + +### SMTP API Endpoints + +All endpoints are scoped to a realm and require authentication. + +| Method | Path | Description | +|---|---|---| +| `GET` | `/realms/{realm_name}/smtp-config` | Read the current config (password omitted) | +| `PUT` | `/realms/{realm_name}/smtp-config` | Create or replace the config | +| `DELETE` | `/realms/{realm_name}/smtp-config` | Remove the config | + +**Required permissions:** reading SMTP config requires view access to the realm. Creating, updating, or deleting requires `ManageRealm`. There are no dedicated SMTP permissions beyond the realm permission gates. + +### Configure SMTP in the Console + +::::step-group +:::step{title="Open Realm Settings"} +In the left sidebar, select the realm you want to configure. Navigate to **Realm Settings**. +::: + +:::step{title="Go to the Email tab"} +Click the **Email** tab. You will see the SMTP configuration form. +::: + +:::step{title="Fill in the provider details"} +Enter the host, port, credentials, sender address and name. Select the encryption mode that matches your provider. + +Common provider settings: + +| Provider | Host | Port | Encryption | +|---|---|---|---| +| Gmail (App Password) | `smtp.gmail.com` | 465 | `tls` | +| Mailgun | `smtp.mailgun.org` | 587 | `starttls` | +| Resend | `smtp.resend.com` | 465 | `tls` | +| Local (MailHog / MailPit) | `localhost` | 1025 | `none` | +::: + +:::step{title="Save and verify"} +Click **Save**. Send a test email from the console to confirm delivery reaches the inbox before enabling email-gated features. +::: +:::: + +:::callout{variant="warning" title="Password is write-only"} +The API never returns the SMTP password. If you need to rotate credentials, submit a full `PUT` with the new password included. +::: + +--- + +## Transactional Emails + +FerrisKey sends exactly three types of transactional email. Each is tied to a realm feature toggle. + +| Email type | Identifier | Sent when | Realm toggle required | +|---|---|---|---| +| Password reset | `reset_password` | A user requests a password reset link | `forgot_password_enabled` | +| Magic link | `magic_link` | A user requests passwordless email login | `magic_link_enabled` | +| Email verification | `email_verification` | A user must verify their email address | `email_verification_enabled` | + +Enabling these toggles in Realm Settings activates the corresponding flow. FerrisKey will use the default built-in template unless you assign a custom one. + +--- + +## Customizing Templates + +### Template Engine + +FerrisKey uses a lightweight custom interpolation engine — not Handlebars or Tera. Placeholders use double-brace syntax: + +```text +{{variable_name}} +``` + +All interpolated values are HTML-escaped before insertion (`&`, `<`, `>`, `"`, `'`), which prevents injection attacks in the rendered email. + +:::callout{variant="info" title="HTML escaping"} +Values inserted via `{{...}}` are always HTML-escaped. If a user's name is `Alice