8.10 The proxy and sms services
Before writing a custom handler for an external provider, check whether the
runtime already owns the shape you need. proxy covers fixed forwarding with
host-injected credentials; sms defines a stable provider-independent SMS
surface over a swappable adapter.
proxy: forward without exposing the key
{ "path": "/stripe", "service": "proxy", "config": {
"access": { "read": "A", "write": "A", "invoke": "A" },
"target": "https://api.stripe.com",
"inject": "infra:stripe-key"
} }
GET /stripe/v1/charges?limit=3 becomes
GET https://api.stripe.com/v1/charges?limit=3. The target fixes the upstream
host; callers cannot turn the mount into an open proxy. Client headers are
forwarded except for Host, the caller's RS2 Authorization/cookie
credentials, and hop-by-hop proxy headers; the upstream response comes back
unchanged.
inject adds authentication immediately before the request leaves the host:
"infra:<name>"uses an operator-managed credential (recommended for operator-owned keys).- An inline strategy can draw from a tenant secret, for example
{"auth":"bearer","token":"secret:apiKey"}when the mount grants"secrets": ["apiKey"].
Strategies include bearer, named header, basic auth, query parameter, HMAC, and AWS SigV4. HMAC and SigV4 materialize the body to sign it; the simpler strategies leave streaming bodies untouched. In every case, the secret is absent from the forwarded tenant config and never enters guest code.
Use proxy when the external API should remain visible as itself. Use a wrapper
or custom service when you need to present a different contract.
sms: one surface, replaceable providers
{ "path": "/sms", "service": "sms", "config": {
"access": { "read": "A", "invoke": "A" },
"store": { "adapter": "code:twilio@<version>" }
} }
The tenant-facing conversation stays fixed:
| Request | Response |
|---|---|
POST /sms/send with {to, body} |
201 {id} |
GET /sms/status/<id> |
The provider's delivery status, or 404 |
The adapter translates that canonical message to Twilio, SNS, or another
provider. It is a resident JavaScript adapter, selected with store.adapter,
and usually uses an httpOut grant with host-side credential injection. No
first-party SMS provider ships in the node, so builtin: is rejected: deploy a
code: adapter or reference an operator infra: that resolves to one.
This separation is the same one used by storage adapters: applications depend on a stable service contract, while provider choice remains configuration.
← Previous: 8.9 Loadable storage adapters · Manual home · Next: 8.11 Manifests and catalogues →