Skip to content

IIS ​

Host the three APIs and oxykit-console on one Windows server. Each API is its own IIS site at the site root. The console is a static site that proxies those APIs on the same origin.

Install ​

  1. IIS, with the WebSocket Protocol feature (the Mail dashboard hub needs it).
  2. The .NET 10 Hosting Bundle (runtime plus the ASP.NET Core Module). Run iisreset after it installs.
  3. For the console site: URL Rewrite and Application Request Routing. In IIS Manager, open the server node → Application Request Routing Cache → Server Proxy Settings → Enable proxy.

Use the win-x64 zip from each product’s Releases page. The zip is framework-dependent and already contains web.config.

One site per API ​

Unzip into the site’s physical path. Do not put an API in a virtual directory. The apps expect to be at / on their binding.

Leave ASPNETCORE_URLS unset. The site binding is the URL. The zip’s README uses ASPNETCORE_URLS only when you start the .exe yourself.

Suggested bindings, localhost only, so other machines reach the APIs through the console site:

SiteBindingAssembly in the zip
Renderhttp://127.0.0.1:5066OxyKit.Render.Api.dll
Mailhttp://127.0.0.1:5026OxyKit.Mail.dll
Flowhttp://127.0.0.1:5077OxyKit.Flow.dll

App pool for each site:

SettingValue
.NET CLR versionNo Managed Code
PipelineIntegrated
Start ModeAlwaysRunning
Idle Time-out (minutes)0

Mail’s queue and Flow’s job worker stop when the pool idles. The pool identity needs Modify on each writable folder: Mail’s SQLite directory and pack root, Render’s pack root, Flow’s database directory, artifact cache, and file-output directory.

ASPNETCORE_ENVIRONMENT=Production. Scalar and /openapi/v1.json stay off.

Set the variables below on the site (system.webServer/aspNetCore/environmentVariables) or in that site’s web.config. Names use __ for nested config.

APIRequired
RenderSecurity__ApiKey. Rendering__TemplatesRoot (writable). Security__Jwt__Key, Issuer, and Audience when the console signs in with a Mail JWT — same triple as Mail.
MailDatabaseProvider, connection string, SmtpSettings__* (host, port, username, password), Jwt__Key (32+ characters), Jwt__Issuer, Jwt__Audience. MailTemplates__TemplatesRoot (writable).
FlowSecurity__ApiKey. Security__Jwt__* (same triple). Render__Engines__oxykit-render__BaseAddress (http://127.0.0.1:5066/) and __ApiKey. OxyKit.Mail__BaseAddress (http://127.0.0.1:5026/) and OxyKit.Mail__ApiKey. Artifacts__Root (writable).

Security__RequireLocalRequests defaults to true on Render. Leave it when only this server calls Render (Flow and the console proxy). Set it to false only if other machines call the Render binding directly.

Mail GET /health returns 503 when SMTP is down. The site is still up; do not recycle the pool because that check failed.

Request size ​

IIS drops a body larger than maxAllowedContentLength before the app reads it. The default is 30,000,000 bytes.

APIApp limit
Render5 MiB request, 2 MiB template pack. The 5 MiB cap is enforced when the process listens itself. Under IIS, set maxAllowedContentLength if you want the same cap.
Mail10 MiB (RequestLimits__MaxRequestBodySize). Mail rejects the body itself after IIS accepts it.
xml
<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="10485760" />
    </requestFiltering>
  </security>
</system.webServer>

Console ​

Unzip oxykit-console-{version}.zip into the site’s physical path. That zip is the same-origin build: it calls /api/v1, /hubs, /api/render, and /api/flow on this site. It is not a per-OS host zip and it has no web.config.

To build from the suite checkout instead, leave VITE_BACKEND_URL, VITE_RENDER_URL, and VITE_FLOW_URL unset:

bash
cd oxykit-console
npm install
npm run build

Point the site at the unzipped folder (or oxykit-console/dist when you built it). Edit config.json there to turn modules on or off, then refresh. No rebuild. Shape and defaults: Modules. Add these rewrite rules on that site. {R:0} is the full matched path. config.json is a real file, so the last rule leaves it alone.

xml
<rewrite>
  <rules>
    <rule name="mail-api" stopProcessing="true">
      <match url="^api/v1/.*" />
      <action type="Rewrite" url="http://127.0.0.1:5026/{R:0}" />
    </rule>
    <rule name="mail-hubs" stopProcessing="true">
      <match url="^hubs/.*" />
      <action type="Rewrite" url="http://127.0.0.1:5026/{R:0}" />
    </rule>
    <rule name="render-api" stopProcessing="true">
      <match url="^api/render/(.*)" />
      <action type="Rewrite" url="http://127.0.0.1:5066/{R:1}" />
    </rule>
    <rule name="flow-api" stopProcessing="true">
      <match url="^api/flow/(.*)" />
      <action type="Rewrite" url="http://127.0.0.1:5077/{R:1}" />
    </rule>
    <rule name="console" stopProcessing="true">
      <match url=".*" />
      <conditions logicalGrouping="MatchAll">
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
      </conditions>
      <action type="Rewrite" url="/index.html" />
    </rule>
  </rules>
</rewrite>

Put the console rule last. /login, /render, and /flow are client routes; a refresh has to serve index.html.

Render and Flow do not send CORS headers. The console has to stay on this origin. Mail CORS is only needed if a browser calls Mail on another origin.

Sign-in is /login on the console site. The session cookie is oxykit.access_token.

Check ​

  1. GET http://127.0.0.1:5066/health, :5026/health, :5077/health.
  2. Open the console site, sign in, and open Render and Flow.
  3. If a site returns 500.30 or 502.5, the Hosting Bundle is missing or the pool is not No Managed Code. Turn on stdoutLogEnabled in that site’s web.config and give the pool identity Modify on the logs folder.

Render · Mail · Flow