Files
Nitro-V3/docs/secure-production-setup.en.md
2026-05-06 06:27:40 +02:00

8.7 KiB

Secure Runtime Production Setup

Quick setup guide for running Nitro with:

  • configuration and gamedata served through /nitro-sec/file;
  • encrypted runtime /api/* calls;
  • obfuscated production bundles loaded as .dat.

Replace the example domains with your real domains:

  • https://hotel.example.com
  • https://nitro.example.com:2096

1. Build Nitro

Inside the Nitro-V3 repository:

yarn build

Then publish the dist folder to your web server, for example:

C:/inetpub/wwwroot/hotel/nitro

The deployed folder should contain at least:

configuration/
assets/
asset-loader.js
index.html
src/

2. configuration/client-mode.json

File:

Nitro-V3/dist/configuration/client-mode.json

Secure production configuration:

{
    "distObfuscationEnabled": true,
    "secureAssetsEnabled": true,
    "secureApiEnabled": true,
    "apiBaseUrl": "https://nitro.example.com:2096",
    "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
    "plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
}

Meaning:

  • distObfuscationEnabled: true loads app.js.dat and app.css.dat.
  • secureAssetsEnabled: true loads renderer-config.json, ui-config.json, and gamedata through /nitro-sec/file.
  • secureApiEnabled: true automatically encrypts /api/* requests.
  • apiBaseUrl must point to the emulator/API.
  • plainConfigBaseUrl and plainGamedataBaseUrl are fallbacks when secure assets are disabled.

3. configuration/renderer-config.json

File:

Nitro-V3/dist/configuration/renderer-config.json

Important values:

{
    "socket.url": "wss://nitro.example.com:2096",
    "api.url": "https://nitro.example.com:2096",
    "gamedata.url": "https://nitro.example.com:2096/nitro-sec/file?kind=gamedata&file=",
    "external.texts.url": [
        "${gamedata.url}/ExternalTexts.json",
        "${gamedata.url}/UITexts.json"
    ],
    "furnidata.url": "${gamedata.url}/FurnitureData.json?t=%timestamp%",
    "productdata.url": "${gamedata.url}/ProductData.json?t=%timestamp%",
    "avatar.actions.url": "${gamedata.url}/HabboAvatarActions.json?t=%timestamp%",
    "avatar.figuredata.url": "${gamedata.url}/FigureData.json?t=%timestamp%",
    "avatar.figuremap.url": "${gamedata.url}/FigureMap.json?t=%timestamp%",
    "avatar.effectmap.url": "${gamedata.url}/EffectMap.json?t=%timestamp%",
    "crypto.ws.enabled": true
}

If you are not using WebSocket crypto yet, use:

"crypto.ws.enabled": false

4. configuration/ui-config.json

File:

Nitro-V3/dist/configuration/ui-config.json

Static image and camera URLs can remain plain:

{
    "camera.url": "https://hotel.example.com/client/camera/",
    "thumbnails.url": "https://hotel.example.com/client/camera/thumbnail/%thumbnail%.png"
}

Non-sensitive images can stay static. JSON configuration and gamedata should go through the secure endpoint.

5. Emulator config.ini

Inside Arcturus-Morningstar-Extended, edit the emulator config:

Emulator/config.ini

Production example:

ws.enabled=true
ws.host=0.0.0.0
ws.port=2096
ws.whitelist=https://hotel.example.com
ws.ip.header=CF-Connecting-IP

crypto.ws.enabled=1

nitro.secure.assets.enabled=true
nitro.secure.api.enabled=true
nitro.secure.config.root=C:/inetpub/wwwroot/hotel/nitro/configuration
nitro.secure.gamedata.root=C:/inetpub/wwwroot/hotel/nitro/client/nitro/gamedata
nitro.secure.master_key=change-this-to-a-long-random-secret

login.remember.enabled=true
login.remember.duration.days=30
login.remember.jwt.secret=change-this-too-if-you-use-remember-me

Notes:

  • nitro.secure.config.root must point to the folder containing renderer-config.json, ui-config.json, and client-mode.json.
  • nitro.secure.gamedata.root must point to the live gamedata folder.
  • Files are read live from disk: if you update a JSON file, a new browser refresh reads the new version.
  • nitro.secure.master_key must be secret and stable. Never put it in public files.

6. Cloudflare

If you use Cloudflare:

  1. Keep the proxy enabled for the website domain hotel.example.com.
  2. Make sure Cloudflare supports/proxies the port used by nitro.example.com:2096.
  3. Always use HTTPS/WSS in the browser:
"api.url": "https://nitro.example.com:2096",
"socket.url": "wss://nitro.example.com:2096"

If you get CORS errors, check:

ws.whitelist=https://hotel.example.com

7. IIS / .dat MIME type

If obfuscated .dat assets are enabled, IIS must serve them correctly.

Add this MIME type:

Extension: .dat
MIME type: application/octet-stream

Without it, the browser can receive 404 even when the file exists.

8. Final checklist

  • client-mode.json has secureAssetsEnabled=true.
  • client-mode.json has secureApiEnabled=true.
  • renderer-config.json uses /nitro-sec/file?kind=gamedata&file=.
  • api.url points to https://nitro.example.com:2096.
  • socket.url points to wss://nitro.example.com:2096.
  • config.ini has the correct nitro.secure.config.root.
  • config.ini has the correct nitro.secure.gamedata.root.
  • config.ini has a stable nitro.secure.master_key.
  • IIS knows the .dat MIME type.
  • Restart the emulator after changing config.ini.
  • Refresh the browser after changing JSON files in configuration or gamedata.

9. Temporarily disable secure mode

For quick debugging, only change client-mode.json:

{
    "distObfuscationEnabled": false,
    "secureAssetsEnabled": false,
    "secureApiEnabled": false,
    "apiBaseUrl": "https://nitro.example.com:2096",
    "plainConfigBaseUrl": "https://hotel.example.com/configuration/",
    "plainGamedataBaseUrl": "https://hotel.example.com/client/nitro/gamedata/"
}

Then hard refresh the browser.

10. configuration/bootstrap.js

File:

Nitro-V3/dist/configuration/bootstrap.js

This is the first loader when you use the external secure mode.

It does three things:

  1. opens an ECDH session with the emulator through /nitro-sec/bootstrap;
  2. downloads encrypted client-mode.json through /nitro-sec/file?kind=config;
  3. downloads encrypted asset-loader.js and imports it as a JavaScript module.

Value to check

Inside bootstrap.js there is:

const API_BASE = "https://nitro.example.com:2096";

It must point to your public emulator/API URL.

In production:

const API_BASE = "https://nitro.example.com:2096";

In local development:

const API_BASE = "http://localhost:2096";

If bootstrap.js fails, it automatically falls back to the plain loader:

configuration/asset-loader.js

So asset-loader.js must always exist inside the configuration folder.

11. configuration/asset-loader.js

File:

Nitro-V3/dist/configuration/asset-loader.js

This loader loads the actual bundle:

  • if distObfuscationEnabled=true

    • it loads app.css.dat;
    • it loads app.js.dat;
    • it decodes, decompresses, and imports the bundle from a blob.
  • if distObfuscationEnabled=false

    • it loads assets/app.css;
    • it loads assets/app.js.

Required files in production

With obfuscation enabled, these files must exist:

assets/app.css.dat
assets/app.js.dat
configuration/asset-loader.js
configuration/bootstrap.js
configuration/client-mode.json

With obfuscation disabled, these files must exist:

assets/app.css
assets/app.js
configuration/asset-loader.js
configuration/client-mode.json

12. index.html

index.html should stay minimal.

Secure production example:

<div id="root"></div>
<script src="/configuration/bootstrap.js"></script>

Vite development example:

<div id="root"></div>
<script type="module" src="/src/bootstrap.ts"></script>

Do not mix the two flows:

  • production build: use configuration/bootstrap.js;
  • yarn start development: use /src/bootstrap.ts.

13. Files inside /configuration

The configuration folder should contain:

asset-loader.js
bootstrap.js
client-mode.json
renderer-config.json
ui-config.json
adsense.json              optional
hotlooks.json             if register hot looks are enabled
UITexts.json              if separate UI texts are enabled

Login news should not live in news.json in production. They come from the database through:

"login.news.url": "${api.url}/api/auth/news"

The emulator reads from the ui_news table.

With secureAssetsEnabled=true, client-loaded files go through:

https://nitro.example.com:2096/nitro-sec/file?kind=config&file=...

The emulator reads them from:

nitro.secure.config.root=C:/inetpub/wwwroot/hotel/nitro/configuration

If you add new JSON/JS files inside configuration and want to protect them, they must be requested through the secure endpoint or loaded through bootstrap.js.