Skip to content
Merged
19 changes: 19 additions & 0 deletions .changeset/22748-stack-email-sms-door.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"@objectstack/spec": minor
---

feat(spec): the stack definition admits `appName`, the non-secret members of `email`, and `sms.retries`; credentials and the SMS provider are refused by name

Clause-②: yes (widening: the stack definition admits `appName`, the non-secret members of `email`, and `sms` carrying `retries` alone, which it refused as unrecognized keys)

- **What you can write now.** `defineStack()` accepts three top-level keys it used to refuse as unrecognized. The mail and SMS readers already read them, and `objectstack serve` and `bootStack` construct the email and SMS providers through those readers:
- `appName`: the deployment's product name. It is the last configured rung of the name chain (`OS_APP_NAME` > `email.appName` > `email.defaultTemplateContext.appName` > `appName` > `'ObjectStack'`), and it reaches the email templates and AuthPlugin.
- `email` (`StackEmailConfigSchema`): every member of `EmailServiceConfigSchema` except `apiKey`. That is `provider`, `defaultFrom`, `retries`, `persist`, `queueDelivery`, `appName` and `defaultTemplateContext`, plus `options` narrowed to `host` / `port` / `secure` / `user` / `messageStream`. `OS_EMAIL_*` still overrides it per key.
- `sms` (`StackSmsConfigSchema`): `retries`, the SMS service's attempt budget. It applies to whichever transport the `sms` settings namespace selects.
- **What stays elsewhere, and is refused by name.** Each refusal names the key and the setting that replaces it:
- `email.apiKey` and `email.options.apiKey`: a credential, and a stack definition is compiled into the published artifact. Set `OS_EMAIL_API_KEY`.
- `email.options.password`: a credential. Set `OS_EMAIL_SMTP_PASSWORD`; the reader layers it over the host / port / secure / user the file carries.
- `sms.provider`, `sms.providerOptions` (and a flat `authToken` / `accessKeySecret`): the delivering SMS transport is built by the `sms` settings namespace from its own provider and credentials, never from a stack definition. Configure them in Setup → Settings → SMS Delivery, or through that namespace's overrides (`OS_SMS_PROVIDER`, `OS_SMS_TWILIO_*`, `OS_SMS_ALIYUN_*`).
- **Composition.** All three keys compose as single values. `composeStacks` passes an identical value through and refuses two different ones by key.
- **Nothing to migrate.** No stack that was accepted before is refused now. A configuration that set these keys only in an object `defineStack` did not build (the one `objectstack serve` boots) reads the same as before. To build it, move any credential, and the SMS provider, into the environment or Settings.
- New exports from `@objectstack/spec/system`: `StackEmailConfigSchema`, `StackEmailProviderOptionsSchema`, `StackSmsConfigSchema`, and their `z.input` / `z.infer` types.
2 changes: 1 addition & 1 deletion content/docs/getting-started/quick-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Plugin architecture, manifests, and kernel runtime.
| **[Metadata Loader](/docs/references/kernel/metadata-loader)** | `metadata-loader.zod.ts` | MetadataLoaderContract | Metadata loading |
| **[Package Registry](/docs/references/kernel/package-registry)** | `package-registry.zod.ts` | InstalledPackage, InstallPackageRequest | Package resolution |

## System Protocol (16 of 34 schemas)
## System Protocol (16 of 35 schemas)

Runtime environment, logging, jobs, caching, and observability.

Expand Down
6 changes: 4 additions & 2 deletions content/docs/getting-started/quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,10 @@ schema page is the authority until one lands.
**Not in the table, and why.** `viewItems` and `runtimeModule` are not authorable
at all (`viewItems` is `z.never()`, the machine-assembled channel for
runtime-assembled manifests; `runtimeModule` is written by `objectstack build`);
`manifest`, `i18n`, `api`, `server`, `requires`, `tiers`, `plugins` and
`devPlugins` configure the stack rather than declare metadata items; and
`manifest`, `i18n`, `api`, `server`, `appName`, `email`, `sms`, `requires`,
`tiers`, `plugins`, `devPlugins`, `devHint` and `devLogins` configure the stack
rather than declare metadata items (`email` and `sms` carry no credentials —
those stay in the environment or in Settings); and
`functions` and `onEnable` are code the runtime calls, not metadata it stores.

