Skip to content

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

  1. In the Perimeter Authentication Configuration tool, add any Custom Location with a Path and Backend FQDN.
  2. Download the configuration.
  3. Open conf.d/pauth.conf and find the custom location block, or run nginx -t against the installed bundle.

Expected

    location /ClinicalRefs/ {
        include conf.d/app_settings/custom.conf;

Actual

    location /ClinicalRefs/ {
        include conf.d/app_settings/custom.conf

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

  1. Add an Upstream Server whose Backend FQDN is your Interconnect server.
  2. Add a SignalR App pointing at that same FQDN.
  3. 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.


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

  1. Download any configuration from the tool.
  2. grep -c root_domain conf.d/pauth.conf0.
  3. 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.