Sms Config schema — System Protocol reference
SMS Service Configuration — the stack definition's sms block. There was no SMS configuration schema in the spec before this file.
SMS Service Configuration — the stack definition's sms block.
There was no SMS configuration schema in the spec before this file. The
shape below is typed from what its one reader reads:
resolveSmsCapabilityArg in @objectstack/service-sms, which
objectstack serve and bootStack both construct SmsServicePlugin
through. It reads three keys off config.sms, and only one of them can do
from a stack definition what it says:
| key | secret? | delivered from a stack? | here |
|---|---|---|---|
retries | no | yes | admitted |
provider | no — a transport tag | NO, measured | refused by name |
providerOptions | YES — every non-log transport requires a credential inside it (Twilio authToken, Aliyun accessKeySecret) | — | refused by name |
retries is the SMS service's own attempt budget. The service keeps it
when the sms settings namespace swaps its transport at kernel:ready
(setTransport replaces the transport and nothing else), so it applies to
whichever transport ends up delivering. It has no environment or settings
carrier, so this block is its only authoring door.
provider cannot select a delivering transport from here. A stack is
not allowed credentials (below), so the transport the plugin constructs from
this tag falls back to its log transport, and the transport that DOES
deliver is the one the settings namespace builds at kernel:ready from its
own provider setting — it never reads this one. Measured through
bootStack: sms: { provider: 'twilio' } with the Twilio credentials in the
environment boots with isConfigured() === false; adding OS_SMS_PROVIDER
makes it true. A declared key the runtime does not honour is refused rather
than admitted, with the setting that does work as its prescription. It comes
back, with a provider vocabulary, in the change that makes SmsServicePlugin
honour it.
providerOptions is refused WHOLE rather than narrowed to its non-secret
members, and the difference from mail's options is measured, not a
preference. The mail reader layers OS_EMAIL_SMTP_* over config.email.options
per key, so a file carrying host and user composes with an environment
carrying the password. The SMS reader hands providerOptions to the
transport as written, and nothing merges an environment credential into it:
a Twilio or Aliyun transport built from the non-secret members alone cannot
be constructed, so the plugin falls back to its log transport.
Why the credentials stop at this door at all: the stack definition is what
objectstack build compiles into the artifact, and artifacts are published.
SMS provider settings — the provider and its credentials — live in the sms
settings namespace (Setup → Settings → SMS Delivery), whose every key
accepts its OS_SMS_* environment override; every refusal here says so.
Source: packages/spec/src/system/sms-config.zod.ts
TypeScript Usage
import { StackSmsConfigSchema } from '@objectstack/spec/system';
import type { StackSmsConfig } from '@objectstack/spec/system';
// Validate data
const result = StackSmsConfigSchema.parse(data);StackSmsConfig
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| retries | integer | optional | Retry attempts on transport throw (default 0); applies to whichever transport the sms settings namespace selects. The provider and its credentials are not authorable here: Setup → Settings → SMS Delivery, or OS_SMS_PROVIDER and the other OS_SMS_* overrides |