## How you verify
Expand Down
11 changes: 6 additions & 5 deletions content/docs/references/index.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Protocol reference — every schema by module
navTitle: Protocol Reference
description: Every schema published by @objectstack/spec — 1517 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1520 schemas across 14 protocol modules
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
Expand Down Expand Up @@ -32,9 +32,9 @@ counts are sums of the rows they head. Regenerate with
| [Security Protocol](/docs/references/security) | 5 | 30 | Permission sets, row-level security, sharing rules, tenancy posture. |
| [Shared Protocol](/docs/references/shared) | 10 | 31 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
| [System Protocol](/docs/references/system) | 34 | 275 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
| [System Protocol](/docs/references/system) | 35 | 278 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
| [UI Protocol](/docs/references/ui) | 16 | 166 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
| **Total** | **195** | **1517** | 14 protocol modules |
| **Total** | **196** | **1520** | 14 protocol modules |

---

Expand Down Expand Up @@ -317,7 +317,7 @@ Studio designer metadata — the authoring surfaces for the protocols above.

## System Protocol

**Source:** `packages/spec/src/system/` · **Import:** `@objectstack/spec/system` · **34 pages, 275 schemas**
**Source:** `packages/spec/src/system/` · **Import:** `@objectstack/spec/system` · **35 pages, 278 schemas**

The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance.

Expand All @@ -333,7 +333,7 @@ The runtime environment — logging, jobs, cache, metrics, notifications, i18n a
| [`dev-login.zod.ts`](/docs/references/system/dev-login) | `DevLogin` |
| [`disaster-recovery.zod.ts`](/docs/references/system/disaster-recovery) | `BackupConfig`, `BackupRetention`, `BackupStrategy`, `DisasterRecoveryPlan`, `FailoverConfig`, `FailoverMode`, `RPO`, `RTO` |
| [`doc.zod.ts`](/docs/references/system/doc) | `Doc` |
| [`email-config.zod.ts`](/docs/references/system/email-config) | `EmailAddressConfig`, `EmailProvider`, `EmailServiceConfig` |
| [`email-config.zod.ts`](/docs/references/system/email-config) | `EmailAddressConfig`, `EmailProvider`, `EmailServiceConfig`, `StackEmailConfig`, `StackEmailProviderOptions` |
| [`email-template.zod.ts`](/docs/references/system/email-template) | `EmailTemplateDefinition`, `EmailTemplateDefinitionCategory`, `EmailTemplateDefinitionVariable` |
| [`encryption.zod.ts`](/docs/references/system/encryption) | `EncryptionAlgorithm`, `EncryptionConfig`, `FieldEncryption`, `KeyManagementProvider`, `KeyRotationPolicy` |
| [`environment-artifact.zod.ts`](/docs/references/system/environment-artifact) | `Sha256Digest` |
Expand All @@ -351,6 +351,7 @@ The runtime environment — logging, jobs, cache, metrics, notifications, i18n a
| [`security-context.zod.ts`](/docs/references/system/security-context) | `ComplianceAuditRequirement`, `ComplianceEncryptionRequirement`, `ComplianceFramework`, `DataClassification`, `DataClassificationPolicy`, `MaskingVisibilityRule`, `SecurityContextConfig`, `SecurityEventCorrelation` |
| [`settings-client.zod.ts`](/docs/references/system/settings-client) | `SettingsChangeEvent` |
| [`settings-manifest.zod.ts`](/docs/references/system/settings-manifest) | `ResolvedSettingValue`, `SettingsActionResult`, `SettingsManifest`, `SettingsNamespacePayload`, `Specifier`, `SpecifierHandler`, `SpecifierOption`, `SpecifierScope`, `SpecifierType`, `SpecifierValueDomain` |
| [`sms-config.zod.ts`](/docs/references/system/sms-config) | `StackSmsConfig` |
| [`stack-server.zod.ts`](/docs/references/system/stack-server) | `ServerRateLimitConfig`, `StackServerConfig`, `StackServerSecurity` |
| [`supplier-security.zod.ts`](/docs/references/system/supplier-security) | `SupplierAssessmentStatus`, `SupplierRiskLevel`, `SupplierSecurityAssessment`, `SupplierSecurityPolicy`, `SupplierSecurityRequirement` |
| [`tenant.zod.ts`](/docs/references/system/tenant) | `DatabaseLevelIsolationStrategy`, `DatabaseProvider`, `QuotaEnforcementResult`, `RowLevelIsolationStrategy`, `SchemaLevelIsolationStrategy`, `Tenant`, `TenantConnectionConfig`, `TenantIsolationConfig`, `TenantIsolationLevel`, `TenantQuota`, `TenantSecurityPolicy`, `TenantUsage` |
Expand Down
72 changes: 66 additions & 6 deletions content/docs/references/system/email-config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@ transport for the EmailServicePlugin. Provider is a `provider` tag
+ provider-specific settings; concrete `IEmailTransport`
implementations live in `@objectstack/plugin-email/transports/*`.

