Migrating from reactor http to egresses
Egresses are Hantera's typed, ACL-gated outbound-traffic primitive. They replace ad-hoc outbound calls made with the reactor http module together with the legacy registry/security/allowedHosts whitelist.
The legacy http module remains available for back-compat, but new components should prefer egresses: a single typed call site, a tenant-scoped ACL, and — for rules — the ability to make a safe blocking call inline.
What changes
| Before | After |
|---|---|
Reactor calls web.http directly | An egress component performs the call; callers use the call filter |
Host allowed via registry/security/allowedHosts | The existence of the egress resource is the host whitelist |
| Any component with the reactor runtime could reach the host | Only callers granted egresses/<key>:call may reach it |
| Rules could not make HTTP calls at all | Rules may call egresses marked idempotent: true |
Recipe
1. Move the HTTP call into an egress component
Take the body of your reactor's http call and put it into a new .hegress / .heg component. It imports http (in scope only in the egress runtime), declares the caller's request fields as params, and returns the result via a single from.
//// components/egresses/createSession.hegress
import 'http'
import 'json'
param configurationId: text
param toAddress: {
name: text
address1: text
postalCode: text
city: text
country: text
}
let config = registry 'apps/my-app'
from
let res = http {
method = 'POST'
url = $'{config->''baseUrl''}/checkouts'
headers = {
'Content-Type' -> 'application/json'
'X-Api-Key' -> config->'clientSecret'
}
body = { configurationId = configurationId, toAddress = toAddress } asJson
}
res.status match
200 |> res.body parseJson
|> { error = { code = $'http_{res.status}', message = res.body } }2. Declare an egress resource in h_app.yaml
The resource binds a key to the component, sets the idempotency gate, and carries the ACL.
egresses:
- id: createSession
componentId: components/egresses/createSession.hegress
idempotent: trueMark idempotent: true only if repeated identical calls have the same observable effect as a single call. Idempotent egresses may be called from rules; non-idempotent egresses may only be called from reactors and jobs.
3. Replace the web.http call with call
Put the request payload on the left and the fully qualified egress key on the right.
from
{ configurationId = '...', toAddress = { ... } }
call 'apps/my-app/createSession'call returns the egress component program's result directly. There is no envelope wrapper — transport and business errors flow through the component's own result convention (by convention { error = { code, message } }).
4. Drop the allowedHosts entry
Once all outbound traffic to a host goes through egresses, the host no longer needs an allowedHosts entry — the egress resource is the whitelist. Remove the entry unless a remaining legacy http-module component still needs it.
Wrapping a raw URL with platform/http
If you don't need a typed component, install a resource that binds the built-in platform/http component and pins the destination with a parameter override so callers cannot redirect the request:
egresses:
- id: shopfront-webhook
componentId: platform/http
idempotent: true
parameters:
url: https://shop.example.com/hooks/order-createdparameters holds literal overrides keyed by the component's param names. For dynamic values (secrets, base URLs), read them inside the egress component via the registry-> macro rather than pinning them on the resource.
from
{
method = 'POST'
headers = { 'Content-Type' -> 'application/json' }
body = { orderId = order.id, total = order.total } asJson
}
call 'my-app/shopfront-webhook'Permissions
A caller must hold egresses/<egressKey>:call to invoke an egress, and this is never granted implicitly — list it explicitly in the calling component's ACL, whether the egress belongs to the same app or another. The class-level wildcard egresses:call grants the ability to call every egress.
Offline type-checking
The Hantera Development Studio type-checks both .hegress/.heg components and every call '<key>' against the egresses declared in the open app's h_app.yaml. No tenant connection is required — a manifest declaration is sufficient for full local validation.