Kuiper generator defects
Defects found in Epic's Perimeter Authentication Configuration tool, and in
the setup instructions that accompany it, while building the perimeter_auth
role against them. Findings 1–3 are defects in generated output, each worked
around by a marked deviation in templates/pauth.conf.j2; finding 4 is a
defect in the documented procedure, worked around by what the role renders
instead.
They are recorded here in enough detail to raise with Epic. Every workaround we carry is permanent maintenance, so the list should shrink as Epic fixes them — when one is fixed, remove the corresponding deviation from the template.
Environment for all findings
| Generator version | 1.0.2 |
| Kuiper build | 119.1.478.6561 (Kuiper August 2026) |
| Found | 2026-08-07 (findings 1–3), 2026-08-19 (finding 4) |
| Method | Configurations created in the tool, downloaded, and inspected; finding 4 additionally reproduced with nginx -t on nginx 1.30.4 / RHEL 9.8 |
1. Custom locations generate invalid nginx configuration
Severity: high — nginx refuses to start or reload.
The include line in a generated custom location block is missing its
terminating semicolon. Every other include the generator emits has one.
Reproduce
- In the Perimeter Authentication Configuration tool, add any Custom Location with a Path and Backend FQDN.
- Download the configuration.
- Open
conf.d/pauth.confand find the custom location block, or runnginx -tagainst the installed bundle.
Expected
Actual
Impact. nginx -t fails and the service will not load the configuration, so
any customer following the documented flow — generate, install, start — is dead
in the water until they spot a missing character. Epic's own guide directs
customers to custom locations for Isolated Recovery Environments, internal
websites and third-party integrations, so this is not an edge case.
Workaround. The role emits the semicolon.
2. SignalR locations never match their upstream group
Severity: medium — silent loss of connection reuse, no error at any level.
The generator can emit upstream blocks, and separately emits SignalR locations
that set $backend_fqdn with a :443 suffix. Those two never match, so the
upstream group is dead configuration.
nginx resolves a variable proxy_pass against upstream groups by name and
port. A group declared as upstream <fqdn> { } carries port 0, so it only matches
a target with no port. With the :443 suffix nginx falls through to a plain
resolver lookup instead, and the keepalive directive in the unused group has
no effect.
Reproduce
- Add an Upstream Server whose Backend FQDN is your Interconnect server.
- Add a SignalR App pointing at that same FQDN.
- Download and inspect
conf.d/pauth.conf.
Actual
upstream ic.example.com {
server ic.example.com:443;
keepalive 32;
}
...
location /Interconnect-EventWS/eventnotificationservice {
include conf.d/app_settings/signalr.conf;
set $backend_fqdn ic.example.com:443; # never matches the group above
}
Impact. SignalR is a long-lived, high-frequency connection path, and it is the
one most likely to benefit from connection reuse. Nothing reports the problem —
the configuration is valid, traffic flows, and the only symptom is extra TCP and
TLS handshake load. The generator's own inline comment actively steers customers
wrong: "set $backend_fqdn ic.yourorg.com:443; -- Make sure to include the port
here!", which is correct only when no upstream group exists.
Workaround. The role derives one upstream group per distinct backend, names
each after its FQDN, and omits the port from the matching set $backend_fqdn.
Related, lower severity. The tool's Upstream Server entry has a user-entered
Name field that is discarded — the generated block is always named after the
Backend FQDN. Confirmed by naming one hsw_pool_named_differently with backend
hsw-vip.example.com and getting upstream hsw-vip.example.com. The field
implies control it does not give, and a name that differs from the FQDN would
silently produce a group nothing matches.
3. Cookie domain fallback is emitted in auth.js but no longer in pauth.conf
Severity: medium — a documented fallback path cannot work.
The auth.js shipped in the 1.0.2 bundle reads an X-Root-Domain request header
and uses it as the perimeter cookie domain when the database returns an empty
PerimeterCookieDomain:
let rootDomain = r.headersIn['X-Root-Domain'];
...
let domain = resp.PerimeterCookieDomain;
if (domain === "") {
domain = rootDomain;
}
But the pauth.conf generated in the same bundle never sets $root_domain
and never sends X-Root-Domain on the /hdtokenauth subrequest. Earlier
hand-maintained templates did both — Epic's EEDS template dated 4/24/26 still
carries root_domain as a REPLACETHIS field.
Reproduce
- Download any configuration from the tool.
grep -c root_domain conf.d/pauth.conf→0.grep -n X-Root-Domain conf.d/auth.js→ matches.
Impact. Whenever the database returns an empty cookie domain, domain is
undefined and the Set-Cookie header is built with Domain=undefined. Browsers
reject it, so the perimeter token never persists and the user loops between the
proxy and the authentication Hyperspace Web servers. This is only reachable when
the Chronicles Cookie Domain is unset — which Epic now requires to be set from
February 2026 — so it is most likely to bite an environment mid-upgrade or
mid-configuration.
Workaround. The role emits both set $root_domain and the X-Root-Domain
header, driven by perimeter_auth_pauth_root_domain.
4. The Isolated Recovery Environment instructions produce a configuration nginx cannot load
Severity: high — nginx refuses to start or reload.
This one is in the documentation rather than the generated file. The guide's
Isolated Recovery Environment section directs customers to add a custom location
per application and to "uncomment every line except for the auth_request and
auth_request_set lines". But the custom location body does not stop there. A
few lines further on it reads the variable that auth_request_set declares:
auth_request /hdtokenauth; # commented per the guide
auth_request_set $tokenvalue $upstream_http_x_newtoken; # commented per the guide
error_page 401 = @authServer;
add_header Set-Cookie $tokenvalue; # still live, now reading an undeclared variable
$tokenvalue is created by auth_request_set and by nothing else, and nginx
resolves variable references when it loads the configuration, so commenting out
the only declaration while leaving a reference behind is fatal. The service does
not start or reload.
The lines above are conf.d/app_settings/custom.conf from a 1.0.2 bundle, where
that body lives in the shared include rather than inline in the location as the
instructions assume.
Reproduce
Confirmed on nginx 1.30.4 (RHEL 9.8), three self-contained configurations
differing only in which of the three lines are commented, each validated with
nginx -t -c:
| Custom location body | nginx -t |
|---|---|
auth_request_set live, add_header live |
syntax is ok |
auth_request + auth_request_set commented, add_header live — the guide's instruction |
[emerg] unknown "tokenvalue" variable |
| all three commented — what the role renders | syntax is ok |
So it is specifically the third line that has to go, and Epic's instruction names only the first two.
Impact. The service will not start or reload, so an IRE built by following the instructions is down until someone works out that a third line has to go — and an IRE is a recovery environment, the worst possible place for a configuration step that only fails at the moment it is needed.
The layout change compounds it. Because 1.0.2 moved the body into an include
shared by every custom location, the procedure cannot be followed line-by-line at
all, and commenting the directives where they now live would disable the
subrequest for non-IRE custom locations too. So the instructions need updating for
the current bundle regardless of the undeclared variable.
Workaround. perimeter_auth_pauth_ire renders custom locations against a
separate custom_ire.conf include that comments out all three directives, so the
shared custom.conf is untouched. See
Isolated Recovery Environments.
Reporting
Worth raising with the Client Systems – Web & Service Servers team, referencing SLG 8524651 (the perimeter authentication reference used throughout Epic's guide).
When a defect is fixed, drop the matching deviation comment from
roles/perimeter_auth/templates/pauth.conf.j2 — or, for finding 4, the
custom_ire.conf derivation in tools/install-assets.sh — and confirm against a
freshly generated bundle. See
Adopting a new Epic template version.