Resolution order in `serve.ts`:
1. `config.email.*` from objectstack.config.ts
2. `OS_EMAIL_*` environment variables (override per setting)
Resolution order in `resolveEmailCapabilityArg` (`@objectstack/plugin-email`,
the one reader; `objectstack serve` and `bootStack` both construct the email
provider through it):
1. `config.email.*` from objectstack.config.ts — the NON-SECRET members
only. A config file is compiled into the published artifact, so the
stack definition carries `StackEmailConfigSchema`: every member
below except `apiKey`, with `options` narrowed to host / port / secure /
user / messageStream. `apiKey` and the SMTP `options.password` are
refused there by name; they are environment-only.
2. `OS_EMAIL_*` environment variables (override per setting) — the only
carrier of `OS_EMAIL_API_KEY` and `OS_EMAIL_SMTP_PASSWORD`
3. Default → provider='log' (LogTransport, no real send)

`appName` is the one key whose env layer is not `OS_EMAIL_*` — it is
Expand All @@ -32,7 +40,9 @@ defaultTemplateContext), both times with the spec on the lagging side.
SMTP delivery is built in (ADR-0012): select it with provider='smtp'
and supply the connection through `options` (host / port / secure /
user / password) or the matching OS_EMAIL_SMTP_HOST / _PORT /
_SECURE / _USER / _PASSWORD environment variables.
_SECURE / _USER / _PASSWORD environment variables. From a config file the
password always arrives through OS_EMAIL_SMTP_PASSWORD, which the reader
layers over the host / port / secure / user the file carries.

<Callout type="info">
**Source:** `packages/spec/src/system/email-config.zod.ts`
Expand All @@ -41,8 +51,8 @@ _SECURE / _USER / _PASSWORD environment variables.
## TypeScript Usage

