From a23856bae38aa68f0f1f9c17a7e92db3af87e366 Mon Sep 17 00:00:00 2001 From: Bohan Jiang <52446949+Bohan-J@users.noreply.github.com> Date: Fri, 15 May 2026 14:18:46 +0800 Subject: [PATCH] MUL-1624 docs(email): clarify 888888 is opt-in; document SMTP option (#2666) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(email): clarify 888888 is opt-in via MULTICA_DEV_VERIFICATION_CODE; document SMTP option in self-host docs The startup log line, .env.example, and SELF_HOSTING_ADVANCED.md still implied that the dev master code 888888 is auto-active whenever APP_ENV != "production". That has not been true since the master code was gated behind MULTICA_DEV_VERIFICATION_CODE — the fixed code is disabled by default and must be opted in explicitly. Also extend the docs site with the SMTP relay backend added in #1877: auth-setup, environment-variables, and self-host-quickstart now cover both Resend and SMTP options in EN and ZH. Co-authored-by: multica-agent * docs(email): treat SMTP as an email backend in self-host docs and startup warning Address review feedback on #2666: - server: startup warning now fires only when both RESEND_API_KEY and SMTP_HOST are empty, since either one is a valid email backend. Otherwise the log mis-tells SMTP-only operators that verification codes go to stdout. - self-host-quickstart (EN/ZH): tell readers to fetch the verification code from whichever backend they configured (Resend or SMTP); fall back to stdout only when neither is configured. - auth-setup (EN/ZH): \"without Resend\" → \"without any email backend configured\" so the wording stays correct now that SMTP is a first-class option. Co-authored-by: multica-agent --------- Co-authored-by: multica-agent --- .env.example | 5 ++-- SELF_HOSTING_ADVANCED.md | 4 +-- apps/docs/content/docs/auth-setup.mdx | 25 ++++++++++++++++--- apps/docs/content/docs/auth-setup.zh.mdx | 25 ++++++++++++++++--- .../content/docs/environment-variables.mdx | 20 ++++++++++++--- .../content/docs/environment-variables.zh.mdx | 20 ++++++++++++--- .../content/docs/self-host-quickstart.mdx | 24 ++++++++++++++---- .../content/docs/self-host-quickstart.zh.mdx | 24 ++++++++++++++---- server/cmd/server/main.go | 4 +-- server/internal/service/email.go | 2 +- 10 files changed, 122 insertions(+), 31 deletions(-) diff --git a/.env.example b/.env.example index e8357a83d3..94858653aa 100644 --- a/.env.example +++ b/.env.example @@ -53,8 +53,9 @@ MULTICA_WEB_IMAGE=ghcr.io/multica-ai/multica-web # # Option A: Resend (SaaS, recommended for cloud deployments) # Set RESEND_API_KEY to a key from resend.com and verify your sending domain there. -# For local/dev use, leave RESEND_API_KEY empty - codes print to stdout and -# master code 888888 works (only when APP_ENV != "production"). +# For local/dev use, leave RESEND_API_KEY empty - codes print to stdout. To +# accept a fixed local code, also set MULTICA_DEV_VERIFICATION_CODE above +# (ignored when APP_ENV=production). RESEND_API_KEY= RESEND_FROM_EMAIL=noreply@multica.ai # diff --git a/SELF_HOSTING_ADVANCED.md b/SELF_HOSTING_ADVANCED.md index b636d67798..87eb51a3b1 100644 --- a/SELF_HOSTING_ADVANCED.md +++ b/SELF_HOSTING_ADVANCED.md @@ -25,7 +25,7 @@ These have sensible defaults and only need to be set when tuning a large or cons ### Email (Required for Authentication) -Multica supports two email backends. `SMTP_HOST` takes priority when set; otherwise `RESEND_API_KEY` is used. In local development with neither configured, verification codes are printed to the server log and the master code `888888` is active (only when `APP_ENV=development`). +Multica supports two email backends. `SMTP_HOST` takes priority when set; otherwise `RESEND_API_KEY` is used. With neither configured, verification codes are printed to the server log — copy them from there to log in. #### Option A: Resend (recommended for cloud deployments) @@ -48,7 +48,7 @@ Use this option when your deployment cannot reach the public internet or you alr STARTTLS is used automatically when advertised by the server. Port 465 (SMTPS / implicit TLS) is not currently supported - use ports 25 or 587 with STARTTLS. -> **Note:** If Resend is not configured, generated verification codes are printed to backend logs. The dev master verification code `888888` is gated by `APP_ENV != "production"`. The Docker self-host stack defaults to `APP_ENV=production` (so `888888` is disabled). For local development without email configured, set `APP_ENV=development` in your `.env` to enable `888888` - never do this on a public instance. +> **Note:** If neither Resend nor SMTP is configured, generated verification codes are printed to backend logs — copy them from there to log in. A fixed local testing code (e.g. `888888`) is **opt-in only**: set `MULTICA_DEV_VERIFICATION_CODE=888888` in `.env` and keep `APP_ENV` non-production. The Docker self-host stack pins `APP_ENV=production`, so the shortcut is ignored there. **Never enable a fixed code on a publicly reachable instance.** ### Google OAuth (Optional) diff --git a/apps/docs/content/docs/auth-setup.mdx b/apps/docs/content/docs/auth-setup.mdx index 7d8fb29085..6624d1c8f1 100644 --- a/apps/docs/content/docs/auth-setup.mdx +++ b/apps/docs/content/docs/auth-setup.mdx @@ -12,9 +12,11 @@ For the list of environment variables referenced below, see [Environment variabl ## How email + verification code sign-in works -The user enters an email on the sign-in page → the server sends a 6-digit code → the user enters it → the server verifies it → a JWT cookie is issued. Standard flow. It requires [Resend](https://resend.com/) as the email provider: +The user enters an email on the sign-in page → the server sends a 6-digit code → the user enters it → the server verifies it → a JWT cookie is issued. Standard flow. Two delivery backends are supported — pick whichever fits your deployment: -1. Create a Resend account and verify your domain +### Option A: Resend (recommended for cloud / public-internet deployments) + +1. Create a [Resend](https://resend.com/) account and verify your domain 2. Create an API key 3. Set the environment variables: @@ -25,7 +27,22 @@ The user enters an email on the sign-in page → the server sends a 6-digit code 4. Restart the server -**What happens if you don't set `RESEND_API_KEY`**: the server doesn't error, but **every email that should have been sent is written to the server's stdout only**. Handy for local development (copy the code from the logs); in production it's a black hole. +### Option B: SMTP relay (for self-hosted / on-premise deployments) + +Use this when the deployment can't reach `api.resend.com` or you already have an internal mail relay (Exchange, Postfix, on-prem SendGrid, etc.). `SMTP_HOST` takes priority over `RESEND_API_KEY` when both are set. + +```bash +SMTP_HOST=smtp.internal.example.com +SMTP_PORT=587 # default 25; use 587 for STARTTLS submission +SMTP_USERNAME=multica # leave empty for unauthenticated relay +SMTP_PASSWORD=... +SMTP_TLS_INSECURE=false # set true only for self-signed / private CA +RESEND_FROM_EMAIL=noreply@yourdomain.com # reused as the From: header +``` + +STARTTLS is upgraded automatically when the server advertises it. Port 465 (SMTPS / implicit TLS) is **not** currently supported — use port 25 or 587. + +**What happens if you set neither**: the server doesn't error, but **every email that should have been sent is written to the server's stdout only**. Handy for local development (copy the code from the logs); in production it's a black hole. ## Fixed local testing codes @@ -34,7 +51,7 @@ The user enters an email on the sign-in page → the server sends a 6-digit code The old behavior where non-production instances accepted `888888` by default has been removed. Unless you explicitly configure it, typing `888888` is treated like any other wrong code. -Local development without Resend should use the generated code printed in server logs. If you need deterministic local/private automation, set `MULTICA_DEV_VERIFICATION_CODE` to a 6-digit value such as `888888`, and keep `APP_ENV` non-production: +Local development without any email backend configured (no Resend, no SMTP) should use the generated code printed in server logs. If you need deterministic local/private automation, set `MULTICA_DEV_VERIFICATION_CODE` to a 6-digit value such as `888888`, and keep `APP_ENV` non-production: ```bash APP_ENV=development diff --git a/apps/docs/content/docs/auth-setup.zh.mdx b/apps/docs/content/docs/auth-setup.zh.mdx index 7ce8115361..2c5d1c0a6c 100644 --- a/apps/docs/content/docs/auth-setup.zh.mdx +++ b/apps/docs/content/docs/auth-setup.zh.mdx @@ -12,9 +12,11 @@ Multica 支持两种登录方式:**Email + 验证码**(默认)和 **Google ## Email + 验证码登录怎么工作 -用户在登录页输邮箱 → server 发 6 位验证码 → 用户填回 → server 验证 → 签发 JWT cookie。是标准流程。需要 [Resend](https://resend.com/) 作为邮件发送服务: +用户在登录页输邮箱 → server 发 6 位验证码 → 用户填回 → server 验证 → 签发 JWT cookie。是标准流程。支持两种邮件发送通道,按部署环境二选一: -1. 在 Resend 建账号、验证你的域名 +### Option A:Resend(公网/云端部署推荐) + +1. 在 [Resend](https://resend.com/) 建账号、验证你的域名 2. 创建 API key 3. 设环境变量: @@ -25,7 +27,22 @@ Multica 支持两种登录方式:**Email + 验证码**(默认)和 **Google 4. 重启 server -**不配 `RESEND_API_KEY` 的后果**:server 不报错,但**所有本该发出去的邮件只打到 server 的 stdout**。本地开发方便(你从日志抄验证码),生产环境等于黑洞。 +### Option B:SMTP relay(内网/自部署) + +适合内网无法访问 `api.resend.com`,或者已经有内部邮件中继(Exchange、Postfix、自部署 SendGrid 等)的场景。同时设置时 `SMTP_HOST` 优先级高于 `RESEND_API_KEY`。 + +```bash +SMTP_HOST=smtp.internal.example.com +SMTP_PORT=587 # 默认 25;STARTTLS 提交端口用 587 +SMTP_USERNAME=multica # 留空则使用未认证 relay +SMTP_PASSWORD=... +SMTP_TLS_INSECURE=false # 仅在私有 CA / 自签证书时改成 true +RESEND_FROM_EMAIL=noreply@yourdomain.com # 同时作为 SMTP From: 头 +``` + +服务端 advertise STARTTLS 时会自动升级。**暂不支持** 465(SMTPS / 隐式 TLS),请使用 25 或 587。 + +**两种都不配**:server 不报错,但所有本该发出去的邮件**只打到 server 的 stdout**。本地开发方便(你从日志抄验证码),生产环境等于黑洞。 ## 固定本地测试验证码 @@ -34,7 +51,7 @@ Multica 支持两种登录方式:**Email + 验证码**(默认)和 **Google 旧版「非 production 默认接受 `888888`」的行为已经移除。除非你显式配置,否则输入 `888888` 会和普通错误验证码一样被拒绝。 -不配 Resend 的本地开发,应使用 server 日志里打印的随机验证码。如果你需要确定性的本地/私有自动化测试,可以把 `MULTICA_DEV_VERIFICATION_CODE` 设成一个 6 位数字,比如 `888888`,并保持 `APP_ENV` 为非 production: +没配任何邮件后端(Resend 和 SMTP 都没设)的本地开发,应使用 server 日志里打印的随机验证码。如果你需要确定性的本地/私有自动化测试,可以把 `MULTICA_DEV_VERIFICATION_CODE` 设成一个 6 位数字,比如 `888888`,并保持 `APP_ENV` 为非 production: ```bash APP_ENV=development diff --git a/apps/docs/content/docs/environment-variables.mdx b/apps/docs/content/docs/environment-variables.mdx index fa9087c386..83eb026053 100644 --- a/apps/docs/content/docs/environment-variables.mdx +++ b/apps/docs/content/docs/environment-variables.mdx @@ -35,14 +35,28 @@ These are the core variables you must think about before deploying — some have ## Email configuration -Multica uses [Resend](https://resend.com/) to send verification codes and invite emails. +Multica supports two delivery backends — [Resend](https://resend.com/) for cloud deployments, or an SMTP relay for internal / on-premise networks. `SMTP_HOST` takes priority over `RESEND_API_KEY` when both are set. + +### Resend | Variable | Default | Description | |---|---|---| | `RESEND_API_KEY` | empty | Resend API key | -| `RESEND_FROM_EMAIL` | `noreply@multica.ai` | Sender address (must be a domain verified in your Resend account) | +| `RESEND_FROM_EMAIL` | `noreply@multica.ai` | Sender address (must be a domain verified in your Resend account; also reused as the `From:` header when SMTP is in use) | -**Behavior when `RESEND_API_KEY` is unset**: the server does not error, but every email that should have been sent (verification codes, invite links) **is written to the server's stdout only**. Convenient for local development — copy the code out of the server logs; **in production, forgetting to set this creates a silent black hole**, with users never receiving email and no error surfaced. +### SMTP relay + +| Variable | Default | Description | +|---|---|---| +| `SMTP_HOST` | empty | SMTP relay hostname. Setting this activates SMTP mode and overrides Resend | +| `SMTP_PORT` | `25` | SMTP port. Use `587` for STARTTLS submission; **port 465 (SMTPS / implicit TLS) is not supported** | +| `SMTP_USERNAME` | empty | SMTP username. Leave empty for unauthenticated relay | +| `SMTP_PASSWORD` | empty | SMTP password | +| `SMTP_TLS_INSECURE` | `false` | Set `true` to skip TLS certificate verification (private CA / self-signed only) | + +STARTTLS is upgraded automatically when the server advertises it. The dial timeout is 10s and the whole SMTP session has a 30s deadline, so a black-holed relay can't hang the auth handler. + +**Behavior when neither is set**: the server does not error, but every email that should have been sent (verification codes, invite links) **is written to the server's stdout only**. Convenient for local development — copy the code out of the server logs; **in production, forgetting to set this creates a silent black hole**, with users never receiving email and no error surfaced. ## Google OAuth configuration diff --git a/apps/docs/content/docs/environment-variables.zh.mdx b/apps/docs/content/docs/environment-variables.zh.mdx index 3c43e36b8b..57dc22f4d1 100644 --- a/apps/docs/content/docs/environment-variables.zh.mdx +++ b/apps/docs/content/docs/environment-variables.zh.mdx @@ -35,14 +35,28 @@ Multica 的 [自部署](/self-host-quickstart) 服务器启动时从环境变量 ## 怎么配邮件 -Multica 用 [Resend](https://resend.com/) 发验证码和邀请邮件。 +Multica 支持两种邮件发送通道——[Resend](https://resend.com/) 适合公网部署,SMTP relay 适合内网/自部署。同时设置时 `SMTP_HOST` 优先级高于 `RESEND_API_KEY`。 + +### Resend | 环境变量 | 默认值 | 说明 | |---|---|---| | `RESEND_API_KEY` | 空 | Resend API key | -| `RESEND_FROM_EMAIL` | `noreply@multica.ai` | 发件地址(必须是 Resend 账号已验证的域名)| +| `RESEND_FROM_EMAIL` | `noreply@multica.ai` | 发件地址(必须是 Resend 账号已验证的域名;走 SMTP 时同时作为 `From:` 头)| -**不设 `RESEND_API_KEY` 时的行为**:server 不会报错,但所有本该发出去的邮件(验证码、邀请链接)**只打到 server 的 stdout**。本地开发时方便——你从 server 日志里抄验证码;**生产环境忘记设就是黑洞**,用户收不到邮件也没任何错误提示。 +### SMTP relay + +| 环境变量 | 默认值 | 说明 | +|---|---|---| +| `SMTP_HOST` | 空 | SMTP relay 主机名。设置后即启用 SMTP 模式并覆盖 Resend | +| `SMTP_PORT` | `25` | SMTP 端口。STARTTLS 提交端口用 `587`;**暂不支持 465(SMTPS / 隐式 TLS)** | +| `SMTP_USERNAME` | 空 | SMTP 用户名。留空表示未认证 relay | +| `SMTP_PASSWORD` | 空 | SMTP 密码 | +| `SMTP_TLS_INSECURE` | `false` | 设为 `true` 跳过 TLS 证书校验(仅限私有 CA / 自签证书)| + +服务端 advertise STARTTLS 时会自动升级。dial 超时 10s,整个 SMTP 会话有 30s deadline,避免 relay 黑洞把 auth handler 挂死。 + +**两种都不设的行为**:server 不会报错,但所有本该发出去的邮件(验证码、邀请链接)**只打到 server 的 stdout**。本地开发方便(你从 server 日志里抄验证码);**生产环境忘记设就是黑洞**,用户收不到邮件也没任何错误提示。 ## 怎么配 Google OAuth diff --git a/apps/docs/content/docs/self-host-quickstart.mdx b/apps/docs/content/docs/self-host-quickstart.mdx index 6206ea10f7..3adec64640 100644 --- a/apps/docs/content/docs/self-host-quickstart.mdx +++ b/apps/docs/content/docs/self-host-quickstart.mdx @@ -59,7 +59,9 @@ Before any public deployment, make sure `.env` has `APP_ENV=production` and `MUL Without email configured, your users can't receive verification codes by email; the server prints generated codes to stdout instead. -To actually send verification emails: +Two delivery backends are supported — pick whichever fits your network: + +**Option A — Resend (cloud / public-internet deployments):** 1. Sign up at [Resend](https://resend.com/) and get an API key 2. Verify a sending domain you control @@ -70,16 +72,28 @@ To actually send verification emails: RESEND_FROM_EMAIL=noreply@yourdomain.com ``` -4. Restart: `docker compose -f docker-compose.selfhost.yml restart backend` +**Option B — SMTP relay (internal networks / on-premise):** -For more auth configuration (OAuth, signup allowlist), see [Auth setup](/auth-setup). +Use this when the deployment can't reach `api.resend.com`, or you already have an internal mail relay (Exchange, Postfix, on-prem SendGrid, etc.). `SMTP_HOST` takes priority over Resend when both are set. + +```bash +SMTP_HOST=smtp.internal.example.com +SMTP_PORT=587 # default 25; use 587 for STARTTLS submission +SMTP_USERNAME=multica # leave empty for unauthenticated relay +SMTP_PASSWORD=... +RESEND_FROM_EMAIL=noreply@yourdomain.com # reused as the From: header +``` + +Then restart: `docker compose -f docker-compose.selfhost.yml restart backend`. + +For more auth configuration (OAuth, signup allowlist) and the full SMTP variable reference, see [Auth setup](/auth-setup) and [Environment variables → Email](/environment-variables#email-configuration). ## 4. First login + create a workspace Open [http://localhost:3000](http://localhost:3000): - Enter your email -- Grab the verification code from the Resend email (or, if you haven't configured Resend, from the server container stdout — look for the `[DEV] Verification code` line) +- Grab the verification code from your configured email backend (Resend or SMTP relay); if neither is configured, copy it from the server container stdout — look for the `[DEV] Verification code` line - Do not use `888888` unless you explicitly set `MULTICA_DEV_VERIFICATION_CODE=888888` on a non-production private instance - Log in and create your first workspace @@ -108,7 +122,7 @@ Same flow as Cloud — see [Cloud quickstart → Steps 5-6](/cloud-quickstart#5- ## Common issues - **Backend won't start**: check container logs with `docker compose -f docker-compose.selfhost.yml logs backend`; usually it's a bad `DATABASE_URL` or `JWT_SECRET` in `.env` -- **Verification code not received**: Resend isn't configured → look for `[DEV] Verification code` in `docker compose logs backend` +- **Verification code not received**: no email backend is configured (neither Resend nor SMTP) → look for `[DEV] Verification code` in `docker compose logs backend` - **WebSocket won't connect**: for public deployments you must set `FRONTEND_ORIGIN` to your real frontend domain; see [Troubleshooting → WebSocket won't connect](/troubleshooting#websocket-wont-connect) ## Next steps diff --git a/apps/docs/content/docs/self-host-quickstart.zh.mdx b/apps/docs/content/docs/self-host-quickstart.zh.mdx index 3e7231acdc..65d5865b72 100644 --- a/apps/docs/content/docs/self-host-quickstart.zh.mdx +++ b/apps/docs/content/docs/self-host-quickstart.zh.mdx @@ -58,7 +58,9 @@ make selfhost 如果不配邮件,用户无法通过邮件收到验证码;server 会把生成的验证码打印到 stdout。 -要真的发验证码邮件: +支持两种发送通道,按部署环境二选一: + +**Option A — Resend(公网/云端部署):** 1. 在 [Resend](https://resend.com/) 注册并拿一个 API key 2. 验证一个你控制的发件域名 @@ -69,16 +71,28 @@ make selfhost RESEND_FROM_EMAIL=noreply@yourdomain.com ``` -4. 重启:`docker compose -f docker-compose.selfhost.yml restart backend` +**Option B — SMTP relay(内网/自部署):** -更多 auth 配置(OAuth、注册白名单)见 [登录与注册配置](/auth-setup)。 +适合内网无法访问 `api.resend.com`,或已经有内部邮件中继(Exchange、Postfix、自部署 SendGrid 等)的场景。同时设置时 `SMTP_HOST` 优先级高于 Resend。 + +```bash +SMTP_HOST=smtp.internal.example.com +SMTP_PORT=587 # 默认 25;STARTTLS 提交端口用 587 +SMTP_USERNAME=multica # 留空则使用未认证 relay +SMTP_PASSWORD=... +RESEND_FROM_EMAIL=noreply@yourdomain.com # 同时作为 SMTP From: 头 +``` + +之后重启:`docker compose -f docker-compose.selfhost.yml restart backend`。 + +更多 auth 配置(OAuth、注册白名单)以及完整的 SMTP 变量说明见 [登录与注册配置](/auth-setup) 和 [环境变量](/environment-variables)。 ## 4. 首次登录 + 创建工作区 打开 [http://localhost:3000](http://localhost:3000): - 输入你的邮箱 -- 从 Resend 邮件里拿验证码(或者前面没配 Resend 的话从 server 容器的 stdout 里抄 `[DEV] Verification code` 这行) +- 从你配置的邮件后端(Resend 或 SMTP relay)收到的邮件里拿验证码;两者都没配的话,从 server 容器的 stdout 里抄 `[DEV] Verification code` 这行 - 不要直接使用 `888888`;只有在非 production 私有实例上显式设置 `MULTICA_DEV_VERIFICATION_CODE=888888` 后它才会生效 - 登录后创建第一个工作区 @@ -107,7 +121,7 @@ multica setup self-host ## 常见问题 - **后端起不来**:看容器日志 `docker compose -f docker-compose.selfhost.yml logs backend`;常见是 `.env` 里 `DATABASE_URL` 或 `JWT_SECRET` 有问题 -- **验证码收不到**:没配 Resend → 从 `docker compose logs backend` 里找 `[DEV] Verification code` +- **验证码收不到**:没配任何邮件后端(Resend 和 SMTP 都没设) → 从 `docker compose logs backend` 里找 `[DEV] Verification code` - **WebSocket 连不上**:公网部署必须设 `FRONTEND_ORIGIN` 成你真实的前端域名;见 [故障排查 → WebSocket 连不上](/troubleshooting#websocket-连不上) ## 下一步 diff --git a/server/cmd/server/main.go b/server/cmd/server/main.go index 29d5733bab..910915132b 100644 --- a/server/cmd/server/main.go +++ b/server/cmd/server/main.go @@ -123,8 +123,8 @@ func main() { if os.Getenv("JWT_SECRET") == "" { slog.Warn("JWT_SECRET is not set — using insecure default. Set JWT_SECRET for production use.") } - if os.Getenv("RESEND_API_KEY") == "" { - slog.Warn("RESEND_API_KEY is not set — email verification codes will be printed to the log instead of emailed.") + if os.Getenv("RESEND_API_KEY") == "" && strings.TrimSpace(os.Getenv("SMTP_HOST")) == "" { + slog.Warn("no email backend configured (RESEND_API_KEY and SMTP_HOST both empty) — verification codes will be printed to the log instead of emailed.") } if os.Getenv("MULTICA_DEV_VERIFICATION_CODE") != "" { if strings.EqualFold(strings.TrimSpace(os.Getenv("APP_ENV")), "production") { diff --git a/server/internal/service/email.go b/server/internal/service/email.go index 6b927fdff7..1ba6a89ce6 100644 --- a/server/internal/service/email.go +++ b/server/internal/service/email.go @@ -59,7 +59,7 @@ func NewEmailService() *EmailService { case client != nil: fmt.Printf("EmailService: Resend API from=%s\n", from) default: - fmt.Println("EmailService: DEV mode — codes printed to stdout, master code 888888 active") + fmt.Println("EmailService: DEV mode — codes printed to stdout (set MULTICA_DEV_VERIFICATION_CODE in .env for a fixed local code)") } return &EmailService{