```typescript
import { EmailAddressConfigSchema, EmailProviderSchema, EmailServiceConfigSchema } from '@objectstack/spec/system';
import type { EmailAddressConfig, EmailProvider, EmailServiceConfig } from '@objectstack/spec/system';
import { EmailAddressConfigSchema, EmailProviderSchema, EmailServiceConfigSchema, StackEmailConfigSchema, StackEmailProviderOptionsSchema } from '@objectstack/spec/system';
import type { EmailAddressConfig, EmailProvider, EmailServiceConfig, StackEmailConfig, StackEmailProviderOptions } from '@objectstack/spec/system';

// Validate data
const result = EmailAddressConfigSchema.parse(data);
Expand Down Expand Up @@ -100,3 +110,53 @@ const result = EmailAddressConfigSchema.parse(data);

---

## StackEmailConfig

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **provider** | `Enum<'log' \| 'resend' \| 'postmark' \| 'smtp'>` | optional (default: `"log"`) | Transport to deliver through (OS_EMAIL_PROVIDER env). Default log — boots, sends nothing |
| **defaultFrom** | `{ name?: string; address: string }` | optional | |
| **retries** | `integer` | optional | Retry attempts on transport throw |
| **persist** | `boolean` | optional | Persist to sys_email (default true) |
| **queueDelivery** | `boolean` | optional | Deliver through the durable sys_job_queue instead of inline (or OS_EMAIL_QUEUE_ENABLED env). Default false. Reuses `retries` as the queue attempt budget; requires a queue service and sys_email persistence, else the boot fails |
| **appName** | `string` | optional | Product name templates interpolate as the appName variable — OS_APP_NAME env wins, then this, then defaultTemplateContext.appName, then the top-level config appName, then "ObjectStack". Also seeds the placeholder no-reply sender when no defaultFrom is configured |
| **defaultTemplateContext** | `Record<string, any>` | optional | Free-form render context merged into every sendTemplate() call, under the per-call data. Passed through unchanged except appName, which is resolved by its own chain — OS_APP_NAME and the appName key both override the value written here |
| **options** | `{ host?: string; port?: integer; secure?: boolean; user?: string; … }` | optional | Provider-specific extras, non-secret members only. smtp: host / port / secure / user, mirroring OS_EMAIL_SMTP_HOST / _PORT / _SECURE / _USER (the password is OS_EMAIL_SMTP_PASSWORD only). postmark: messageStream |

### Nested Shape: `StackEmailConfig.defaultFrom`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **name** | `string` | optional | Display name (e.g. "Acme CRM") |
| **address** | `string` | ✅ | RFC-5322 address |

### Nested Shape: `StackEmailConfig.options`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **host** | `string` | optional | SMTP server hostname (OS_EMAIL_SMTP_HOST env wins). Required for provider smtp unless the env supplies it |
| **port** | `integer` | optional | SMTP port (OS_EMAIL_SMTP_PORT env wins). Default 587; 465 selects implicit TLS |
| **secure** | `boolean` | optional | Require TLS (OS_EMAIL_SMTP_SECURE env wins). Default true |
| **user** | `string` | optional | SMTP AUTH username (OS_EMAIL_SMTP_USER env wins). Its password is environment-only: OS_EMAIL_SMTP_PASSWORD |
| **messageStream** | `string` | optional | Postmark message stream. Default outbound |


---

## StackEmailProviderOptions

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **host** | `string` | optional | SMTP server hostname (OS_EMAIL_SMTP_HOST env wins). Required for provider smtp unless the env supplies it |
| **port** | `integer` | optional | SMTP port (OS_EMAIL_SMTP_PORT env wins). Default 587; 465 selects implicit TLS |
| **secure** | `boolean` | optional | Require TLS (OS_EMAIL_SMTP_SECURE env wins). Default true |
| **user** | `string` | optional | SMTP AUTH username (OS_EMAIL_SMTP_USER env wins). Its password is environment-only: OS_EMAIL_SMTP_PASSWORD |
| **messageStream** | `string` | optional | Postmark message stream. Default outbound |


---

3 changes: 2 additions & 1 deletion content/docs/references/system/index.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: System Protocol — complete schema reference
navTitle: System Protocol
description: "The ObjectStack System Protocol in 34 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example."
description: "The ObjectStack System Protocol in 35 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example."
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
Expand Down Expand Up @@ -37,6 +37,7 @@ This section contains all protocol schemas for the system layer of ObjectStack.
<Card href="/docs/references/system/security-context" title="Security Context" description="Source: packages/spec/src/system/security-context.zod.ts" />
<Card href="/docs/references/system/settings-client" title="Settings Client" description="Source: packages/spec/src/system/settings-client.zod.ts" />
<Card href="/docs/references/system/settings-manifest" title="Settings Manifest" description="Source: packages/spec/src/system/settings-manifest.zod.ts" />
<Card href="/docs/references/system/sms-config" title="Sms Config" description="Source: packages/spec/src/system/sms-config.zod.ts" />
<Card href="/docs/references/system/stack-server" title="Stack Server" description="Source: packages/spec/src/system/stack-server.zod.ts" />
<Card href="/docs/references/system/supplier-security" title="Supplier Security" description="Source: packages/spec/src/system/supplier-security.zod.ts" />
<Card href="/docs/references/system/tenant" title="Tenant" description="Source: packages/spec/src/system/tenant.zod.ts" />
Expand Down
1 change: 1 addition & 0 deletions content/docs/references/system/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"doc",
"---More---",
"dev-login",
"sms-config",
"stack-server"
]
}
Loading
Loading