From 40a21cfc165cfbd1db25ee1ffe0933acac144fbd Mon Sep 17 00:00:00 2001
From: burgerni10 <nicolas.burger@optimistik.com>
Date: Wed, 13 Mar 2024 13:07:26 +0100
Subject: [PATCH] feat(logger): Add OIAnalytics Pino transport and refactor
 registration service to reload logger on change

---
 backend/package-lock.json                     | 237 ++++-
 backend/package.json                          |   3 +-
 .../v3.2.0-oia-registration.ts                |  15 +-
 backend/src/index.ts                          |  33 +-
 .../src/repository/engine.repository.spec.ts  |  38 +-
 backend/src/repository/engine.repository.ts   |  29 +-
 backend/src/repository/log.repository.spec.ts |  22 +-
 .../src/service/logger/logger.service.spec.ts | 116 ++-
 backend/src/service/logger/logger.service.ts  |  44 +-
 backend/src/service/logger/loki-transport.ts  | 220 -----
 .../service/logger/oianalytics-transport.ts   | 156 ++++
 .../service/{ => oia}/command.service.spec.ts |  27 +-
 .../src/service/{ => oia}/command.service.ts  |  17 +-
 .../service/oia/registration.service.spec.ts  | 865 ++++++++++++++++++
 .../src/service/oia/registration.service.ts   | 347 +++++++
 backend/src/service/oibus.service.spec.ts     | 763 +--------------
 backend/src/service/oibus.service.ts          | 324 +------
 backend/src/service/reload.service.spec.ts    |  13 +-
 backend/src/service/reload.service.ts         |  23 +-
 .../src/tests/__mocks__/koa-context.mock.ts   |   2 +
 .../src/tests/__mocks__/oibus-service.mock.ts |  13 +-
 .../__mocks__/registration-service.mock.ts    |   7 +
 .../tests/__mocks__/reload-service.mock.ts    |   1 +
 .../registration.controller.spec.ts           |  14 +-
 .../controllers/registration.controller.ts    |   8 +-
 .../validators/engine.validator.spec.ts       |  24 +-
 .../validators/oibus-validation-schema.ts     |   4 +-
 backend/src/web-server/koa.ts                 |   2 +
 backend/src/web-server/middlewares/oibus.ts   |   3 +
 backend/src/web-server/web-server.ts          |   3 +
 backend/tsconfig.app.json                     |   2 +-
 .../edit-engine/edit-engine.component.html    |  22 +-
 .../edit-engine/edit-engine.component.spec.ts |  23 +-
 .../edit-engine/edit-engine.component.ts      |   8 +-
 .../app/engine/engine-detail.component.html   |  31 +-
 .../engine/engine-detail.component.spec.ts    |  16 +-
 frontend/src/i18n/en.json                     |  15 +-
 shared/model/engine.model.ts                  |   5 +-
 shared/model/logs.model.ts                    |   3 +-
 39 files changed, 1996 insertions(+), 1502 deletions(-)
 delete mode 100644 backend/src/service/logger/loki-transport.ts
 create mode 100644 backend/src/service/logger/oianalytics-transport.ts
 rename backend/src/service/{ => oia}/command.service.spec.ts (88%)
 rename backend/src/service/{ => oia}/command.service.ts (91%)
 create mode 100644 backend/src/service/oia/registration.service.spec.ts
 create mode 100644 backend/src/service/oia/registration.service.ts
 create mode 100644 backend/src/tests/__mocks__/registration-service.mock.ts

diff --git a/backend/package-lock.json b/backend/package-lock.json
index 578b6ad07b..b8851d872d 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -51,6 +51,7 @@
         "pg": "8.11.3",
         "pino": "8.19.0",
         "pino-abstract-transport": "1.1.0",
+        "pino-loki": "2.2.1",
         "pino-pretty": "10.3.1",
         "pino-roll": "1.0.0-rc.1",
         "selfsigned": "2.4.1",
@@ -3231,6 +3232,17 @@
       "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
       "dev": true
     },
+    "node_modules/@sindresorhus/is": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+      "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/is?sponsor=1"
+      }
+    },
     "node_modules/@sinonjs/commons": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
@@ -3896,6 +3908,17 @@
         "proper-lockfile": "^4.1.2"
       }
     },
+    "node_modules/@szmarczak/http-timer": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
+      "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
+      "dependencies": {
+        "defer-to-connect": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/@tediousjs/connection-string": {
       "version": "0.5.0",
       "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.5.0.tgz",
@@ -4009,6 +4032,17 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/cacheable-request": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
+      "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
+      "dependencies": {
+        "@types/http-cache-semantics": "*",
+        "@types/keyv": "^3.1.4",
+        "@types/node": "*",
+        "@types/responselike": "^1.0.0"
+      }
+    },
     "node_modules/@types/connect": {
       "version": "3.4.38",
       "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
@@ -4093,6 +4127,11 @@
       "integrity": "sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==",
       "dev": true
     },
+    "node_modules/@types/http-cache-semantics": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+      "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA=="
+    },
     "node_modules/@types/http-errors": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
@@ -4185,6 +4224,14 @@
       "integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==",
       "dev": true
     },
+    "node_modules/@types/keyv": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
+      "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/koa": {
       "version": "2.15.0",
       "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.15.0.tgz",
@@ -4372,6 +4419,14 @@
         "safe-buffer": "~5.1.1"
       }
     },
+    "node_modules/@types/responselike": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
+      "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/retry": {
       "version": "0.12.5",
       "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.5.tgz",
@@ -5538,6 +5593,45 @@
         "node": ">= 6.0.0"
       }
     },
+    "node_modules/cacheable-lookup": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+      "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
+      "engines": {
+        "node": ">=10.6.0"
+      }
+    },
+    "node_modules/cacheable-request": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
+      "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
+      "dependencies": {
+        "clone-response": "^1.0.2",
+        "get-stream": "^5.1.0",
+        "http-cache-semantics": "^4.0.0",
+        "keyv": "^4.0.0",
+        "lowercase-keys": "^2.0.0",
+        "normalize-url": "^6.0.1",
+        "responselike": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cacheable-request/node_modules/get-stream": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+      "dependencies": {
+        "pump": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/call-bind": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
@@ -5705,6 +5799,25 @@
         "node": ">=12"
       }
     },
+    "node_modules/clone-response": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
+      "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
+      "dependencies": {
+        "mimic-response": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/clone-response/node_modules/mimic-response": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+      "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -6105,6 +6218,14 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/defer-to-connect": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+      "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/define-data-property": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
@@ -7819,6 +7940,30 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/got": {
+      "version": "11.8.6",
+      "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
+      "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
+      "dependencies": {
+        "@sindresorhus/is": "^4.0.0",
+        "@szmarczak/http-timer": "^4.0.5",
+        "@types/cacheable-request": "^6.0.1",
+        "@types/responselike": "^1.0.0",
+        "cacheable-lookup": "^5.0.3",
+        "cacheable-request": "^7.0.2",
+        "decompress-response": "^6.0.0",
+        "http2-wrapper": "^1.0.0-beta.5.2",
+        "lowercase-keys": "^2.0.0",
+        "p-cancelable": "^2.0.0",
+        "responselike": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10.19.0"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/got?sponsor=1"
+      }
+    },
     "node_modules/graceful-fs": {
       "version": "4.2.11",
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -8004,6 +8149,11 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/http-cache-semantics": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+      "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
+    },
     "node_modules/http-errors": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -8044,6 +8194,18 @@
         "node": ">= 14"
       }
     },
+    "node_modules/http2-wrapper": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
+      "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
+      "dependencies": {
+        "quick-lru": "^5.1.1",
+        "resolve-alpn": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=10.19.0"
+      }
+    },
     "node_modules/https-proxy-agent": {
       "version": "7.0.4",
       "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz",
@@ -9426,8 +9588,7 @@
     "node_modules/json-buffer": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
-      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
-      "dev": true
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
     },
     "node_modules/json-parse-even-better-errors": {
       "version": "2.3.1",
@@ -9533,7 +9694,6 @@
       "version": "4.5.4",
       "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
       "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
-      "dev": true,
       "dependencies": {
         "json-buffer": "3.0.1"
       }
@@ -9865,6 +10025,14 @@
       "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
       "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
     },
+    "node_modules/lowercase-keys": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+      "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/lru-cache": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -11331,6 +11499,17 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/normalize-url": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/npm-run-path": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
@@ -11598,6 +11777,14 @@
         "node": ">=14.6"
       }
     },
+    "node_modules/p-cancelable": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
+      "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/p-is-promise": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz",
@@ -11976,6 +12163,23 @@
         "split2": "^4.0.0"
       }
     },
+    "node_modules/pino-loki": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/pino-loki/-/pino-loki-2.2.1.tgz",
+      "integrity": "sha512-NLo9INo4lOQ8PfC0i/AZBh8xh6LCCkuGRuREq69Mw25zmoISlZiYCn5FBidagu+Cjm/dvhvt19THRhc0B71NnA==",
+      "dependencies": {
+        "commander": "^10.0.1",
+        "got": "^11.8.6",
+        "pino-abstract-transport": "^1.1.0",
+        "pump": "^3.0.0"
+      },
+      "bin": {
+        "pino-loki": "dist/cli.cjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/Julien-R44"
+      }
+    },
     "node_modules/pino-pretty": {
       "version": "10.3.1",
       "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.3.1.tgz",
@@ -12584,6 +12788,17 @@
       "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
       "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
     },
+    "node_modules/quick-lru": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/raw-body": {
       "version": "2.5.2",
       "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
@@ -12752,6 +12967,11 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/resolve-alpn": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+      "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
+    },
     "node_modules/resolve-cwd": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
@@ -12852,6 +13072,17 @@
         "node": ">=10"
       }
     },
+    "node_modules/responselike": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
+      "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
+      "dependencies": {
+        "lowercase-keys": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/retry": {
       "version": "0.12.0",
       "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
diff --git a/backend/package.json b/backend/package.json
index a264040418..779103a410 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -89,6 +89,7 @@
     "pg": "8.11.3",
     "pino": "8.19.0",
     "pino-abstract-transport": "1.1.0",
+    "pino-loki": "2.2.1",
     "pino-pretty": "10.3.1",
     "pino-roll": "1.0.0-rc.1",
     "selfsigned": "2.4.1",
@@ -152,7 +153,7 @@
     "assets": [
       "dist/frontend/**/*",
       "dist/backend/src/service/logger/sqlite-transport.js",
-      "dist/backend/src/service/logger/loki-transport.js",
+      "dist/backend/src/service/logger/oianalytics-transport.js",
       "dist/backend/src/db/**/*.js",
       "dist/shared/**/*.js",
       "node_modules/sqlite3/**/*.node",
diff --git a/backend/src/db/entity-migrations/v3.2.0-oia-registration.ts b/backend/src/db/entity-migrations/v3.2.0-oia-registration.ts
index f4ec45d690..02f539b5de 100644
--- a/backend/src/db/entity-migrations/v3.2.0-oia-registration.ts
+++ b/backend/src/db/entity-migrations/v3.2.0-oia-registration.ts
@@ -88,11 +88,10 @@ interface NewSouthOIAnalyticsSettings {
 export async function up(knex: Knex): Promise<void> {
   await createRegistrationTable(knex);
   await createCommandTable(knex);
-  await addProxyServerSettings(knex);
+  await updateEngineSettings(knex);
   await updateNorthOIAnalyticsConnectors(knex);
   await updateSouthOIAnalyticsConnectors(knex);
   await updateOIAnalyticsHistoryQueries(knex);
-  await updateEngineLogSettings(knex);
 }
 
 function createDefaultEntityFields(table: CreateTableBuilder): void {
@@ -100,10 +99,13 @@ function createDefaultEntityFields(table: CreateTableBuilder): void {
   table.timestamps(false, true);
 }
 
-async function addProxyServerSettings(knex: Knex) {
+async function updateEngineSettings(knex: Knex) {
   await knex.schema.alterTable(ENGINES_TABLE, table => {
     table.boolean('proxy_enabled').defaultTo(false);
     table.integer('proxy_port').defaultTo(9000);
+    table.enum('log_oia_level', LOG_LEVELS).notNullable().defaultTo('silent');
+    table.integer('log_oia_interval').notNullable().defaultTo(10);
+    table.dropColumn('log_loki_token_address');
   });
 }
 
@@ -267,13 +269,6 @@ async function updateOIAnalyticsHistoryQueries(knex: Knex): Promise<void> {
   }
 }
 
-async function updateEngineLogSettings(knex: Knex): Promise<void> {
-  await knex.schema.table(ENGINES_TABLE, table => {
-    table.enum('log_oia_level', LOG_LEVELS).notNullable().defaultTo('silent');
-    // TODO: simplify pino-loki
-  });
-}
-
 export async function down(knex: Knex): Promise<void> {
   await knex.schema.dropTable(REGISTRATIONS_TABLE);
   await knex.schema.dropTable(COMMANDS_TABLE);
diff --git a/backend/src/index.ts b/backend/src/index.ts
index 64c8829028..e04a4078bc 100644
--- a/backend/src/index.ts
+++ b/backend/src/index.ts
@@ -15,7 +15,8 @@ import HistoryQueryService from './service/history-query.service';
 import OIBusService from './service/oibus.service';
 import { migrateCrypto, migrateEntities, migrateLogsAndMetrics, migrateSouthCache } from './db/migration-service';
 import HomeMetricsService from './service/home-metrics.service';
-import CommandService from './service/command.service';
+import CommandService from './service/oia/command.service';
+import RegistrationService from './service/oia/registration.service';
 import ProxyServer from './web-server/proxy-server';
 
 const CONFIG_DATABASE = 'oibus.db';
@@ -77,7 +78,12 @@ const LOG_DB_NAME = 'logs.db';
 
   await createFolder(LOG_FOLDER_NAME);
   const loggerService = new LoggerService(encryptionService, path.resolve(LOG_FOLDER_NAME));
-  await loggerService.start(oibusSettings.id, oibusSettings.name, oibusSettings.logParameters);
+  await loggerService.start(
+    oibusSettings.id,
+    oibusSettings.name,
+    oibusSettings.logParameters,
+    repositoryService.registrationRepository.getRegistrationSettings()
+  );
 
   const northService = new NorthService(encryptionService, repositoryService);
   const southService = new SouthService(encryptionService, repositoryService);
@@ -101,17 +107,10 @@ const LOG_DB_NAME = 'logs.db';
     loggerService.logger!
   );
 
-  const commandService = new CommandService(oibusSettings.id, repositoryService, encryptionService, loggerService.logger!, binaryFolder);
+  const commandService = new CommandService(repositoryService, encryptionService, loggerService.logger!, binaryFolder);
   commandService.start();
 
-  const oibusService = new OIBusService(
-    engine,
-    historyQueryEngine,
-    repositoryService,
-    encryptionService,
-    commandService,
-    loggerService.logger!
-  );
+  const oibusService = new OIBusService(engine, historyQueryEngine);
 
   await engine.start();
   await historyQueryEngine.start();
@@ -138,13 +137,24 @@ const LOG_DB_NAME = 'logs.db';
     southService,
     engine,
     historyQueryEngine,
+    oibusService,
     proxyServer
   );
+
+  const registrationService = new RegistrationService(
+    repositoryService,
+    encryptionService,
+    commandService,
+    reloadService,
+    loggerService.logger!
+  );
+  registrationService.start();
   const server = new WebServer(
     oibusSettings.id,
     oibusSettings.port,
     encryptionService,
     reloadService,
+    registrationService,
     repositoryService,
     southService,
     northService,
@@ -164,6 +174,7 @@ const LOG_DB_NAME = 'logs.db';
     await commandService.stop();
     await proxyServer.stop();
     await server.stop();
+    registrationService.stop();
     loggerService.stop();
     console.info('OIBus stopped');
     stopping = false;
diff --git a/backend/src/repository/engine.repository.spec.ts b/backend/src/repository/engine.repository.spec.ts
index b7a1f52dcd..4f7b7da230 100644
--- a/backend/src/repository/engine.repository.spec.ts
+++ b/backend/src/repository/engine.repository.spec.ts
@@ -47,12 +47,12 @@ describe('Empty engine repository', () => {
           level: 'silent',
           interval: 60,
           address: '',
-          tokenAddress: '',
           username: '',
           password: ''
         },
         oia: {
-          level: 'silent'
+          level: 'silent',
+          interval: 10
         }
       }
     };
@@ -62,7 +62,7 @@ describe('Empty engine repository', () => {
     expect(database.prepare).toHaveBeenCalledWith(
       'INSERT INTO engines (id, name, port, proxy_enabled, proxy_port, log_console_level, log_file_level, log_file_max_file_size, ' +
         'log_file_number_of_files, log_database_level, log_database_max_number_of_logs, log_loki_level, ' +
-        'log_loki_interval, log_loki_address, log_loki_token_address, log_loki_username, log_loki_password, log_oia_level) ' +
+        'log_loki_interval, log_loki_address, log_loki_username, log_loki_password, log_oia_level, log_oia_interval) ' +
         'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);'
     );
     expect(run).toHaveBeenCalledWith(
@@ -80,10 +80,10 @@ describe('Empty engine repository', () => {
       command.logParameters.loki.level,
       command.logParameters.loki.interval,
       command.logParameters.loki.address,
-      command.logParameters.loki.tokenAddress,
       command.logParameters.loki.username,
       command.logParameters.loki.password,
-      command.logParameters.oia.level
+      command.logParameters.oia.level,
+      command.logParameters.oia.interval
     );
 
     expect(run).toHaveBeenCalledTimes(2);
@@ -112,12 +112,12 @@ describe('Empty engine repository', () => {
           level: 'silent',
           interval: 60,
           address: '',
-          tokenAddress: '',
           username: '',
           password: ''
         },
         oia: {
-          level: 'silent'
+          level: 'silent',
+          interval: 10
         }
       }
     };
@@ -125,8 +125,8 @@ describe('Empty engine repository', () => {
     expect(database.prepare).toHaveBeenCalledWith(
       'UPDATE engines SET name = ?, port = ?, proxy_enabled = ?, proxy_port = ?, log_console_level = ?, log_file_level = ?, log_file_max_file_size = ?, ' +
         'log_file_number_of_files = ?, log_database_level = ?, log_database_max_number_of_logs = ?, log_loki_level = ?, ' +
-        'log_loki_interval = ?, log_loki_address = ?, log_loki_token_address = ?, log_loki_username = ?, ' +
-        'log_loki_password = ?, log_oia_level = ? WHERE rowid=(SELECT MIN(rowid) FROM engines);'
+        'log_loki_interval = ?, log_loki_address = ?, log_loki_username = ?, ' +
+        'log_loki_password = ?, log_oia_level = ?, log_oia_interval = ? WHERE rowid=(SELECT MIN(rowid) FROM engines);'
     );
     expect(run).toHaveBeenCalledWith(
       command.name,
@@ -142,10 +142,10 @@ describe('Empty engine repository', () => {
       command.logParameters.loki.level,
       command.logParameters.loki.interval,
       command.logParameters.loki.address,
-      command.logParameters.loki.tokenAddress,
       command.logParameters.loki.username,
       command.logParameters.loki.password,
-      command.logParameters.oia.level
+      command.logParameters.oia.level,
+      command.logParameters.oia.interval
     );
   });
 });
@@ -166,10 +166,10 @@ describe('Non-empty Engine repository', () => {
     lokiLogLevel: 'silent',
     lokiLogInterval: 60,
     lokiLogAddress: '',
-    lokiLogTokenAddress: '',
     lokiLogUsername: '',
     lokiLogPassword: '',
-    oiaLogLevel: 'silent'
+    oiaLogLevel: 'silent',
+    oiaLogInterval: 10
   };
   beforeEach(() => {
     jest.clearAllMocks();
@@ -207,12 +207,12 @@ describe('Non-empty Engine repository', () => {
           level: 'silent',
           interval: 60,
           address: '',
-          tokenAddress: '',
           username: '',
           password: ''
         },
         oia: {
-          level: 'silent'
+          level: 'silent',
+          interval: 10
         }
       }
     };
@@ -222,8 +222,8 @@ describe('Non-empty Engine repository', () => {
         'log_file_max_file_size AS fileLogMaxFileSize, log_file_number_of_files AS fileLogNumberOfFiles, ' +
         'log_database_level AS databaseLogLevel, log_database_max_number_of_logs AS databaseLogMaxNumberOfLogs, ' +
         'log_loki_level AS lokiLogLevel, log_loki_interval AS lokiLogInterval, log_loki_address AS lokiLogAddress, ' +
-        'log_loki_token_address AS lokiLogTokenAddress, log_loki_username AS lokiLogUsername, ' +
-        'log_loki_password AS lokiLogPassword, log_oia_level AS oiaLogLevel FROM engines;'
+        'log_loki_username AS lokiLogUsername, log_loki_password AS lokiLogPassword, ' +
+        'log_oia_level AS oiaLogLevel, log_oia_interval AS oiaLogInterval FROM engines;'
     );
     expect(all).toHaveBeenCalledTimes(2);
     expect(engineSettings).toEqual(expectedValue);
@@ -252,12 +252,12 @@ describe('Non-empty Engine repository', () => {
           level: 'silent',
           interval: 60,
           address: '',
-          tokenAddress: '',
           username: '',
           password: ''
         },
         oia: {
-          level: 'silent'
+          level: 'silent',
+          interval: 10
         }
       }
     };
diff --git a/backend/src/repository/engine.repository.ts b/backend/src/repository/engine.repository.ts
index 8df43c0b37..a114fdd35c 100644
--- a/backend/src/repository/engine.repository.ts
+++ b/backend/src/repository/engine.repository.ts
@@ -26,12 +26,12 @@ const defaultEngineSettings: EngineSettingsCommandDTO = {
       level: 'silent',
       interval: 60,
       address: '',
-      tokenAddress: '',
       username: '',
       password: ''
     },
     oia: {
-      level: 'silent'
+      level: 'silent',
+      interval: 10
     }
   }
 };
@@ -63,10 +63,10 @@ export default class EngineRepository {
       'log_loki_level AS lokiLogLevel, ' +
       'log_loki_interval AS lokiLogInterval, ' +
       'log_loki_address AS lokiLogAddress, ' +
-      'log_loki_token_address AS lokiLogTokenAddress, ' +
       'log_loki_username AS lokiLogUsername, ' +
       'log_loki_password AS lokiLogPassword, ' +
-      'log_oia_level AS oiaLogLevel ' +
+      'log_oia_level AS oiaLogLevel, ' +
+      'log_oia_interval AS oiaLogInterval ' +
       `FROM ${ENGINES_TABLE};`;
     const results: Array<any> = this.database.prepare(query).all();
 
@@ -94,12 +94,12 @@ export default class EngineRepository {
             level: results[0].lokiLogLevel,
             interval: results[0].lokiLogInterval,
             address: results[0].lokiLogAddress,
-            tokenAddress: results[0].lokiLogTokenAddress,
             username: results[0].lokiLogUsername,
             password: results[0].lokiLogPassword
           },
           oia: {
-            level: results[0].oiaLogLevel
+            level: results[0].oiaLogLevel,
+            interval: results[0].oiaLogInterval
           }
         }
       };
@@ -123,10 +123,10 @@ export default class EngineRepository {
       'log_loki_level = ?, ' +
       'log_loki_interval = ?, ' +
       'log_loki_address = ?, ' +
-      'log_loki_token_address = ?, ' +
       'log_loki_username = ?, ' +
       'log_loki_password = ?, ' +
-      'log_oia_level = ? ' +
+      'log_oia_level = ?, ' +
+      'log_oia_interval = ? ' +
       `WHERE rowid=(SELECT MIN(rowid) FROM ${ENGINES_TABLE});`;
 
     this.database
@@ -145,10 +145,10 @@ export default class EngineRepository {
         command.logParameters.loki.level,
         command.logParameters.loki.interval,
         command.logParameters.loki.address,
-        command.logParameters.loki.tokenAddress,
         command.logParameters.loki.username,
         command.logParameters.loki.password,
-        command.logParameters.oia.level
+        command.logParameters.oia.level,
+        command.logParameters.oia.interval
       );
   }
 
@@ -163,8 +163,9 @@ export default class EngineRepository {
     const query =
       `INSERT INTO ${ENGINES_TABLE} (id, name, port, proxy_enabled, proxy_port, log_console_level, ` +
       'log_file_level, log_file_max_file_size, log_file_number_of_files, log_database_level, ' +
-      'log_database_max_number_of_logs, log_loki_level, log_loki_interval, log_loki_address, log_loki_token_address, ' +
-      'log_loki_username, log_loki_password, log_oia_level) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);';
+      'log_database_max_number_of_logs, log_loki_level, log_loki_interval, log_loki_address, ' +
+      'log_loki_username, log_loki_password, log_oia_level, log_oia_interval) ' +
+      'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);';
     this.database
       .prepare(query)
       .run(
@@ -182,10 +183,10 @@ export default class EngineRepository {
         command.logParameters.loki.level,
         command.logParameters.loki.interval,
         command.logParameters.loki.address,
-        command.logParameters.loki.tokenAddress,
         command.logParameters.loki.username,
         command.logParameters.loki.password,
-        command.logParameters.oia.level
+        command.logParameters.oia.level,
+        command.logParameters.oia.interval
       );
   }
 }
diff --git a/backend/src/repository/log.repository.spec.ts b/backend/src/repository/log.repository.spec.ts
index e36729ee8c..03bae4c29c 100644
--- a/backend/src/repository/log.repository.spec.ts
+++ b/backend/src/repository/log.repository.spec.ts
@@ -78,21 +78,35 @@ describe('Log repository', () => {
 
   it('should add logs', () => {
     repository.addLogs([
-      { msg: 'my message 1', scopeType: 'myScopeType', scopeId: 'scopeId', scopeName: 'scope name', time: 0, level: '30' },
-      { msg: 'my message 1', scopeType: 'myScopeType', scopeId: 'scopeId', scopeName: 'scope name', time: 1, level: '10' }
+      {
+        msg: 'my message 1',
+        scopeType: 'myScopeType',
+        scopeId: 'scopeId',
+        scopeName: 'scope name',
+        time: '2020-01-01T00:00:00.000Z',
+        level: '30'
+      },
+      {
+        msg: 'my message 1',
+        scopeType: 'myScopeType',
+        scopeId: 'scopeId',
+        scopeName: 'scope name',
+        time: '2020-01-01T01:00:00.000Z',
+        level: '10'
+      }
     ]);
 
     expect(database.prepare).toHaveBeenCalledWith(
       'INSERT INTO logs (timestamp, level, scope_type, scope_id, scope_name, message) VALUES (?,?,?,?,?,?), (?,?,?,?,?,?);'
     );
     expect(run).toHaveBeenCalledWith(
-      0,
+      '2020-01-01T00:00:00.000Z',
       'info',
       'myScopeType',
       'scopeId',
       'scope name',
       'my message 1',
-      1,
+      '2020-01-01T01:00:00.000Z',
       'trace',
       'myScopeType',
       'scopeId',
diff --git a/backend/src/service/logger/logger.service.spec.ts b/backend/src/service/logger/logger.service.spec.ts
index b208880c96..b9951ab133 100644
--- a/backend/src/service/logger/logger.service.spec.ts
+++ b/backend/src/service/logger/logger.service.spec.ts
@@ -1,6 +1,6 @@
 import path from 'node:path';
 import EncryptionServiceMock from '../../tests/__mocks__/encryption-service.mock';
-import { LogSettings } from '../../../../shared/model/engine.model';
+import { LogSettings, RegistrationSettingsDTO } from '../../../../shared/model/engine.model';
 
 import pino from 'pino';
 
@@ -59,12 +59,12 @@ describe('Logger', () => {
         level: 'debug',
         username: 'user',
         password: 'loki-pass',
-        tokenAddress: 'token-url',
         address: 'loki-url',
         interval: 60
       },
       oia: {
-        level: 'error'
+        level: 'error',
+        interval: 60
       }
     };
     service = new LoggerService(encryptionService, 'folder');
@@ -72,6 +72,17 @@ describe('Logger', () => {
 
   it('should be properly initialized', async () => {
     service.createChildLogger = jest.fn();
+    const registration: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false,
+      proxyUrl: 'http://localhost:9000',
+      proxyUsername: 'username',
+      proxyPassword: 'password',
+      token: 'token',
+      status: 'REGISTERED'
+    } as RegistrationSettingsDTO;
     const expectedTargets = [
       { target: 'pino-pretty', options: { colorize: true, singleLine: true }, level: logSettings.console.level },
       {
@@ -91,21 +102,36 @@ describe('Logger', () => {
         level: logSettings.database.level
       },
       {
-        target: path.join(__dirname, 'loki-transport.js'),
+        target: 'pino-loki',
         options: {
-          username: logSettings.loki.username,
-          password: logSettings.loki.password,
-          tokenAddress: logSettings.loki.tokenAddress,
-          address: logSettings.loki.address,
-          id: oibusId,
-          name: oibusName,
-          interval: logSettings.loki.interval
+          batching: true,
+          interval: logSettings.loki.interval,
+          host: logSettings.loki.address,
+          basicAuth: {
+            username: logSettings.loki.username,
+            password: logSettings.loki.password
+          },
+          labels: { name: oibusName }
         },
         level: logSettings.loki.level
+      },
+      {
+        target: path.join(__dirname, 'oianalytics-transport.js'),
+        options: {
+          interval: logSettings.oia.interval,
+          host: registration.host,
+          token: registration.token,
+          useProxy: registration.useProxy,
+          proxyUrl: registration.proxyUrl,
+          proxyUsername: registration.proxyUsername,
+          proxyPassword: registration.proxyPassword,
+          acceptUnauthorized: registration.acceptUnauthorized
+        },
+        level: logSettings.oia.level
       }
     ];
 
-    await service.start(oibusId, oibusName, logSettings);
+    await service.start(oibusId, oibusName, logSettings, registration);
 
     expect(pino).toHaveBeenCalledTimes(1);
     expect(pino).toHaveBeenCalledWith({
@@ -117,22 +143,39 @@ describe('Logger', () => {
   });
 
   it('should be properly initialized with loki error and standard file names', async () => {
+    const registration: RegistrationSettingsDTO = {
+      id: 'id',
+      status: 'REGISTERED',
+      token: 'token'
+    } as RegistrationSettingsDTO;
     service.createChildLogger = jest.fn();
 
-    jest.spyOn(console, 'error').mockImplementationOnce(() => {});
+    jest.spyOn(console, 'error').mockImplementation(() => {});
 
     (encryptionService.decryptText as jest.Mock).mockImplementation(() => {
       throw new Error('decrypt-error');
     });
 
     logSettings.database.maxNumberOfLogs = 0;
-    await service.start(oibusId, oibusName, logSettings);
+    await service.start(oibusId, oibusName, logSettings, registration);
 
-    expect(console.error).toHaveBeenCalledTimes(1);
+    expect(console.error).toHaveBeenCalledTimes(2);
     expect(console.error).toHaveBeenCalledWith(new Error('decrypt-error'));
   });
 
-  it('should be properly initialized without loki password and without sqliteLog', async () => {
+  it('should be properly initialized without loki password, without oia token and without sqliteLog', async () => {
+    const registration: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: true,
+      proxyUrl: 'http://localhost:9000',
+      proxyUsername: 'username',
+      proxyPassword: '',
+      token: '',
+      status: 'REGISTERED'
+    } as RegistrationSettingsDTO;
+
     jest.spyOn(console, 'error').mockImplementationOnce(() => {});
     service.createChildLogger = jest.fn();
 
@@ -149,21 +192,36 @@ describe('Logger', () => {
         level: logSettings.file.level
       },
       {
-        target: path.join(__dirname, 'loki-transport.js'),
+        target: 'pino-loki',
         options: {
-          username: logSettings.loki.username,
-          password: logSettings.loki.password,
-          tokenAddress: logSettings.loki.tokenAddress,
-          address: logSettings.loki.address,
-          id: oibusId,
-          name: oibusName,
-          interval: logSettings.loki.interval
+          batching: true,
+          interval: logSettings.loki.interval,
+          host: logSettings.loki.address,
+          basicAuth: {
+            username: logSettings.loki.username,
+            password: logSettings.loki.password
+          },
+          labels: { name: oibusName }
         },
         level: logSettings.loki.level
+      },
+      {
+        target: path.join(__dirname, 'oianalytics-transport.js'),
+        options: {
+          interval: logSettings.oia.interval,
+          host: registration.host,
+          token: registration.token,
+          useProxy: registration.useProxy,
+          proxyUrl: registration.proxyUrl,
+          proxyUsername: registration.proxyUsername,
+          proxyPassword: registration.proxyPassword,
+          acceptUnauthorized: registration.acceptUnauthorized
+        },
+        level: logSettings.oia.level
       }
     ];
 
-    await service.start(oibusId, oibusName, logSettings);
+    await service.start(oibusId, oibusName, logSettings, registration);
 
     expect(pino).toHaveBeenCalledTimes(1);
     expect(pino).toHaveBeenCalledWith({
@@ -174,7 +232,11 @@ describe('Logger', () => {
     });
   });
 
-  it('should be properly initialized without lokiLog nor sqliteLog', async () => {
+  it('should be properly initialized without lokiLog, nor oianalytics nor sqliteLog', async () => {
+    const registration: RegistrationSettingsDTO = {
+      status: 'NOT_REGISTERED'
+    } as RegistrationSettingsDTO;
+
     jest.spyOn(console, 'error').mockImplementationOnce(() => {});
     service.createChildLogger = jest.fn();
 
@@ -192,7 +254,7 @@ describe('Logger', () => {
       }
     ];
 
-    await service.start(oibusId, oibusName, logSettings);
+    await service.start(oibusId, oibusName, logSettings, registration);
 
     expect(pino).toHaveBeenCalledTimes(1);
     expect(pino).toHaveBeenCalledWith({
diff --git a/backend/src/service/logger/logger.service.ts b/backend/src/service/logger/logger.service.ts
index f5cd073507..9a31c280bf 100644
--- a/backend/src/service/logger/logger.service.ts
+++ b/backend/src/service/logger/logger.service.ts
@@ -2,7 +2,7 @@ import path from 'node:path';
 
 import pino from 'pino';
 
-import { LogSettings, ScopeType } from '../../../../shared/model/engine.model';
+import { LogSettings, RegistrationSettingsDTO, ScopeType } from '../../../../shared/model/engine.model';
 
 import FileCleanupService from './file-cleanup.service';
 import EncryptionService from '../encryption.service';
@@ -31,7 +31,7 @@ class LoggerService {
   /**
    * Run the appropriate pino log transports according to the configuration
    */
-  async start(oibusId: string, oibusName: string, logParameters: LogSettings): Promise<void> {
+  async start(oibusId: string, oibusName: string, logParameters: LogSettings, registration: RegistrationSettingsDTO | null): Promise<void> {
     const targets = [];
     targets.push({ target: 'pino-pretty', options: { colorize: true, singleLine: true }, level: logParameters.console.level });
 
@@ -58,15 +58,16 @@ class LoggerService {
     if (logParameters.loki.address) {
       try {
         targets.push({
-          target: path.join(__dirname, './loki-transport.js'),
+          target: 'pino-loki',
           options: {
-            username: logParameters.loki.username,
-            password: logParameters.loki.password ? await this.encryptionService.decryptText(logParameters.loki.password) : '',
-            address: logParameters.loki.address,
-            tokenAddress: logParameters.loki.tokenAddress,
-            id: oibusId,
-            name: oibusName,
-            interval: logParameters.loki.interval
+            batching: true,
+            interval: logParameters.loki.interval,
+            host: logParameters.loki.address,
+            basicAuth: {
+              username: logParameters.loki.username,
+              password: logParameters.loki.password ? await this.encryptionService.decryptText(logParameters.loki.password) : ''
+            },
+            labels: { name: oibusName }
           },
           level: logParameters.loki.level
         });
@@ -77,6 +78,29 @@ class LoggerService {
       }
     }
 
+    if (registration && registration.status === 'REGISTERED') {
+      try {
+        targets.push({
+          target: path.join(__dirname, './oianalytics-transport.js'),
+          options: {
+            interval: logParameters.oia.interval,
+            host: registration.host,
+            token: registration.token ? await this.encryptionService.decryptText(registration.token) : '',
+            useProxy: registration.useProxy,
+            proxyUrl: registration.proxyUrl,
+            proxyUsername: registration.proxyUsername,
+            proxyPassword: registration.proxyPassword ? await this.encryptionService.decryptText(registration.proxyPassword) : '',
+            acceptUnauthorized: registration.acceptUnauthorized
+          },
+          level: logParameters.oia.level
+        });
+      } catch (error) {
+        // In case of bad decryption, an error is triggered, so instead of leaving the process, the error will just be
+        // logged in the console and loki won't be activated
+        console.error(error);
+      }
+    }
+
     this.logger = pino({
       base: undefined,
       level: 'trace', // default to trace since each transport has its defined level
diff --git a/backend/src/service/logger/loki-transport.ts b/backend/src/service/logger/loki-transport.ts
deleted file mode 100644
index 80b4199c09..0000000000
--- a/backend/src/service/logger/loki-transport.ts
+++ /dev/null
@@ -1,220 +0,0 @@
-import fetch from 'node-fetch';
-import build from 'pino-abstract-transport';
-
-import { LogStreamValuesCommandDTO, PinoLog } from '../../../../shared/model/logs.model';
-import { LogLevel } from '../../../../shared/model/engine.model';
-
-const MAX_BATCH_LOG = 500;
-const MAX_BATCH_INTERVAL_S = 60;
-const LEVEL_FORMAT: { [key: string]: LogLevel } = {
-  '10': 'trace',
-  '20': 'debug',
-  '30': 'info',
-  '40': 'warn',
-  '50': 'error',
-  '60': 'fatal'
-};
-
-interface LokiOptions {
-  username?: string;
-  password?: string;
-  tokenAddress?: string;
-  address: string;
-  id: string;
-  oibusName: string;
-  interval?: number;
-  batchLimit?: number;
-}
-
-interface AccessToken {
-  access_token: string;
-}
-
-/**
- * Class to support logging to a remote loki instance as a custom Pino Transport module
- */
-class LokiTransport {
-  private readonly options: LokiOptions;
-  private token: AccessToken | null = null;
-  private mustRenewToken = true;
-  private mustRenewTokenTimeout: NodeJS.Timeout | null = null;
-  private sendLokiLogsInterval: NodeJS.Timeout | null = null;
-  private batchLogs: { [key: string]: Array<[string, string]> };
-  private numberOfLogs = 0;
-
-  constructor(options: LokiOptions) {
-    this.options = options;
-    this.batchLogs = {
-      '60': [],
-      '50': [],
-      '40': [],
-      '30': [],
-      '20': [],
-      '10': []
-    };
-
-    this.sendLokiLogsInterval = setInterval(
-      async () => {
-        await this.sendLokiLogs();
-      },
-      (this.options.interval || MAX_BATCH_INTERVAL_S) * 1000
-    );
-  }
-
-  /**
-   * Method used to send the log to the remote loki instance
-   */
-  sendLokiLogs = async (): Promise<void> => {
-    const streams: Array<LogStreamValuesCommandDTO> = [];
-    Object.entries(this.batchLogs).forEach(([logLevel, logMessages]) => {
-      if (logMessages.length > 0) {
-        logMessages.forEach(logMessage => {
-          const jsonMessage = JSON.parse(logMessage[1]);
-          streams.push({
-            stream: {
-              oibus: this.options.id,
-              oibusName: this.options.oibusName,
-              level: LEVEL_FORMAT[logLevel],
-              scopeType: jsonMessage.scopeType,
-              scopeId: jsonMessage.scopeId,
-              scopeName: jsonMessage.scopeName
-            },
-            values: [[logMessage[0], jsonMessage.message]]
-          });
-        });
-      }
-    });
-    if (streams.length === 0) {
-      return;
-    }
-    const dataBuffer = JSON.stringify({ streams });
-    this.batchLogs = {
-      '60': [],
-      '50': [],
-      '40': [],
-      '30': [],
-      '20': [],
-      '10': []
-    };
-    this.numberOfLogs = 0;
-
-    const fetchOptions = {
-      method: 'POST',
-      headers: { 'Content-Type': 'application/json', Authorization: '' },
-      body: dataBuffer
-    };
-
-    if (this.options.tokenAddress) {
-      if (this.mustRenewToken || !this.token) {
-        await this.updateLokiToken();
-      }
-      if (!this.token) {
-        return;
-      }
-      fetchOptions.headers.Authorization = `Bearer ${this.token.access_token}`;
-    } else if (this.options.username && this.options.password) {
-      const basicAuth = Buffer.from(`${this.options.username}:${this.options.password}`).toString('base64');
-      fetchOptions.headers.Authorization = `Basic ${basicAuth}`;
-    }
-    try {
-      const result = await fetch(this.options.address, fetchOptions);
-      if (result.status !== 200 && result.status !== 201 && result.status !== 204) {
-        console.error(`Loki fetch error: ${result.status} - ${result.statusText} with payload ${dataBuffer}`);
-      }
-    } catch (error) {
-      console.error(error);
-    }
-  };
-
-  /**
-   * Method used to update the token if needed
-   */
-  updateLokiToken = async (): Promise<void> => {
-    if (!this.options.tokenAddress || !this.options.username || !this.options.password) {
-      return;
-    }
-    try {
-      const basic = Buffer.from(`${this.options.username}:${this.options.password}`).toString('base64');
-      const fetchOptions = {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/x-www-form-urlencoded',
-          Authorization: `Basic ${basic}`
-        },
-        timeout: 10000
-      };
-      const response = await fetch(this.options.tokenAddress, fetchOptions);
-      const responseData = (await response.json()) as { expires_in: number; access_token: string };
-
-      if (this.mustRenewTokenTimeout) {
-        clearTimeout(this.mustRenewTokenTimeout);
-      }
-      this.mustRenewTokenTimeout = setTimeout(
-        () => {
-          this.mustRenewToken = true;
-        },
-        (responseData.expires_in - 60) * 1000
-      );
-      this.token = responseData;
-      this.mustRenewToken = false;
-    } catch (error) {
-      console.error(error);
-    }
-  };
-
-  /**
-   * Store the log in the batch log array and send them immediately if the array is full
-   */
-  addLokiLogs = async (log: PinoLog): Promise<void> => {
-    this.batchLogs[log.level].push([
-      (new Date(log.time).getTime() * 1000000).toString(),
-      JSON.stringify({ message: log.msg, scopeType: log.scopeType, scopeId: log.scopeId, scopeName: log.scopeName })
-    ]);
-    this.numberOfLogs += 1;
-    const batchLimit = this.options.batchLimit || MAX_BATCH_LOG;
-    if (this.numberOfLogs >= batchLimit) {
-      if (this.sendLokiLogsInterval) {
-        clearInterval(this.sendLokiLogsInterval);
-      }
-      await this.sendLokiLogs();
-
-      this.sendLokiLogsInterval = setInterval(
-        async () => {
-          await this.sendLokiLogs();
-        },
-        (this.options.interval || MAX_BATCH_INTERVAL_S) * 1000
-      );
-    }
-  };
-
-  /**
-   * Clear timeout and interval and send last logs before closing the transport
-   */
-  end = async (): Promise<void> => {
-    if (this.sendLokiLogsInterval) {
-      clearInterval(this.sendLokiLogsInterval);
-    }
-    await this.sendLokiLogs();
-    if (this.mustRenewTokenTimeout) {
-      clearTimeout(this.mustRenewTokenTimeout);
-    }
-  };
-}
-
-const createTransport = async (opts: LokiOptions) => {
-  const lokiTransport = new LokiTransport(opts);
-  return build(
-    async source => {
-      for await (const log of source) {
-        await lokiTransport.addLokiLogs(log);
-      }
-    },
-    {
-      close: async () => {
-        await lokiTransport.end();
-      }
-    }
-  );
-};
-
-export default createTransport;
diff --git a/backend/src/service/logger/oianalytics-transport.ts b/backend/src/service/logger/oianalytics-transport.ts
new file mode 100644
index 0000000000..ba74d5f78f
--- /dev/null
+++ b/backend/src/service/logger/oianalytics-transport.ts
@@ -0,0 +1,156 @@
+import build from 'pino-abstract-transport';
+
+import { LogDTO, PinoLog } from '../../../../shared/model/logs.model';
+import { LogLevel, ScopeType } from '../../../../shared/model/engine.model';
+import { createProxyAgent } from '../proxy-agent';
+import fetch, { HeadersInit } from 'node-fetch';
+
+const MAX_BATCH_LOG = 500;
+const MAX_BATCH_INTERVAL_S = 60;
+const LEVEL_FORMAT: { [key: string]: LogLevel } = {
+  '10': 'TRACE',
+  '20': 'DEBUG',
+  '30': 'INFO',
+  '40': 'WARN',
+  '50': 'ERROR'
+};
+
+const SCOPE_TYPE_FORMAT: { [key: ScopeType]: LogLevel } = {
+  south: 'SOUTH',
+  north: 'NORTH',
+  'history-query': 'HISTORY_QUERY',
+  internal: 'INTERNAL',
+  'web-server': 'WEB_SERVER'
+};
+
+interface OIAnalyticsOptions {
+  interval: number;
+  host: string;
+  token: string;
+  useProxy: boolean;
+  proxyUrl?: string;
+  proxyUsername?: string | null;
+  proxyPassword?: string | null;
+  acceptUnauthorized: boolean;
+  batchLimit?: number;
+}
+
+/**
+ * Class to support logging to OIAnalytics
+ */
+class OianalyticsTransport {
+  private readonly options: OIAnalyticsOptions;
+  private sendOIALogsInterval: NodeJS.Timeout | null = null;
+  private batchLogs: Array<LogDTO> = [];
+
+  constructor(options: OIAnalyticsOptions) {
+    this.options = options;
+    if (this.options.host.endsWith('/')) {
+      this.options.host = this.options.host.slice(0, this.options.host.length - 1);
+    }
+
+    const batchInterval = this.options.interval > MAX_BATCH_INTERVAL_S ? MAX_BATCH_INTERVAL_S : this.options.interval;
+    this.sendOIALogsInterval = setInterval(
+      async () => {
+        await this.sendOIALogs();
+      },
+      (batchInterval || MAX_BATCH_INTERVAL_S) * 1000
+    );
+  }
+
+  /**
+   * Method used to send the log to OIAnalytics
+   */
+  sendOIALogs = async (): Promise<void> => {
+    const headers: HeadersInit = {};
+    headers.authorization = `Bearer ${this.options.token}`;
+    headers['Content-Type'] = 'application/json';
+    const endpoint = '/api/oianalytics/oibus/logs';
+    const agent = createProxyAgent(
+      this.options.useProxy,
+      `${this.options.host}${endpoint}`,
+      this.options.useProxy
+        ? {
+            url: this.options.proxyUrl!,
+            username: this.options.proxyUsername || null,
+            password: this.options.proxyPassword || null
+          }
+        : null,
+      this.options.acceptUnauthorized
+    );
+
+    const dataBuffer = JSON.stringify(this.batchLogs);
+    this.batchLogs = [];
+    const fetchOptions = {
+      method: 'POST',
+      headers,
+      body: dataBuffer,
+      agent
+    };
+
+    const logUrl = `${this.options.host}${endpoint}`;
+    try {
+      const response = await fetch(logUrl, fetchOptions);
+      if (response.status !== 200 && response.status !== 201 && response.status !== 204) {
+        console.error(`OIAnalytics fetch error on ${logUrl}: ${response.status} - ${response.statusText} with payload ${dataBuffer}`);
+      }
+    } catch (error) {
+      console.error(`Error when sending logs to ${logUrl}. ${error}`);
+    }
+  };
+
+  /**
+   * Store the log in the batch log array and send them immediately if the array is full
+   */
+  addLogs = async (log: PinoLog): Promise<void> => {
+    this.batchLogs.push({
+      timestamp: log.time,
+      level: LEVEL_FORMAT[log.level],
+      scopeType: SCOPE_TYPE_FORMAT[log.scopeType],
+      scopeId: log.scopeId || undefined,
+      scopeName: log.scopeName || undefined,
+      message: log.msg
+    });
+    const batchLimit = this.options.batchLimit || MAX_BATCH_LOG;
+    if (this.batchLogs.length >= batchLimit) {
+      if (this.sendOIALogsInterval) {
+        clearInterval(this.sendOIALogsInterval);
+        this.sendOIALogsInterval = null;
+      }
+      await this.sendOIALogs();
+
+      const batchInterval = this.options.interval > MAX_BATCH_INTERVAL_S ? MAX_BATCH_INTERVAL_S : this.options.interval;
+      this.sendOIALogsInterval = setInterval(async () => {
+        await this.sendOIALogs();
+      }, batchInterval * 1000);
+    }
+  };
+
+  /**
+   * Clear timeout and interval and send last logs before closing the transport
+   */
+  end = async (): Promise<void> => {
+    if (this.sendOIALogsInterval) {
+      clearInterval(this.sendOIALogsInterval);
+    }
+    await this.sendOIALogs();
+  };
+}
+
+const createTransport = async (opts: OIAnalyticsOptions) => {
+  const oianalyticsTransport = new OianalyticsTransport(opts);
+  return build(
+    async source => {
+      for await (const log of source) {
+        await oianalyticsTransport.addLogs(log);
+      }
+    },
+    {
+      close: async () => {
+        await oianalyticsTransport.end();
+      }
+    }
+  );
+};
+
+export default createTransport;
diff --git a/backend/src/service/command.service.spec.ts b/backend/src/service/oia/command.service.spec.ts
similarity index 88%
rename from backend/src/service/command.service.spec.ts
rename to backend/src/service/oia/command.service.spec.ts
index c8e0315967..f546643298 100644
--- a/backend/src/service/command.service.spec.ts
+++ b/backend/src/service/oia/command.service.spec.ts
@@ -1,19 +1,19 @@
-import RepositoryService from './repository.service';
-import RepositoryServiceMock from '../tests/__mocks__/repository-service.mock';
-import EncryptionServiceMock from '../tests/__mocks__/encryption-service.mock';
-import EncryptionService from './encryption.service';
+import RepositoryService from '../repository.service';
+import RepositoryServiceMock from '../../tests/__mocks__/repository-service.mock';
+import EncryptionServiceMock from '../../tests/__mocks__/encryption-service.mock';
+import EncryptionService from '../encryption.service';
 import pino from 'pino';
-import PinoLogger from '../tests/__mocks__/logger.mock';
-import { OIBusCommandDTO } from '../../../shared/model/command.model';
+import PinoLogger from '../../tests/__mocks__/logger.mock';
+import { OIBusCommandDTO } from '../../../../shared/model/command.model';
 import CommandService from './command.service';
-import { downloadFile, getNetworkSettingsFromRegistration, getOIBusInfo, unzip } from './utils';
-import { RegistrationSettingsDTO } from '../../../shared/model/engine.model';
+import { downloadFile, getNetworkSettingsFromRegistration, getOIBusInfo, unzip } from '../utils';
+import { RegistrationSettingsDTO } from '../../../../shared/model/engine.model';
 import fs from 'node:fs/promises';
 import path from 'node:path';
 
 jest.mock('node:fs/promises');
 jest.mock('node-fetch');
-jest.mock('./utils');
+jest.mock('../utils');
 
 // @ts-ignore
 jest.spyOn(process, 'exit').mockImplementation(() => {});
@@ -23,6 +23,7 @@ const encryptionService: EncryptionService = new EncryptionServiceMock('', '');
 
 const nowDateString = '2020-02-02T02:02:02.222Z';
 const logger: pino.Logger = new PinoLogger();
+const anotherLogger: pino.Logger = new PinoLogger();
 
 const command: OIBusCommandDTO = {
   id: 'id1',
@@ -58,7 +59,7 @@ describe('Command service with running command', () => {
 
     (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(registration);
     (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
-    service = new CommandService('oibusId', repositoryService, encryptionService, logger, 'binaryFolder');
+    service = new CommandService(repositoryService, encryptionService, logger, 'binaryFolder');
   });
 
   it('should properly start and stop', async () => {
@@ -127,7 +128,7 @@ describe('Command service without command', () => {
     jest.clearAllMocks();
 
     (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([]);
-    service = new CommandService('oibusId', repositoryService, encryptionService, logger, 'binaryFolder');
+    service = new CommandService(repositoryService, encryptionService, logger, 'binaryFolder');
   });
 
   it('should properly start when not registered', () => {
@@ -202,4 +203,8 @@ describe('Command service without command', () => {
     expect(unzip).toHaveBeenCalledWith(expectedFilename, path.resolve('binaryFolder', '..', 'update'));
     expect(fs.unlink).toHaveBeenCalledWith(expectedFilename);
   });
+
+  it('should change logger', () => {
+    service.setLogger(anotherLogger);
+  });
 });
diff --git a/backend/src/service/command.service.ts b/backend/src/service/oia/command.service.ts
similarity index 91%
rename from backend/src/service/command.service.ts
rename to backend/src/service/oia/command.service.ts
index a53f62c576..28b3515601 100644
--- a/backend/src/service/command.service.ts
+++ b/backend/src/service/oia/command.service.ts
@@ -1,13 +1,13 @@
 import fs from 'node:fs/promises';
-import { delay, downloadFile, getNetworkSettingsFromRegistration, getOIBusInfo, unzip } from './utils';
-import RepositoryService from './repository.service';
-import EncryptionService from './encryption.service';
+import { delay, downloadFile, getNetworkSettingsFromRegistration, getOIBusInfo, unzip } from '../utils';
+import RepositoryService from '../repository.service';
+import EncryptionService from '../encryption.service';
 import pino from 'pino';
-import { OIBusCommandDTO } from '../../../shared/model/command.model';
+import { OIBusCommandDTO } from '../../../../shared/model/command.model';
 import { EventEmitter } from 'node:events';
-import DeferredPromise from './deferred-promise';
+import DeferredPromise from '../deferred-promise';
 import { DateTime } from 'luxon';
-import { RegistrationSettingsDTO } from '../../../shared/model/engine.model';
+import { RegistrationSettingsDTO } from '../../../../shared/model/engine.model';
 import path from 'node:path';
 
 const DOWNLOAD_TIMEOUT = 600_000;
@@ -19,7 +19,6 @@ export default class CommandService {
   private runProgress$: DeferredPromise | null = null;
 
   constructor(
-    private oibusId: string,
     private repositoryService: RepositoryService,
     private encryptionService: EncryptionService,
     private logger: pino.Logger,
@@ -136,4 +135,8 @@ export default class CommandService {
     }
     this.logger.debug(`Command service stopped`);
   }
+
+  setLogger(logger: pino.Logger) {
+    this.logger = logger;
+  }
 }
diff --git a/backend/src/service/oia/registration.service.spec.ts b/backend/src/service/oia/registration.service.spec.ts
new file mode 100644
index 0000000000..8212b0c637
--- /dev/null
+++ b/backend/src/service/oia/registration.service.spec.ts
@@ -0,0 +1,865 @@
+import fetch from 'node-fetch';
+import RepositoryService from '../repository.service';
+import RepositoryServiceMock from '../../tests/__mocks__/repository-service.mock';
+import { EngineSettingsDTO, RegistrationSettingsCommandDTO, RegistrationSettingsDTO } from '../../../../shared/model/engine.model';
+import EncryptionServiceMock from '../../tests/__mocks__/encryption-service.mock';
+import EncryptionService from '../encryption.service';
+import pino from 'pino';
+import PinoLogger from '../../tests/__mocks__/logger.mock';
+import { createProxyAgent } from '../proxy-agent';
+import { OIBusCommandDTO } from '../../../../shared/model/command.model';
+import { generateRandomId, getNetworkSettingsFromRegistration, getOIBusInfo } from '../utils';
+import CommandService from './command.service';
+import CommandServiceMock from '../../tests/__mocks__/command-service.mock';
+import RegistrationService from './registration.service';
+import ReloadServiceMock from '../../tests/__mocks__/reload-service.mock';
+import ReloadService from '../reload.service';
+
+jest.mock('node:fs/promises');
+jest.mock('node-fetch');
+const { Response } = jest.requireActual('node-fetch');
+jest.mock('../utils');
+jest.mock('../proxy-agent');
+
+const repositoryService: RepositoryService = new RepositoryServiceMock('', '');
+const encryptionService: EncryptionService = new EncryptionServiceMock('', '');
+const commandService: CommandService = new CommandServiceMock();
+const reloadService: ReloadService = new ReloadServiceMock();
+
+const nowDateString = '2020-02-02T02:02:02.222Z';
+const logger: pino.Logger = new PinoLogger();
+const flushPromises = () => new Promise(jest.requireActual('timers').setImmediate);
+
+const command: OIBusCommandDTO = {
+  id: 'id1',
+  type: 'UPGRADE',
+  status: 'COMPLETED',
+  ack: true,
+  creationDate: '2023-01-01T12:00:00Z',
+  completedDate: '2023-01-01T12:00:00Z',
+  result: 'ok',
+  version: '3.2.0',
+  assetId: 'assetId'
+};
+
+const fakeEngineSettings: EngineSettingsDTO = {
+  id: 'id1',
+  name: 'MyOIBus',
+  logParameters: {
+    oia: {
+      level: 'silent'
+    }
+  }
+} as EngineSettingsDTO;
+
+let service: RegistrationService;
+describe('Registration service', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+    jest.useFakeTimers().setSystemTime(new Date(nowDateString));
+    (createProxyAgent as jest.Mock).mockReturnValue(undefined);
+
+    service = new RegistrationService(repositoryService, encryptionService, commandService, reloadService, logger);
+  });
+
+  it('should get NOT_REGISTERED registration settings', () => {
+    const mockResult: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false,
+      token: 'token',
+      activationCode: '1234',
+      status: 'NOT_REGISTERED',
+      activationDate: '2020-20-20T00:00:00.000Z',
+      activationExpirationDate: ''
+    };
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    service.start();
+    const result = service.getRegistrationSettings();
+    expect(result).toEqual(mockResult);
+    expect(repositoryService.registrationRepository.getRegistrationSettings).toHaveBeenCalledTimes(2);
+  });
+
+  it('should update registration', async () => {
+    (generateRandomId as jest.Mock).mockReturnValue('1234');
+
+    const command: RegistrationSettingsCommandDTO = {
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false
+    };
+    (getOIBusInfo as jest.Mock).mockReturnValueOnce({ version: 'v3.2.0' });
+    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce(fakeEngineSettings);
+    const fetchResponse = {
+      redirectUrl: 'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
+      expirationDate: '2020-02-02T02:12:02.222Z'
+    };
+
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+
+    await service.updateRegistrationSettings(command);
+    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledTimes(1);
+    expect(generateRandomId).toHaveBeenCalledWith(6);
+    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledWith(
+      command,
+      '1234',
+      'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
+      '2020-02-02T02:12:02.222Z'
+    );
+    expect(reloadService.restartLogger).not.toHaveBeenCalled();
+  });
+
+  it('should update registration with proxy', async () => {
+    (generateRandomId as jest.Mock).mockReturnValue('1234');
+
+    const command: RegistrationSettingsCommandDTO = {
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: true,
+      proxyUrl: 'http://localhost:3128',
+      proxyUsername: 'user',
+      proxyPassword: 'pass'
+    };
+    (getOIBusInfo as jest.Mock).mockReturnValueOnce({ version: 'v3.2.0' });
+    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce(fakeEngineSettings);
+    const fetchResponse = {
+      redirectUrl: 'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
+      expirationDate: '2020-02-02T02:12:02.222Z'
+    };
+
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+
+    await service.updateRegistrationSettings(command);
+    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledTimes(1);
+    expect(generateRandomId).toHaveBeenCalledWith(6);
+    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledWith(
+      command,
+      '1234',
+      'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
+      '2020-02-02T02:12:02.222Z'
+    );
+  });
+
+  it('should update registration with proxy and without password', async () => {
+    (generateRandomId as jest.Mock).mockReturnValue('1234');
+
+    const command: RegistrationSettingsCommandDTO = {
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: true,
+      proxyUrl: 'http://localhost:3128',
+      proxyUsername: '',
+      proxyPassword: ''
+    };
+    (getOIBusInfo as jest.Mock).mockReturnValueOnce({ version: 'v3.2.0' });
+    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce(fakeEngineSettings);
+    const fetchResponse = {
+      redirectUrl: 'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
+      expirationDate: '2020-02-02T02:12:02.222Z'
+    };
+
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+
+    await service.updateRegistrationSettings(command);
+    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledTimes(1);
+    expect(generateRandomId).toHaveBeenCalledWith(6);
+    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledWith(
+      command,
+      '1234',
+      'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
+      '2020-02-02T02:12:02.222Z'
+    );
+  });
+
+  it('should handle fetch error during registration update', async () => {
+    (generateRandomId as jest.Mock).mockReturnValue('1234');
+
+    const command: RegistrationSettingsCommandDTO = {
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false
+    };
+    (getOIBusInfo as jest.Mock).mockReturnValueOnce({ version: 'v3.2.0' });
+    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce(fakeEngineSettings);
+    (fetch as unknown as jest.Mock).mockImplementation(() => {
+      throw new Error('error');
+    });
+
+    let error;
+    try {
+      await service.updateRegistrationSettings(command);
+    } catch (e) {
+      error = e;
+    }
+    expect(error).toEqual(new Error('Registration failed: Error: error'));
+  });
+
+  it('should handle fetch bad response during registration update', async () => {
+    (generateRandomId as jest.Mock).mockReturnValue('1234');
+
+    const command: RegistrationSettingsCommandDTO = {
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false
+    };
+    (getOIBusInfo as jest.Mock).mockReturnValueOnce({ version: 'v3.2.0' });
+    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce(fakeEngineSettings);
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response('invalid', { status: 404 })));
+
+    let error;
+    try {
+      await service.updateRegistrationSettings(command);
+    } catch (e) {
+      error = e;
+    }
+    expect(error).toEqual(new Error(`Registration failed with status code 404 and message: Not Found`));
+  });
+
+  it('should handle error if registration not found', async () => {
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValueOnce(null);
+    (generateRandomId as jest.Mock).mockReturnValue('1234');
+
+    const command: RegistrationSettingsCommandDTO = {
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false
+    };
+
+    let error;
+    try {
+      await service.updateRegistrationSettings(command);
+    } catch (e) {
+      error = e;
+    }
+    expect(error).toEqual(new Error('Registration settings not found'));
+  });
+
+  it('should activate registration', async () => {
+    await service.activateRegistration('2020-20-20T00:00:00.000Z', 'token');
+    expect(repositoryService.registrationRepository.activateRegistration).toHaveBeenCalledWith('2020-20-20T00:00:00.000Z', 'token');
+  });
+
+  it('should unregister', async () => {
+    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce(fakeEngineSettings);
+
+    await service.onUnregister();
+    expect(repositoryService.registrationRepository.unregister).toHaveBeenCalledTimes(1);
+  });
+
+  it('should check registration', async () => {
+    const mockResult: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false,
+      token: 'token',
+      activationCode: '1234',
+      checkUrl: '/check/url',
+      status: 'PENDING',
+      activationDate: '2020-20-20T00:00:00.000Z',
+      activationExpirationDate: ''
+    };
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    const fetchResponse = { status: 'COMPLETED', expired: true, accessToken: 'access_token' };
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+    service.activateRegistration = jest.fn();
+
+    await service.checkRegistration();
+    expect(fetch).toHaveBeenCalledWith(`${mockResult.host}${mockResult.checkUrl}`, { method: 'GET', timeout: 10000 });
+    expect(service.activateRegistration).toHaveBeenCalledWith('2020-02-02T02:02:02.222Z', 'access_token');
+    expect(reloadService.restartLogger).not.toHaveBeenCalled();
+  });
+
+  it('should check registration and return because already checking', async () => {
+    const mockResult: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false,
+      token: 'token',
+      activationCode: '1234',
+      checkUrl: '/check/url',
+      status: 'PENDING',
+      activationDate: '2020-20-20T00:00:00.000Z',
+      activationExpirationDate: ''
+    };
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValue(fakeEngineSettings);
+    const fetchResponse = { status: 'COMPLETED', expired: true, accessToken: 'access_token' };
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+    service.activateRegistration = jest.fn();
+
+    service.checkRegistration();
+    await service.checkRegistration();
+    expect(logger.trace).toHaveBeenCalledWith('On going registration check');
+    await flushPromises();
+  });
+
+  it('should check registration but fail because of return status', async () => {
+    const mockResult: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false,
+      token: 'token',
+      activationCode: '1234',
+      checkUrl: '/check/url',
+      status: 'PENDING',
+      activationDate: '2020-20-20T00:00:00.000Z',
+      activationExpirationDate: ''
+    };
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    const fetchResponse = { status: 'DECLINED', expired: true, accessToken: 'access_token' };
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+    service.activateRegistration = jest.fn();
+
+    await service.checkRegistration();
+    expect(fetch).toHaveBeenCalledWith(`${mockResult.host}${mockResult.checkUrl}`, { method: 'GET', timeout: 10000 });
+    expect(service.activateRegistration).not.toHaveBeenCalled();
+    expect(logger.warn).toHaveBeenCalledWith(`Registration not completed. Status: DECLINED`);
+    await service.checkRegistration();
+  });
+
+  it('should check registration but fail because of fetch response', async () => {
+    const mockResult: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false,
+      token: 'token',
+      activationCode: '1234',
+      checkUrl: '/check/url',
+      status: 'PENDING',
+      activationDate: '2020-20-20T00:00:00.000Z',
+      activationExpirationDate: ''
+    };
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response('invalid', { status: 404 })));
+    await service.checkRegistration();
+    expect(fetch).toHaveBeenCalledWith(`${mockResult.host}${mockResult.checkUrl}`, { method: 'GET', timeout: 10000 });
+    expect(logger.error).toHaveBeenCalledWith(
+      `Error 404 while checking registration status on ${mockResult.host}${mockResult.checkUrl}: Not Found`
+    );
+  });
+
+  it('should check registration and fail when registration check url not set', async () => {
+    const mockResult: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false,
+      token: 'token',
+      activationCode: '1234',
+      status: 'PENDING',
+      activationDate: '2020-20-20T00:00:00.000Z',
+      activationExpirationDate: ''
+    };
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    await service.checkRegistration();
+    expect(logger.error).toHaveBeenCalledWith('Error while checking registration status: Could not retrieve check URL');
+  });
+
+  it('should check registration and fail on fetch error', async () => {
+    const mockResult: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false,
+      token: 'token',
+      activationCode: '1234',
+      checkUrl: 'check/url',
+      status: 'PENDING',
+      activationDate: '2020-20-20T00:00:00.000Z',
+      activationExpirationDate: ''
+    };
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    (fetch as unknown as jest.Mock).mockImplementation(() => {
+      throw new Error('error');
+    });
+    await service.checkRegistration();
+    expect(logger.error).toHaveBeenCalledWith(
+      `Error while checking registration status on ${mockResult.host}${mockResult.checkUrl}. Error: error`
+    );
+  });
+
+  it('should check registration with proxy', async () => {
+    const mockResult: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: true,
+      proxyUrl: 'http://localhost:3128',
+      proxyUsername: 'user',
+      proxyPassword: 'pass',
+      token: 'token',
+      activationCode: '1234',
+      checkUrl: '/check/url',
+      status: 'PENDING',
+      activationDate: '2020-20-20T00:00:00.000Z',
+      activationExpirationDate: ''
+    };
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    const fetchResponse = { status: 'COMPLETED', expired: true, accessToken: 'access_token' };
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+    service.activateRegistration = jest.fn();
+
+    await service.checkRegistration();
+    expect(fetch).toHaveBeenCalledWith(`${mockResult.host}${mockResult.checkUrl}`, { method: 'GET', timeout: 10000 });
+    expect(service.activateRegistration).toHaveBeenCalledWith('2020-02-02T02:02:02.222Z', 'access_token');
+  });
+
+  it('should check registration with proxy and without password', async () => {
+    const mockResult: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: true,
+      proxyUrl: 'http://localhost:3128',
+      proxyUsername: '',
+      proxyPassword: '',
+      token: 'token',
+      activationCode: '1234',
+      checkUrl: '/check/url',
+      status: 'PENDING',
+      activationDate: '2020-20-20T00:00:00.000Z',
+      activationExpirationDate: ''
+    };
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    const fetchResponse = { status: 'COMPLETED', expired: true, accessToken: 'access_token' };
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+    service.activateRegistration = jest.fn();
+
+    await service.checkRegistration();
+    expect(fetch).toHaveBeenCalledWith(`${mockResult.host}${mockResult.checkUrl}`, { method: 'GET', timeout: 10000 });
+    expect(service.activateRegistration).toHaveBeenCalledWith('2020-02-02T02:02:02.222Z', 'access_token');
+  });
+
+  it('should check commands', async () => {
+    service.checkRetrievedCommands = jest.fn();
+    service.retrieveCommands = jest.fn();
+    service.sendAckCommands = jest.fn();
+
+    await service.checkCommands();
+    expect(service.checkRetrievedCommands).toHaveBeenCalledTimes(1);
+    expect(service.retrieveCommands).toHaveBeenCalledTimes(1);
+    expect(service.sendAckCommands).toHaveBeenCalledTimes(1);
+    expect(service.checkRetrievedCommands).toHaveBeenCalledTimes(1);
+    expect(service.retrieveCommands).toHaveBeenCalledTimes(1);
+    expect(service.sendAckCommands).toHaveBeenCalledTimes(1);
+  });
+
+  it('should check comm ands and return because already checking', async () => {
+    service.checkRetrievedCommands = jest.fn().mockImplementation(() => {
+      return new Promise<void>(resolve => {
+        setTimeout(resolve, 1000);
+      });
+    });
+    service.retrieveCommands = jest.fn();
+    service.sendAckCommands = jest.fn();
+
+    service.checkCommands();
+    await service.checkCommands();
+    expect(service.checkRetrievedCommands).toHaveBeenCalledTimes(1);
+    expect(service.retrieveCommands).not.toHaveBeenCalled();
+    expect(logger.trace).toHaveBeenCalledWith('On going commands check');
+    await flushPromises();
+  });
+});
+
+describe('Registration service with PENDING registration', () => {
+  const mockResult: RegistrationSettingsDTO = {
+    id: 'id',
+    host: 'http://localhost:4200',
+    acceptUnauthorized: false,
+    useProxy: false,
+    token: 'token',
+    activationCode: '1234',
+    status: 'PENDING',
+    checkUrl: 'http://localhost:4200/check/url',
+    activationDate: '2020-20-20T00:00:00.000Z',
+    activationExpirationDate: ''
+  };
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+    jest.useFakeTimers().setSystemTime(new Date(nowDateString));
+    (createProxyAgent as jest.Mock).mockReturnValue(undefined);
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    service = new RegistrationService(repositoryService, encryptionService, commandService, reloadService, logger);
+  });
+
+  it('should get PENDING registration settings', () => {
+    const setIntervalSpy = jest.spyOn(global, 'setInterval');
+    service.start();
+    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
+    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
+
+    const result = service.getRegistrationSettings();
+    expect(result).toEqual(mockResult);
+    expect(repositoryService.registrationRepository.getRegistrationSettings).toHaveBeenCalledTimes(2);
+  });
+
+  it('should stop and clear interval', async () => {
+    const setIntervalSpy = jest.spyOn(global, 'setInterval');
+    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
+    service.start();
+    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
+    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
+    await service.stop();
+    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
+  });
+
+  it('should activate registration and clear interval', async () => {
+    const setIntervalSpy = jest.spyOn(global, 'setInterval');
+    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
+    service.start();
+    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
+    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
+    await service.activateRegistration('2020-20-20T00:00:00.000Z', 'token');
+    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
+  });
+
+  it('should unregister and clear interval', () => {
+    const setIntervalSpy = jest.spyOn(global, 'setInterval');
+    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
+    service.start();
+    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
+    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
+    service.unregister();
+    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
+  });
+});
+
+describe('Registration service with REGISTERED registration', () => {
+  const mockResult: RegistrationSettingsDTO = {
+    id: 'id',
+    host: 'http://localhost:4200',
+    acceptUnauthorized: false,
+    useProxy: false,
+    token: 'token',
+    activationCode: '1234',
+    status: 'REGISTERED',
+    checkUrl: 'http://localhost:4200/check/url',
+    activationDate: '2020-20-20T00:00:00.000Z',
+    activationExpirationDate: ''
+  };
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+    jest.useFakeTimers().setSystemTime(new Date(nowDateString));
+    (createProxyAgent as jest.Mock).mockReturnValue(undefined);
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    service = new RegistrationService(repositoryService, encryptionService, commandService, reloadService, logger);
+  });
+
+  it('should get REGISTERED registration settings', () => {
+    const setIntervalSpy = jest.spyOn(global, 'setInterval');
+
+    service.start();
+    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
+    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
+
+    const result = service.getRegistrationSettings();
+    expect(result).toEqual(mockResult);
+    expect(repositoryService.registrationRepository.getRegistrationSettings).toHaveBeenCalledTimes(2);
+  });
+
+  it('should stop and clear interval', async () => {
+    const setIntervalSpy = jest.spyOn(global, 'setInterval');
+    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
+
+    service.start();
+    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
+    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
+    await service.stop();
+    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
+  });
+
+  it('should activate registration and clear interval', async () => {
+    const setIntervalSpy = jest.spyOn(global, 'setInterval');
+    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
+
+    service.start();
+    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
+    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
+    await service.activateRegistration('2020-20-20T00:00:00.000Z', 'token');
+    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
+  });
+
+  it('should unregister and clear interval', () => {
+    const setIntervalSpy = jest.spyOn(global, 'setInterval');
+    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
+
+    service.start();
+    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
+    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
+    service.unregister();
+    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
+  });
+});
+
+describe('OIBus service should interact with OIA and', () => {
+  const mockResult: RegistrationSettingsDTO = {
+    id: 'id',
+    host: 'http://localhost:4200',
+    acceptUnauthorized: false,
+    useProxy: false,
+    token: 'token',
+    activationCode: '1234',
+    status: 'REGISTERED',
+    checkUrl: 'http://localhost:4200/check/url',
+    activationDate: '2020-20-20T00:00:00.000Z',
+    activationExpirationDate: ''
+  };
+  const mockEngineSettings: EngineSettingsDTO = {
+    id: 'id1',
+    name: 'MyOIBus',
+    logParameters: {
+      oia: {
+        level: 'error'
+      }
+    }
+  } as EngineSettingsDTO;
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+    jest.useFakeTimers().setSystemTime(new Date(nowDateString));
+    (createProxyAgent as jest.Mock).mockReturnValue(undefined);
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValue(mockEngineSettings);
+    (getNetworkSettingsFromRegistration as jest.Mock).mockReturnValue({ host: 'http://localhost:4200', headers: {}, agent: undefined });
+    service = new RegistrationService(repositoryService, encryptionService, commandService, reloadService, logger);
+  });
+
+  it('should ack commands and return if no commands in OIBus', async () => {
+    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([]);
+
+    await service.sendAckCommands();
+    expect(fetch).not.toHaveBeenCalled();
+  });
+
+  it('should ack commands', async () => {
+    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
+
+    const fetchResponse: Array<OIBusCommandDTO> = [{ id: 'id1' }] as Array<OIBusCommandDTO>;
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+
+    await service.sendAckCommands();
+    expect(logger.trace).toHaveBeenCalledWith(`1 commands acknowledged`);
+    expect(fetch).toHaveBeenCalledWith('http://localhost:4200/api/oianalytics/oibus/commands/status', {
+      method: 'PUT',
+      headers: {
+        'Content-Type': 'application/json'
+      },
+      body: JSON.stringify([command]),
+      timeout: 10000,
+      agent: undefined
+    });
+    expect(repositoryService.commandRepository.markAsAcknowledged).toHaveBeenCalledWith('id1');
+  });
+
+  it('should ack commands and manage 404 error', async () => {
+    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
+
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response('invalid', { status: 404 })));
+
+    await service.sendAckCommands();
+    expect(logger.error).toHaveBeenCalledWith(
+      `Error 404 while acknowledging 1 commands on http://localhost:4200/api/oianalytics/oibus/commands/status: Not Found`
+    );
+  });
+
+  it('should ack commands and log error on fetch error', async () => {
+    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
+
+    (fetch as unknown as jest.Mock).mockImplementation(() => {
+      throw new Error('error');
+    });
+
+    await service.sendAckCommands();
+    expect(logger.error).toHaveBeenCalledWith(
+      `Error while acknowledging 1 commands on http://localhost:4200/api/oianalytics/oibus/commands/status. ${new Error('error')}`
+    );
+  });
+
+  it('should check cancelled commands and return if no commands in OIBus', async () => {
+    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([]);
+
+    await service.checkRetrievedCommands();
+    expect(fetch).not.toHaveBeenCalled();
+  });
+
+  it('should check cancelled commands and no command retrieved from oia', async () => {
+    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
+
+    const fetchResponse: Array<OIBusCommandDTO> = [];
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+
+    await service.checkRetrievedCommands();
+    expect(logger.trace).toHaveBeenCalledWith(`No command cancelled among the 1 commands`);
+    expect(fetch).toHaveBeenCalledWith('http://localhost:4200/api/oianalytics/oibus/commands/list-by-ids?ids=id1', {
+      method: 'GET',
+      headers: {},
+      timeout: 10000,
+      agent: undefined
+    });
+  });
+
+  it('should check cancelled commands and cancel retrieved commands', async () => {
+    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
+
+    const fetchResponse: Array<OIBusCommandDTO> = [{ id: 'id1' }] as Array<OIBusCommandDTO>;
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+
+    await service.checkRetrievedCommands();
+    expect(logger.trace).toHaveBeenCalledWith(`1 commands cancelled among the 1 pending commands`);
+    expect(fetch).toHaveBeenCalledWith('http://localhost:4200/api/oianalytics/oibus/commands/list-by-ids?ids=id1', {
+      method: 'GET',
+      headers: {},
+      timeout: 10000,
+      agent: undefined
+    });
+    expect(repositoryService.commandRepository.cancel).toHaveBeenCalledWith('id1');
+  });
+
+  it('should check cancelled commands log error if fetch response not ok', async () => {
+    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
+
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response('invalid', { status: 404 })));
+
+    await service.checkRetrievedCommands();
+    expect(logger.error).toHaveBeenCalledWith(
+      `Error 404 while checking PENDING commands status on http://localhost:4200/api/oianalytics/oibus/commands/list-by-ids?ids=id1: Not Found`
+    );
+  });
+
+  it('should check cancelled commands log error on fetch error', async () => {
+    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
+
+    (fetch as unknown as jest.Mock).mockImplementation(() => {
+      throw new Error('error');
+    });
+
+    await service.checkRetrievedCommands();
+    expect(logger.error).toHaveBeenCalledWith(
+      `Error while checking PENDING commands status on http://localhost:4200/api/oianalytics/oibus/commands/list-by-ids?ids=id1. ${new Error(
+        'error'
+      )}`
+    );
+  });
+
+  it('should retrieve commands and trace logs if no command retrieved', async () => {
+    const fetchResponse: Array<OIBusCommandDTO> = [];
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+
+    await service.retrieveCommands();
+    expect(logger.trace).toHaveBeenCalledWith(`No command to create`);
+    expect(fetch).toHaveBeenCalledWith('http://localhost:4200/api/oianalytics/oibus/commands/pending', {
+      method: 'GET',
+      headers: {},
+      timeout: 10000,
+      agent: undefined
+    });
+  });
+
+  it('should retrieve and create commands', async () => {
+    const fetchResponse: Array<OIBusCommandDTO> = [command];
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+
+    await service.retrieveCommands();
+    expect(logger.trace).toHaveBeenCalledWith(`1 commands to add`);
+    expect(fetch).toHaveBeenCalledWith('http://localhost:4200/api/oianalytics/oibus/commands/pending', {
+      method: 'GET',
+      headers: {},
+      timeout: 10000,
+      agent: undefined
+    });
+    expect(repositoryService.commandRepository.create).toHaveBeenCalledWith('id1', {
+      type: command.type,
+      version: command.version,
+      assetId: command.assetId
+    });
+  });
+
+  it('should retrieve log error on bad fetch response', async () => {
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response('invalid', { status: 404 })));
+
+    await service.retrieveCommands();
+    expect(logger.error).toHaveBeenCalledWith(
+      `Error 404 while retrieving commands on http://localhost:4200/api/oianalytics/oibus/commands/pending: Not Found`
+    );
+  });
+
+  it('should retrieve log error on fetch error', async () => {
+    (fetch as unknown as jest.Mock).mockImplementation(() => {
+      throw new Error('error');
+    });
+
+    await service.retrieveCommands();
+    expect(logger.error).toHaveBeenCalledWith(
+      `Error while retrieving commands on http://localhost:4200/api/oianalytics/oibus/commands/pending. ${new Error('error')}`
+    );
+  });
+
+  it('should unregister', async () => {
+    service.unregister = jest.fn();
+    await service.onUnregister();
+    expect(service.unregister).toHaveBeenCalledTimes(1);
+    expect(reloadService.restartLogger).toHaveBeenCalledTimes(1);
+  });
+
+  it('should update registration', async () => {
+    (generateRandomId as jest.Mock).mockReturnValue('1234');
+
+    const command: RegistrationSettingsCommandDTO = {
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false
+    };
+    (getOIBusInfo as jest.Mock).mockReturnValueOnce({ version: 'v3.2.0' });
+    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce(mockEngineSettings);
+    const fetchResponse = {
+      redirectUrl: 'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
+      expirationDate: '2020-02-02T02:12:02.222Z'
+    };
+
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+
+    await service.updateRegistrationSettings(command);
+    expect(reloadService.restartLogger).toHaveBeenCalledTimes(1);
+    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledTimes(1);
+    expect(generateRandomId).toHaveBeenCalledWith(6);
+    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledWith(
+      command,
+      '1234',
+      'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
+      '2020-02-02T02:12:02.222Z'
+    );
+  });
+
+  it('should check registration', async () => {
+    const mockResult: RegistrationSettingsDTO = {
+      id: 'id',
+      host: 'http://localhost:4200',
+      acceptUnauthorized: false,
+      useProxy: false,
+      token: 'token',
+      activationCode: '1234',
+      checkUrl: '/check/url',
+      status: 'PENDING',
+      activationDate: '2020-20-20T00:00:00.000Z',
+      activationExpirationDate: ''
+    };
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
+    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValue(mockEngineSettings);
+    const fetchResponse = { status: 'COMPLETED', expired: true, accessToken: 'access_token' };
+    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
+    service.activateRegistration = jest.fn();
+
+    await service.checkRegistration();
+    expect(reloadService.restartLogger).toHaveBeenCalled();
+    expect(fetch).toHaveBeenCalledWith(`${mockResult.host}${mockResult.checkUrl}`, { method: 'GET', timeout: 10000 });
+    expect(service.activateRegistration).toHaveBeenCalledWith('2020-02-02T02:02:02.222Z', 'access_token');
+  });
+});
diff --git a/backend/src/service/oia/registration.service.ts b/backend/src/service/oia/registration.service.ts
new file mode 100644
index 0000000000..94a5b3d8c7
--- /dev/null
+++ b/backend/src/service/oia/registration.service.ts
@@ -0,0 +1,347 @@
+import { getNetworkSettingsFromRegistration, getOIBusInfo, generateRandomId } from '../utils';
+import RepositoryService from '../repository.service';
+import EncryptionService from '../encryption.service';
+import pino from 'pino';
+import { OIBusCommandDTO, OIBusCommand } from '../../../../shared/model/command.model';
+import { DateTime } from 'luxon';
+import { RegistrationSettingsDTO, RegistrationSettingsCommandDTO } from '../../../../shared/model/engine.model';
+import { createProxyAgent } from '../proxy-agent';
+import fetch from 'node-fetch';
+import { Instant } from '../../../../shared/model/types';
+import CommandService from './command.service';
+import ReloadService from '../reload.service';
+
+const CHECK_TIMEOUT = 10_000;
+export default class RegistrationService {
+  private intervalCheckRegistration: NodeJS.Timeout | null = null;
+  private intervalCheckCommands: NodeJS.Timeout | null = null;
+  private ongoingCheckRegistration = false;
+  private ongoingCheckCommands = false;
+
+  constructor(
+    private repositoryService: RepositoryService,
+    private encryptionService: EncryptionService,
+    private commandService: CommandService,
+    private reloadService: ReloadService,
+    private logger: pino.Logger
+  ) {}
+
+  start() {
+    const registrationSettings = this.repositoryService.registrationRepository.getRegistrationSettings();
+    if (registrationSettings && registrationSettings.checkUrl && registrationSettings.status === 'PENDING') {
+      this.intervalCheckRegistration = setInterval(this.checkRegistration.bind(this), CHECK_TIMEOUT);
+    }
+    if (registrationSettings && registrationSettings.status === 'REGISTERED') {
+      this.intervalCheckCommands = setInterval(this.checkCommands.bind(this), CHECK_TIMEOUT);
+    }
+  }
+
+  getRegistrationSettings(): RegistrationSettingsDTO | null {
+    return this.repositoryService.registrationRepository.getRegistrationSettings();
+  }
+
+  async updateRegistrationSettings(command: RegistrationSettingsCommandDTO): Promise<void> {
+    const activationCode = generateRandomId(6);
+    const registrationSettings = this.repositoryService.registrationRepository.getRegistrationSettings()!;
+    if (!registrationSettings) {
+      throw new Error(`Registration settings not found`);
+    }
+
+    if (!command.proxyPassword) {
+      command.proxyPassword = registrationSettings.proxyPassword;
+    } else {
+      command.proxyPassword = await this.encryptionService.encryptText(command.proxyPassword);
+    }
+
+    const engineSettings = this.repositoryService.engineRepository.getEngineSettings()!;
+
+    const oibusInfo = getOIBusInfo();
+    const body = {
+      activationCode,
+      oibusVersion: oibusInfo.version,
+      oibusArch: oibusInfo.architecture,
+      oibusOs: oibusInfo.operatingSystem,
+      oibusId: engineSettings.id,
+      oibusName: engineSettings.name
+    };
+    let response;
+    try {
+      const url = `${command.host}/api/oianalytics/oibus/registration`;
+      const agent = createProxyAgent(
+        command.useProxy,
+        url,
+        command.useProxy
+          ? {
+              url: command.proxyUrl!,
+              username: command.proxyUsername!,
+              password: command.proxyPassword ? await this.encryptionService.decryptText(command.proxyPassword) : null
+            }
+          : null,
+        command.acceptUnauthorized
+      );
+      response = await fetch(url, {
+        method: 'POST',
+        timeout: CHECK_TIMEOUT,
+        agent,
+        body: JSON.stringify(body),
+        headers: {
+          'Content-Type': 'application/json'
+        }
+      });
+    } catch (fetchError) {
+      throw new Error(`Registration failed: ${fetchError}`);
+    }
+
+    if (!response.ok) {
+      throw new Error(`Registration failed with status code ${response.status} and message: ${response.statusText}`);
+    }
+
+    const result: { redirectUrl: string; expirationDate: Instant } = await response.json();
+    this.repositoryService.registrationRepository.updateRegistration(command, activationCode, result.redirectUrl, result.expirationDate);
+    if (!this.intervalCheckRegistration) {
+      this.intervalCheckRegistration = setInterval(this.checkRegistration.bind(this), CHECK_TIMEOUT);
+    }
+
+    if (engineSettings.logParameters.oia.level !== 'silent') {
+      await this.reloadService.restartLogger(engineSettings);
+    }
+  }
+
+  async activateRegistration(activationDate: string, accessToken: string): Promise<void> {
+    const encryptedToken = await this.encryptionService.encryptText(accessToken);
+    this.repositoryService.registrationRepository.activateRegistration(activationDate, encryptedToken);
+    if (this.intervalCheckRegistration) {
+      clearInterval(this.intervalCheckRegistration);
+      this.intervalCheckRegistration = null;
+    }
+
+    if (this.intervalCheckCommands) {
+      clearInterval(this.intervalCheckCommands);
+      this.intervalCheckCommands = null;
+    }
+    this.intervalCheckCommands = setInterval(this.checkCommands.bind(this), CHECK_TIMEOUT);
+  }
+
+  unregister() {
+    this.repositoryService.registrationRepository.unregister();
+    if (this.intervalCheckRegistration) {
+      clearInterval(this.intervalCheckRegistration);
+      this.intervalCheckRegistration = null;
+    }
+
+    if (this.intervalCheckCommands) {
+      clearInterval(this.intervalCheckCommands);
+      this.intervalCheckCommands = null;
+    }
+  }
+
+  async checkRegistration(): Promise<void> {
+    if (this.ongoingCheckRegistration) {
+      this.logger.trace(`On going registration check`);
+      return;
+    }
+    this.logger.trace(`Registration check`);
+    const registrationSettings = this.repositoryService.registrationRepository.getRegistrationSettings();
+    if (!registrationSettings || !registrationSettings.checkUrl) {
+      this.logger.error(`Error while checking registration status: Could not retrieve check URL`);
+      return;
+    }
+    let response;
+    const url = `${registrationSettings.host}${registrationSettings.checkUrl}`;
+    try {
+      this.ongoingCheckRegistration = true;
+      const agent = createProxyAgent(
+        registrationSettings.useProxy,
+        url,
+        registrationSettings.useProxy
+          ? {
+              url: registrationSettings.proxyUrl!,
+              username: registrationSettings.proxyUsername!,
+              password: registrationSettings.proxyPassword
+                ? await this.encryptionService.decryptText(registrationSettings.proxyPassword)
+                : null
+            }
+          : null,
+        registrationSettings.acceptUnauthorized
+      );
+      response = await fetch(url, {
+        method: 'GET',
+        timeout: CHECK_TIMEOUT,
+        agent
+      });
+      if (!response.ok) {
+        this.logger.error(`Error ${response.status} while checking registration status on ${url}: ${response.statusText}`);
+        this.ongoingCheckRegistration = false;
+        return;
+      }
+
+      const responseData: { status: string; expired: boolean; accessToken: string } = await response.json();
+      if (responseData.status !== 'COMPLETED') {
+        this.logger.warn(`Registration not completed. Status: ${responseData.status}`);
+      } else {
+        await this.activateRegistration(DateTime.now().toUTC().toISO()!, responseData.accessToken);
+        this.logger.info(`OIBus registered on ${registrationSettings.host}`);
+        const engineSettings = this.repositoryService.engineRepository.getEngineSettings()!;
+        if (engineSettings.logParameters.oia.level !== 'silent') {
+          await this.reloadService.restartLogger(engineSettings);
+        }
+      }
+    } catch (fetchError) {
+      this.logger.error(`Error while checking registration status on ${url}. ${fetchError}`);
+    }
+    this.ongoingCheckRegistration = false;
+  }
+
+  async checkCommands(): Promise<void> {
+    if (this.ongoingCheckCommands) {
+      this.logger.trace(`On going commands check`);
+      return;
+    }
+    this.ongoingCheckCommands = true;
+
+    await this.sendAckCommands();
+    await this.checkRetrievedCommands();
+    await this.retrieveCommands();
+    this.ongoingCheckCommands = false;
+  }
+
+  async sendAckCommands(): Promise<void> {
+    const commandsToAck = this.repositoryService.commandRepository.searchCommandsList({
+      status: [],
+      types: [],
+      ack: false
+    });
+    if (commandsToAck.length === 0) {
+      return;
+    }
+
+    const endpoint = `/api/oianalytics/oibus/commands/status`;
+    const registrationSettings = this.getRegistrationSettings();
+    const connectionSettings = await getNetworkSettingsFromRegistration(registrationSettings, endpoint, this.encryptionService);
+    let response;
+    const url = `${connectionSettings.host}${endpoint}`;
+    try {
+      response = await fetch(url, {
+        method: 'PUT',
+        body: JSON.stringify(commandsToAck),
+        headers: { ...connectionSettings.headers, 'Content-Type': 'application/json' },
+        timeout: CHECK_TIMEOUT,
+        agent: connectionSettings.agent
+      });
+      if (!response.ok) {
+        this.logger.error(
+          `Error ${response.status} while acknowledging ${commandsToAck.length} commands on ${url}: ${response.statusText}`
+        );
+        return;
+      }
+      for (const command of commandsToAck) {
+        this.repositoryService.commandRepository.markAsAcknowledged(command.id);
+      }
+      this.logger.trace(`${commandsToAck.length} commands acknowledged`);
+    } catch (fetchError) {
+      this.logger.error(`Error while acknowledging ${commandsToAck.length} commands on ${url}. ${fetchError}`);
+    }
+  }
+
+  /**
+   * Check if retrieved commands have been cancelled on OIAnalytics before running them
+   */
+  async checkRetrievedCommands(): Promise<void> {
+    const pendingCommands = this.repositoryService.commandRepository.searchCommandsList({ status: ['RETRIEVED'], types: [] });
+    if (pendingCommands.length === 0) {
+      return;
+    }
+
+    let endpoint = `/api/oianalytics/oibus/commands/list-by-ids?`;
+    for (const command of pendingCommands) {
+      endpoint += `ids=${command.id}&`;
+    }
+    endpoint = endpoint.slice(0, endpoint.length - 1);
+    const registrationSettings = this.getRegistrationSettings();
+    const connectionSettings = await getNetworkSettingsFromRegistration(registrationSettings, endpoint, this.encryptionService);
+    let response;
+    const url = `${connectionSettings.host}${endpoint}`;
+    try {
+      response = await fetch(url, {
+        method: 'GET',
+        headers: connectionSettings.headers,
+        timeout: CHECK_TIMEOUT,
+        agent: connectionSettings.agent
+      });
+      if (!response.ok) {
+        this.logger.error(`Error ${response.status} while checking PENDING commands status on ${url}: ${response.statusText}`);
+        return;
+      }
+      const commandsToCancel: Array<OIBusCommandDTO> = await response.json();
+      if (commandsToCancel.length === 0) {
+        this.logger.trace(`No command cancelled among the ${pendingCommands.length} commands`);
+        return;
+      }
+      this.logger.trace(`${commandsToCancel.length} commands cancelled among the ${pendingCommands.length} pending commands`);
+      for (const command of commandsToCancel) {
+        this.commandService.removeCommandFromQueue(command.id);
+        this.repositoryService.commandRepository.cancel(command.id);
+      }
+    } catch (fetchError) {
+      this.logger.error(`Error while checking PENDING commands status on ${url}. ${fetchError}`);
+    }
+  }
+
+  async retrieveCommands(): Promise<void> {
+    const endpoint = `/api/oianalytics/oibus/commands/pending`;
+    const registrationSettings = this.getRegistrationSettings();
+    const connectionSettings = await getNetworkSettingsFromRegistration(registrationSettings, endpoint, this.encryptionService);
+    let response;
+    const url = `${connectionSettings.host}${endpoint}`;
+    try {
+      response = await fetch(url, {
+        method: 'GET',
+        headers: connectionSettings.headers,
+        timeout: CHECK_TIMEOUT,
+        agent: connectionSettings.agent
+      });
+      if (!response.ok) {
+        this.logger.error(`Error ${response.status} while retrieving commands on ${url}: ${response.statusText}`);
+        return;
+      }
+      const newCommands: Array<OIBusCommandDTO> = await response.json();
+      if (newCommands.length === 0) {
+        this.logger.trace(`No command to create`);
+        return;
+      }
+      this.logger.trace(`${newCommands.length} commands to add`);
+      for (const command of newCommands) {
+        const creationCommand: OIBusCommand = {
+          type: command.type,
+          version: command.version,
+          assetId: command.assetId
+        };
+        const newCommand = this.repositoryService.commandRepository.create(command.id, creationCommand);
+        this.commandService.addCommandToQueue(newCommand);
+      }
+      await this.sendAckCommands();
+    } catch (fetchError) {
+      this.logger.error(`Error while retrieving commands on ${url}. ${fetchError}`);
+    }
+  }
+
+  stop() {
+    if (this.intervalCheckRegistration) {
+      clearInterval(this.intervalCheckRegistration);
+      this.intervalCheckRegistration = null;
+    }
+
+    if (this.intervalCheckCommands) {
+      clearInterval(this.intervalCheckCommands);
+      this.intervalCheckCommands = null;
+    }
+  }
+
+  async onUnregister() {
+    this.unregister();
+    const engineSettings = this.repositoryService.engineRepository.getEngineSettings()!;
+    if (engineSettings.logParameters.oia.level !== 'silent') {
+      await this.reloadService.restartLogger(engineSettings);
+    }
+  }
+}
diff --git a/backend/src/service/oibus.service.spec.ts b/backend/src/service/oibus.service.spec.ts
index 3ac434666e..c3575d932c 100644
--- a/backend/src/service/oibus.service.spec.ts
+++ b/backend/src/service/oibus.service.spec.ts
@@ -1,50 +1,22 @@
-import fetch from 'node-fetch';
 import OIBusService from './oibus.service';
 import OIBusEngine from '../engine/oibus-engine';
 import OibusEngineMock from '../tests/__mocks__/oibus-engine.mock';
 import HistoryQueryEngine from '../engine/history-query-engine';
 import HistoryQueryEngineMock from '../tests/__mocks__/history-query-engine.mock';
-import * as utils from '../service/utils';
-import RepositoryService from './repository.service';
-import RepositoryServiceMock from '../tests/__mocks__/repository-service.mock';
-import { RegistrationSettingsCommandDTO, RegistrationSettingsDTO } from '../../../shared/model/engine.model';
-import EncryptionServiceMock from '../tests/__mocks__/encryption-service.mock';
-import EncryptionService from './encryption.service';
+import { createProxyAgent } from './proxy-agent';
 import pino from 'pino';
 import PinoLogger from '../tests/__mocks__/logger.mock';
-import { createProxyAgent } from './proxy-agent';
-import { OIBusCommandDTO } from '../../../shared/model/command.model';
-import { getNetworkSettingsFromRegistration } from './utils';
-import CommandService from './command.service';
-import CommandServiceMock from '../tests/__mocks__/command-service.mock';
 
 jest.mock('node:fs/promises');
 jest.mock('node-fetch');
-const { Response } = jest.requireActual('node-fetch');
 jest.mock('./utils');
 jest.mock('./proxy-agent');
 
 const oibusEngine: OIBusEngine = new OibusEngineMock();
 const historyQueryEngine: HistoryQueryEngine = new HistoryQueryEngineMock();
-const repositoryService: RepositoryService = new RepositoryServiceMock('', '');
-const encryptionService: EncryptionService = new EncryptionServiceMock('', '');
-const commandService: CommandService = new CommandServiceMock('', '');
-
-const nowDateString = '2020-02-02T02:02:02.222Z';
 const logger: pino.Logger = new PinoLogger();
-const flushPromises = () => new Promise(jest.requireActual('timers').setImmediate);
 
-const command: OIBusCommandDTO = {
-  id: 'id1',
-  type: 'UPGRADE',
-  status: 'COMPLETED',
-  ack: true,
-  creationDate: '2023-01-01T12:00:00Z',
-  completedDate: '2023-01-01T12:00:00Z',
-  result: 'ok',
-  version: '3.2.0',
-  assetId: 'assetId'
-};
+const nowDateString = '2020-02-02T02:02:02.222Z';
 
 let service: OIBusService;
 describe('OIBus service', () => {
@@ -53,7 +25,7 @@ describe('OIBus service', () => {
     jest.useFakeTimers().setSystemTime(new Date(nowDateString));
     (createProxyAgent as jest.Mock).mockReturnValue(undefined);
 
-    service = new OIBusService(oibusEngine, historyQueryEngine, repositoryService, encryptionService, commandService, logger);
+    service = new OIBusService(oibusEngine, historyQueryEngine);
   });
 
   it('should restart OIBus', async () => {
@@ -80,730 +52,9 @@ describe('OIBus service', () => {
     expect(oibusEngine.addExternalFile).toHaveBeenCalledWith('source', 'filePath');
   });
 
-  it('should get NOT_REGISTERED registration settings', () => {
-    const mockResult: RegistrationSettingsDTO = {
-      id: 'id',
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: false,
-      token: 'token',
-      activationCode: '1234',
-      status: 'NOT_REGISTERED',
-      activationDate: '2020-20-20T00:00:00.000Z',
-      activationExpirationDate: ''
-    };
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-    const result = service.getRegistrationSettings();
-    expect(result).toEqual(mockResult);
-    expect(repositoryService.registrationRepository.getRegistrationSettings).toHaveBeenCalledTimes(2);
-  });
-
-  it('should update registration', async () => {
-    (utils.generateRandomId as jest.Mock).mockReturnValue('1234');
-
-    const command: RegistrationSettingsCommandDTO = {
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: false
-    };
-    (utils.getOIBusInfo as jest.Mock).mockReturnValueOnce({ version: 'v3.2.0' });
-    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce({ id: 'id1', name: 'MyOIBus' });
-    const fetchResponse = {
-      redirectUrl: 'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
-      expirationDate: '2020-02-02T02:12:02.222Z'
-    };
-
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-
-    await service.updateRegistrationSettings(command);
-    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledTimes(1);
-    expect(utils.generateRandomId).toHaveBeenCalledWith(6);
-    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledWith(
-      command,
-      '1234',
-      'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
-      '2020-02-02T02:12:02.222Z'
-    );
-  });
-
-  it('should update registration with proxy', async () => {
-    (utils.generateRandomId as jest.Mock).mockReturnValue('1234');
-
-    const command: RegistrationSettingsCommandDTO = {
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: true,
-      proxyUrl: 'http://localhost:3128',
-      proxyUsername: 'user',
-      proxyPassword: 'pass'
-    };
-    (utils.getOIBusInfo as jest.Mock).mockReturnValueOnce({ version: 'v3.2.0' });
-    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce({ id: 'id1', name: 'MyOIBus' });
-    const fetchResponse = {
-      redirectUrl: 'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
-      expirationDate: '2020-02-02T02:12:02.222Z'
-    };
-
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-
-    await service.updateRegistrationSettings(command);
-    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledTimes(1);
-    expect(utils.generateRandomId).toHaveBeenCalledWith(6);
-    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledWith(
-      command,
-      '1234',
-      'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
-      '2020-02-02T02:12:02.222Z'
-    );
-  });
-
-  it('should update registration with proxy and without password', async () => {
-    (utils.generateRandomId as jest.Mock).mockReturnValue('1234');
-
-    const command: RegistrationSettingsCommandDTO = {
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: true,
-      proxyUrl: 'http://localhost:3128',
-      proxyUsername: '',
-      proxyPassword: ''
-    };
-    (utils.getOIBusInfo as jest.Mock).mockReturnValueOnce({ version: 'v3.2.0' });
-    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce({ id: 'id1', name: 'MyOIBus' });
-    const fetchResponse = {
-      redirectUrl: 'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
-      expirationDate: '2020-02-02T02:12:02.222Z'
-    };
-
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-
-    await service.updateRegistrationSettings(command);
-    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledTimes(1);
-    expect(utils.generateRandomId).toHaveBeenCalledWith(6);
-    expect(repositoryService.registrationRepository.updateRegistration).toHaveBeenCalledWith(
-      command,
-      '1234',
-      'http://localhost:4200/api/oianalytics/oibus/check-registration?id=id',
-      '2020-02-02T02:12:02.222Z'
-    );
-  });
-
-  it('should handle fetch error during registration update', async () => {
-    (utils.generateRandomId as jest.Mock).mockReturnValue('1234');
-
-    const command: RegistrationSettingsCommandDTO = {
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: false
-    };
-    (utils.getOIBusInfo as jest.Mock).mockReturnValueOnce({ version: 'v3.2.0' });
-    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce({ id: 'id1', name: 'MyOIBus' });
-    (fetch as unknown as jest.Mock).mockImplementation(() => {
-      throw new Error('error');
-    });
-
-    let error;
-    try {
-      await service.updateRegistrationSettings(command);
-    } catch (e) {
-      error = e;
-    }
-    expect(error).toEqual(new Error('Registration failed: Error: error'));
-  });
-
-  it('should handle fetch bad response during registration update', async () => {
-    (utils.generateRandomId as jest.Mock).mockReturnValue('1234');
-
-    const command: RegistrationSettingsCommandDTO = {
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: false
-    };
-    (utils.getOIBusInfo as jest.Mock).mockReturnValueOnce({ version: 'v3.2.0' });
-    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce({ id: 'id1', name: 'MyOIBus' });
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response('invalid', { status: 404 })));
-
-    let error;
-    try {
-      await service.updateRegistrationSettings(command);
-    } catch (e) {
-      error = e;
-    }
-    expect(error).toEqual(new Error(`Registration failed with status code 404 and message: Not Found`));
-  });
-
-  it('should handle error if registration not found', async () => {
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValueOnce(null);
-    (utils.generateRandomId as jest.Mock).mockReturnValue('1234');
-    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce({ id: 'id1', name: 'MyOIBus' });
-
-    const command: RegistrationSettingsCommandDTO = {
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: false
-    };
-
-    let error;
-    try {
-      await service.updateRegistrationSettings(command);
-    } catch (e) {
-      error = e;
-    }
-    expect(error).toEqual(new Error('Registration settings not found'));
-  });
-
-  it('should activate registration', async () => {
-    await service.activateRegistration('2020-20-20T00:00:00.000Z', 'token');
-    expect(repositoryService.registrationRepository.activateRegistration).toHaveBeenCalledWith('2020-20-20T00:00:00.000Z', 'token');
-  });
-
-  it('should unregister', () => {
-    service.unregister();
-    expect(repositoryService.registrationRepository.unregister).toHaveBeenCalledTimes(1);
-  });
-
-  it('should check registration', async () => {
-    const mockResult: RegistrationSettingsDTO = {
-      id: 'id',
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: false,
-      token: 'token',
-      activationCode: '1234',
-      checkUrl: '/check/url',
-      status: 'PENDING',
-      activationDate: '2020-20-20T00:00:00.000Z',
-      activationExpirationDate: ''
-    };
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-    const fetchResponse = { status: 'COMPLETED', expired: true, accessToken: 'access_token' };
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-    service.activateRegistration = jest.fn();
-
-    await service.checkRegistration();
-    expect(fetch).toHaveBeenCalledWith(`${mockResult.host}${mockResult.checkUrl}`, { method: 'GET', timeout: 10000 });
-    expect(service.activateRegistration).toHaveBeenCalledWith('2020-02-02T02:02:02.222Z', 'access_token');
-  });
-
-  it('should check registration and return because already checking', async () => {
-    const mockResult: RegistrationSettingsDTO = {
-      id: 'id',
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: false,
-      token: 'token',
-      activationCode: '1234',
-      checkUrl: '/check/url',
-      status: 'PENDING',
-      activationDate: '2020-20-20T00:00:00.000Z',
-      activationExpirationDate: ''
-    };
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-    const fetchResponse = { status: 'COMPLETED', expired: true, accessToken: 'access_token' };
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-    service.activateRegistration = jest.fn();
-
-    service.checkRegistration();
-    await service.checkRegistration();
-    expect(logger.trace).toHaveBeenCalledWith('On going registration check');
-    await flushPromises();
-  });
-
-  it('should check registration but fail because of return status', async () => {
-    const mockResult: RegistrationSettingsDTO = {
-      id: 'id',
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: false,
-      token: 'token',
-      activationCode: '1234',
-      checkUrl: '/check/url',
-      status: 'PENDING',
-      activationDate: '2020-20-20T00:00:00.000Z',
-      activationExpirationDate: ''
-    };
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-    const fetchResponse = { status: 'DECLINED', expired: true, accessToken: 'access_token' };
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-    service.activateRegistration = jest.fn();
-
-    await service.checkRegistration();
-    expect(fetch).toHaveBeenCalledWith(`${mockResult.host}${mockResult.checkUrl}`, { method: 'GET', timeout: 10000 });
-    expect(service.activateRegistration).not.toHaveBeenCalled();
-    expect(logger.warn).toHaveBeenCalledWith(`Registration not completed. Status: DECLINED`);
-    await service.checkRegistration();
-  });
-
-  it('should check registration but fail because of fetch response', async () => {
-    const mockResult: RegistrationSettingsDTO = {
-      id: 'id',
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: false,
-      token: 'token',
-      activationCode: '1234',
-      checkUrl: '/check/url',
-      status: 'PENDING',
-      activationDate: '2020-20-20T00:00:00.000Z',
-      activationExpirationDate: ''
-    };
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response('invalid', { status: 404 })));
-    await service.checkRegistration();
-    expect(fetch).toHaveBeenCalledWith(`${mockResult.host}${mockResult.checkUrl}`, { method: 'GET', timeout: 10000 });
-    expect(logger.error).toHaveBeenCalledWith(
-      `Error 404 while checking registration status on ${mockResult.host}${mockResult.checkUrl}: Not Found`
-    );
-  });
-
-  it('should check registration and fail when registration check url not set', async () => {
-    const mockResult: RegistrationSettingsDTO = {
-      id: 'id',
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: false,
-      token: 'token',
-      activationCode: '1234',
-      status: 'PENDING',
-      activationDate: '2020-20-20T00:00:00.000Z',
-      activationExpirationDate: ''
-    };
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-    await service.checkRegistration();
-    expect(logger.error).toHaveBeenCalledWith('Error while checking registration status: Could not retrieve check URL');
-  });
-
-  it('should check registration and fail on fetch error', async () => {
-    const mockResult: RegistrationSettingsDTO = {
-      id: 'id',
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: false,
-      token: 'token',
-      activationCode: '1234',
-      checkUrl: 'check/url',
-      status: 'PENDING',
-      activationDate: '2020-20-20T00:00:00.000Z',
-      activationExpirationDate: ''
-    };
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-    (fetch as unknown as jest.Mock).mockImplementation(() => {
-      throw new Error('error');
-    });
-    await service.checkRegistration();
-    expect(logger.error).toHaveBeenCalledWith(
-      `Error while checking registration status on ${mockResult.host}${mockResult.checkUrl}. Error: error`
-    );
-  });
-
-  it('should check registration with proxy', async () => {
-    const mockResult: RegistrationSettingsDTO = {
-      id: 'id',
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: true,
-      proxyUrl: 'http://localhost:3128',
-      proxyUsername: 'user',
-      proxyPassword: 'pass',
-      token: 'token',
-      activationCode: '1234',
-      checkUrl: '/check/url',
-      status: 'PENDING',
-      activationDate: '2020-20-20T00:00:00.000Z',
-      activationExpirationDate: ''
-    };
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-    const fetchResponse = { status: 'COMPLETED', expired: true, accessToken: 'access_token' };
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-    service.activateRegistration = jest.fn();
-
-    await service.checkRegistration();
-    expect(fetch).toHaveBeenCalledWith(`${mockResult.host}${mockResult.checkUrl}`, { method: 'GET', timeout: 10000 });
-    expect(service.activateRegistration).toHaveBeenCalledWith('2020-02-02T02:02:02.222Z', 'access_token');
-  });
-
-  it('should check registration with proxy and without password', async () => {
-    const mockResult: RegistrationSettingsDTO = {
-      id: 'id',
-      host: 'http://localhost:4200',
-      acceptUnauthorized: false,
-      useProxy: true,
-      proxyUrl: 'http://localhost:3128',
-      proxyUsername: '',
-      proxyPassword: '',
-      token: 'token',
-      activationCode: '1234',
-      checkUrl: '/check/url',
-      status: 'PENDING',
-      activationDate: '2020-20-20T00:00:00.000Z',
-      activationExpirationDate: ''
-    };
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-    const fetchResponse = { status: 'COMPLETED', expired: true, accessToken: 'access_token' };
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-    service.activateRegistration = jest.fn();
-
-    await service.checkRegistration();
-    expect(fetch).toHaveBeenCalledWith(`${mockResult.host}${mockResult.checkUrl}`, { method: 'GET', timeout: 10000 });
-    expect(service.activateRegistration).toHaveBeenCalledWith('2020-02-02T02:02:02.222Z', 'access_token');
-  });
-
-  it('should check commands', async () => {
-    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce({ id: 'id1', name: 'MyOIBus' });
-
-    service.checkRetrievedCommands = jest.fn();
-    service.retrieveCommands = jest.fn();
-    service.sendAckCommands = jest.fn();
-
-    await service.checkCommands();
-    expect(service.checkRetrievedCommands).toHaveBeenCalledTimes(1);
-    expect(service.retrieveCommands).toHaveBeenCalledTimes(1);
-    expect(service.sendAckCommands).toHaveBeenCalledTimes(1);
-    expect(service.checkRetrievedCommands).toHaveBeenCalledTimes(1);
-    expect(service.retrieveCommands).toHaveBeenCalledTimes(1);
-    expect(service.sendAckCommands).toHaveBeenCalledTimes(1);
-  });
-
-  it('should check comm ands and return because already checking', async () => {
-    (repositoryService.engineRepository.getEngineSettings as jest.Mock).mockReturnValueOnce({ id: 'id1', name: 'MyOIBus' });
-
-    service.checkRetrievedCommands = jest.fn().mockImplementation(() => {
-      return new Promise<void>(resolve => {
-        setTimeout(resolve, 1000);
-      });
-    });
-    service.retrieveCommands = jest.fn();
-    service.sendAckCommands = jest.fn();
-
-    service.checkCommands();
-    await service.checkCommands();
-    expect(service.checkRetrievedCommands).toHaveBeenCalledTimes(1);
-    expect(service.retrieveCommands).not.toHaveBeenCalled();
-    expect(logger.trace).toHaveBeenCalledWith('On going commands check');
-    await flushPromises();
-  });
-});
-
-describe('OIBus service with PENDING registration', () => {
-  const mockResult: RegistrationSettingsDTO = {
-    id: 'id',
-    host: 'http://localhost:4200',
-    acceptUnauthorized: false,
-    useProxy: false,
-    token: 'token',
-    activationCode: '1234',
-    status: 'PENDING',
-    checkUrl: 'http://localhost:4200/check/url',
-    activationDate: '2020-20-20T00:00:00.000Z',
-    activationExpirationDate: ''
-  };
-
-  beforeEach(() => {
-    jest.clearAllMocks();
-    jest.useFakeTimers().setSystemTime(new Date(nowDateString));
-    (createProxyAgent as jest.Mock).mockReturnValue(undefined);
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-  });
-
-  it('should get PENDING registration settings', () => {
-    const setIntervalSpy = jest.spyOn(global, 'setInterval');
-
-    service = new OIBusService(oibusEngine, historyQueryEngine, repositoryService, encryptionService, commandService, logger);
-    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
-    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
-
-    const result = service.getRegistrationSettings();
-    expect(result).toEqual(mockResult);
-    expect(repositoryService.registrationRepository.getRegistrationSettings).toHaveBeenCalledTimes(2);
-  });
-
-  it('should stop and clear interval', async () => {
-    const setIntervalSpy = jest.spyOn(global, 'setInterval');
-    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
-
-    service = new OIBusService(oibusEngine, historyQueryEngine, repositoryService, encryptionService, commandService, logger);
-    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
-    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
-    await service.stopOIBus();
-    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
-  });
-
-  it('should activate registration and clear interval', async () => {
-    const setIntervalSpy = jest.spyOn(global, 'setInterval');
-    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
-    service = new OIBusService(oibusEngine, historyQueryEngine, repositoryService, encryptionService, commandService, logger);
-    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
-    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
-    await service.activateRegistration('2020-20-20T00:00:00.000Z', 'token');
-    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
-  });
-
-  it('should unregister and clear interval', () => {
-    const setIntervalSpy = jest.spyOn(global, 'setInterval');
-    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
-    service = new OIBusService(oibusEngine, historyQueryEngine, repositoryService, encryptionService, commandService, logger);
-    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
-    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
-    service.unregister();
-    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
-  });
-});
-
-describe('OIBus service with REGISTERED registration', () => {
-  const mockResult: RegistrationSettingsDTO = {
-    id: 'id',
-    host: 'http://localhost:4200',
-    acceptUnauthorized: false,
-    useProxy: false,
-    token: 'token',
-    activationCode: '1234',
-    status: 'REGISTERED',
-    checkUrl: 'http://localhost:4200/check/url',
-    activationDate: '2020-20-20T00:00:00.000Z',
-    activationExpirationDate: ''
-  };
-
-  beforeEach(() => {
-    jest.clearAllMocks();
-    jest.useFakeTimers().setSystemTime(new Date(nowDateString));
-    (createProxyAgent as jest.Mock).mockReturnValue(undefined);
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-  });
-
-  it('should get REGISTERED registration settings', () => {
-    const setIntervalSpy = jest.spyOn(global, 'setInterval');
-
-    service = new OIBusService(oibusEngine, historyQueryEngine, repositoryService, encryptionService, commandService, logger);
-    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
-    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
-
-    const result = service.getRegistrationSettings();
-    expect(result).toEqual(mockResult);
-    expect(repositoryService.registrationRepository.getRegistrationSettings).toHaveBeenCalledTimes(2);
-  });
-
-  it('should stop and clear interval', async () => {
-    const setIntervalSpy = jest.spyOn(global, 'setInterval');
-    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
-
-    service = new OIBusService(oibusEngine, historyQueryEngine, repositoryService, encryptionService, commandService, logger);
-    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
-    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
-    await service.stopOIBus();
-    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
-  });
-
-  it('should activate registration and clear interval', async () => {
-    const setIntervalSpy = jest.spyOn(global, 'setInterval');
-    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
-    service = new OIBusService(oibusEngine, historyQueryEngine, repositoryService, encryptionService, commandService, logger);
-    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
-    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
-    await service.activateRegistration('2020-20-20T00:00:00.000Z', 'token');
-    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
-  });
-
-  it('should unregister and clear interval', () => {
-    const setIntervalSpy = jest.spyOn(global, 'setInterval');
-    const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
-    service = new OIBusService(oibusEngine, historyQueryEngine, repositoryService, encryptionService, commandService, logger);
-    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
-    expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 10000);
-    service.unregister();
-    expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
-  });
-});
-
-describe('OIBus service should interact with OIA and', () => {
-  const mockResult: RegistrationSettingsDTO = {
-    id: 'id',
-    host: 'http://localhost:4200',
-    acceptUnauthorized: false,
-    useProxy: false,
-    token: 'token',
-    activationCode: '1234',
-    status: 'REGISTERED',
-    checkUrl: 'http://localhost:4200/check/url',
-    activationDate: '2020-20-20T00:00:00.000Z',
-    activationExpirationDate: ''
-  };
-
-  beforeEach(() => {
-    jest.clearAllMocks();
-    jest.useFakeTimers().setSystemTime(new Date(nowDateString));
-    (createProxyAgent as jest.Mock).mockReturnValue(undefined);
-    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue(mockResult);
-    service = new OIBusService(oibusEngine, historyQueryEngine, repositoryService, encryptionService, commandService, logger);
-    (getNetworkSettingsFromRegistration as jest.Mock).mockReturnValue({ host: 'http://localhost:4200', headers: {}, agent: undefined });
-  });
-
-  it('should ack commands and return if no commands in OIBus', async () => {
-    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([]);
-
-    await service.sendAckCommands();
-    expect(fetch).not.toHaveBeenCalled();
-  });
-
-  it('should ack commands', async () => {
-    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
-
-    const fetchResponse: Array<OIBusCommandDTO> = [{ id: 'id1' }] as Array<OIBusCommandDTO>;
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-
-    await service.sendAckCommands();
-    expect(logger.trace).toHaveBeenCalledWith(`1 commands acknowledged`);
-    expect(fetch).toHaveBeenCalledWith('http://localhost:4200/api/oianalytics/oibus/commands/status', {
-      method: 'PUT',
-      headers: {
-        'Content-Type': 'application/json'
-      },
-      body: JSON.stringify([command]),
-      timeout: 10000,
-      agent: undefined
-    });
-    expect(repositoryService.commandRepository.markAsAcknowledged).toHaveBeenCalledWith('id1');
-  });
-
-  it('should ack commands and manage 404 error', async () => {
-    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
-
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response('invalid', { status: 404 })));
-
-    await service.sendAckCommands();
-    expect(logger.error).toHaveBeenCalledWith(
-      `Error 404 while acknowledging 1 commands on http://localhost:4200/api/oianalytics/oibus/commands/status: Not Found`
-    );
-  });
-
-  it('should ack commands and log error on fetch error', async () => {
-    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
-
-    (fetch as unknown as jest.Mock).mockImplementation(() => {
-      throw new Error('error');
-    });
-
-    await service.sendAckCommands();
-    expect(logger.error).toHaveBeenCalledWith(
-      `Error while acknowledging 1 commands on http://localhost:4200/api/oianalytics/oibus/commands/status. ${new Error('error')}`
-    );
-  });
-
-  it('should check cancelled commands and return if no commands in OIBus', async () => {
-    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([]);
-
-    await service.checkRetrievedCommands();
-    expect(fetch).not.toHaveBeenCalled();
-  });
-
-  it('should check cancelled commands and no command retrieved from oia', async () => {
-    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
-
-    const fetchResponse: Array<OIBusCommandDTO> = [];
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-
-    await service.checkRetrievedCommands();
-    expect(logger.trace).toHaveBeenCalledWith(`No command cancelled among the 1 commands`);
-    expect(fetch).toHaveBeenCalledWith('http://localhost:4200/api/oianalytics/oibus/commands/list-by-ids?ids=id1', {
-      method: 'GET',
-      headers: {},
-      timeout: 10000,
-      agent: undefined
-    });
-  });
-
-  it('should check cancelled commands and cancel retrieved commands', async () => {
-    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
-
-    const fetchResponse: Array<OIBusCommandDTO> = [{ id: 'id1' }] as Array<OIBusCommandDTO>;
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-
-    await service.checkRetrievedCommands();
-    expect(logger.trace).toHaveBeenCalledWith(`1 commands cancelled among the 1 pending commands`);
-    expect(fetch).toHaveBeenCalledWith('http://localhost:4200/api/oianalytics/oibus/commands/list-by-ids?ids=id1', {
-      method: 'GET',
-      headers: {},
-      timeout: 10000,
-      agent: undefined
-    });
-    expect(repositoryService.commandRepository.cancel).toHaveBeenCalledWith('id1');
-  });
-
-  it('should check cancelled commands log error if fetch response not ok', async () => {
-    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
-
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response('invalid', { status: 404 })));
-
-    await service.checkRetrievedCommands();
-    expect(logger.error).toHaveBeenCalledWith(
-      `Error 404 while checking PENDING commands status on http://localhost:4200/api/oianalytics/oibus/commands/list-by-ids?ids=id1: Not Found`
-    );
-  });
-
-  it('should check cancelled commands log error on fetch error', async () => {
-    (repositoryService.commandRepository.searchCommandsList as jest.Mock).mockReturnValue([command]);
-
-    (fetch as unknown as jest.Mock).mockImplementation(() => {
-      throw new Error('error');
-    });
-
-    await service.checkRetrievedCommands();
-    expect(logger.error).toHaveBeenCalledWith(
-      `Error while checking PENDING commands status on http://localhost:4200/api/oianalytics/oibus/commands/list-by-ids?ids=id1. ${new Error(
-        'error'
-      )}`
-    );
-  });
-
-  it('should retrieve commands and trace logs if no command retrieved', async () => {
-    const fetchResponse: Array<OIBusCommandDTO> = [];
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-
-    await service.retrieveCommands();
-    expect(logger.trace).toHaveBeenCalledWith(`No command to create`);
-    expect(fetch).toHaveBeenCalledWith('http://localhost:4200/api/oianalytics/oibus/commands/pending', {
-      method: 'GET',
-      headers: {},
-      timeout: 10000,
-      agent: undefined
-    });
-  });
-
-  it('should retrieve and create commands', async () => {
-    const fetchResponse: Array<OIBusCommandDTO> = [command];
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response(JSON.stringify(fetchResponse))));
-
-    await service.retrieveCommands();
-    expect(logger.trace).toHaveBeenCalledWith(`1 commands to add`);
-    expect(fetch).toHaveBeenCalledWith('http://localhost:4200/api/oianalytics/oibus/commands/pending', {
-      method: 'GET',
-      headers: {},
-      timeout: 10000,
-      agent: undefined
-    });
-    expect(repositoryService.commandRepository.create).toHaveBeenCalledWith('id1', {
-      type: command.type,
-      version: command.version,
-      assetId: command.assetId
-    });
-  });
-
-  it('should retrieve log error on bad fetch response', async () => {
-    (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve(new Response('invalid', { status: 404 })));
-
-    await service.retrieveCommands();
-    expect(logger.error).toHaveBeenCalledWith(
-      `Error 404 while retrieving commands on http://localhost:4200/api/oianalytics/oibus/commands/pending: Not Found`
-    );
-  });
-
-  it('should retrieve log error on fetch error', async () => {
-    (fetch as unknown as jest.Mock).mockImplementation(() => {
-      throw new Error('error');
-    });
-
-    await service.retrieveCommands();
-    expect(logger.error).toHaveBeenCalledWith(
-      `Error while retrieving commands on http://localhost:4200/api/oianalytics/oibus/commands/pending. ${new Error('error')}`
-    );
+  it('should set logger', () => {
+    service.setLogger(logger);
+    expect(oibusEngine.setLogger).toHaveBeenCalledWith(logger);
+    expect(historyQueryEngine.setLogger).toHaveBeenCalledWith(logger);
   });
 });
diff --git a/backend/src/service/oibus.service.ts b/backend/src/service/oibus.service.ts
index f3c59c8884..39717226a2 100644
--- a/backend/src/service/oibus.service.ts
+++ b/backend/src/service/oibus.service.ts
@@ -1,41 +1,12 @@
-import fetch from 'node-fetch';
 import OIBusEngine from '../engine/oibus-engine';
 import HistoryQueryEngine from '../engine/history-query-engine';
-import { generateRandomId, getNetworkSettingsFromRegistration, getOIBusInfo } from './utils';
-import RepositoryService from './repository.service';
-import { RegistrationSettingsCommandDTO, RegistrationSettingsDTO } from '../../../shared/model/engine.model';
-import EncryptionService from './encryption.service';
 import pino from 'pino';
-import { Instant } from '../../../shared/model/types';
-import { DateTime } from 'luxon';
-import { createProxyAgent } from './proxy-agent';
-import { OIBusCommand, OIBusCommandDTO } from '../../../shared/model/command.model';
-import CommandService from './command.service';
-
-const CHECK_TIMEOUT = 10_000;
 
 export default class OIBusService {
-  private intervalCheckRegistration: NodeJS.Timeout | null = null;
-  private intervalCheckCommands: NodeJS.Timeout | null = null;
-  private ongoingCheckRegistration = false;
-  private ongoingCheckCommands = false;
-
   constructor(
     private engine: OIBusEngine,
-    private historyEngine: HistoryQueryEngine,
-    private repositoryService: RepositoryService,
-    private encryptionService: EncryptionService,
-    private commandService: CommandService,
-    private logger: pino.Logger
-  ) {
-    const registrationSettings = this.repositoryService.registrationRepository.getRegistrationSettings();
-    if (registrationSettings && registrationSettings.checkUrl && registrationSettings.status === 'PENDING') {
-      this.intervalCheckRegistration = setInterval(this.checkRegistration.bind(this), CHECK_TIMEOUT);
-    }
-    if (registrationSettings && registrationSettings.status === 'REGISTERED') {
-      this.intervalCheckCommands = setInterval(this.checkCommands.bind(this), CHECK_TIMEOUT);
-    }
-  }
+    private historyEngine: HistoryQueryEngine
+  ) {}
 
   async restartOIBus(): Promise<void> {
     await this.engine.stop();
@@ -47,15 +18,6 @@ export default class OIBusService {
   async stopOIBus(): Promise<void> {
     await this.engine.stop();
     await this.historyEngine.stop();
-    if (this.intervalCheckRegistration) {
-      clearInterval(this.intervalCheckRegistration);
-      this.intervalCheckRegistration = null;
-    }
-
-    if (this.intervalCheckCommands) {
-      clearInterval(this.intervalCheckCommands);
-      this.intervalCheckCommands = null;
-    }
   }
 
   async addValues(externalSourceId: string | null, values: Array<any>): Promise<void> {
@@ -66,284 +28,8 @@ export default class OIBusService {
     await this.engine.addExternalFile(externalSourceId, filePath);
   }
 
-  getRegistrationSettings(): RegistrationSettingsDTO | null {
-    return this.repositoryService.registrationRepository.getRegistrationSettings();
-  }
-
-  async updateRegistrationSettings(command: RegistrationSettingsCommandDTO): Promise<void> {
-    const activationCode = generateRandomId(6);
-    const registrationSettings = this.repositoryService.registrationRepository.getRegistrationSettings()!;
-    if (!registrationSettings) {
-      throw new Error(`Registration settings not found`);
-    }
-
-    if (!command.proxyPassword) {
-      command.proxyPassword = registrationSettings.proxyPassword;
-    } else {
-      command.proxyPassword = await this.encryptionService.encryptText(command.proxyPassword);
-    }
-
-    const engineSettings = this.repositoryService.engineRepository.getEngineSettings()!;
-
-    const oibusInfo = getOIBusInfo();
-    const body = {
-      activationCode,
-      oibusVersion: oibusInfo.version,
-      oibusArch: oibusInfo.architecture,
-      oibusOs: oibusInfo.operatingSystem,
-      oibusId: engineSettings.id,
-      oibusName: engineSettings.name
-    };
-    let response;
-    try {
-      const url = `${command.host}/api/oianalytics/oibus/registration`;
-      const agent = createProxyAgent(
-        command.useProxy,
-        url,
-        command.useProxy
-          ? {
-              url: command.proxyUrl!,
-              username: command.proxyUsername!,
-              password: command.proxyPassword ? await this.encryptionService.decryptText(command.proxyPassword) : null
-            }
-          : null,
-        command.acceptUnauthorized
-      );
-      response = await fetch(url, {
-        method: 'POST',
-        timeout: CHECK_TIMEOUT,
-        agent,
-        body: JSON.stringify(body),
-        headers: {
-          'Content-Type': 'application/json'
-        }
-      });
-    } catch (fetchError) {
-      throw new Error(`Registration failed: ${fetchError}`);
-    }
-
-    if (!response.ok) {
-      throw new Error(`Registration failed with status code ${response.status} and message: ${response.statusText}`);
-    }
-
-    const result: { redirectUrl: string; expirationDate: Instant } = await response.json();
-    this.repositoryService.registrationRepository.updateRegistration(command, activationCode, result.redirectUrl, result.expirationDate);
-    if (!this.intervalCheckRegistration) {
-      this.intervalCheckRegistration = setInterval(this.checkRegistration.bind(this), CHECK_TIMEOUT);
-    }
-  }
-
-  async activateRegistration(activationDate: string, accessToken: string): Promise<void> {
-    const encryptedToken = await this.encryptionService.encryptText(accessToken);
-    this.repositoryService.registrationRepository.activateRegistration(activationDate, encryptedToken);
-    if (this.intervalCheckRegistration) {
-      clearInterval(this.intervalCheckRegistration);
-      this.intervalCheckRegistration = null;
-    }
-
-    if (this.intervalCheckCommands) {
-      clearInterval(this.intervalCheckCommands);
-      this.intervalCheckCommands = null;
-    }
-    this.intervalCheckCommands = setInterval(this.checkCommands.bind(this), CHECK_TIMEOUT);
-  }
-
-  unregister() {
-    this.repositoryService.registrationRepository.unregister();
-    if (this.intervalCheckRegistration) {
-      clearInterval(this.intervalCheckRegistration);
-      this.intervalCheckRegistration = null;
-    }
-
-    if (this.intervalCheckCommands) {
-      clearInterval(this.intervalCheckCommands);
-      this.intervalCheckCommands = null;
-    }
-  }
-
-  async checkRegistration(): Promise<void> {
-    if (this.ongoingCheckRegistration) {
-      this.logger.trace(`On going registration check`);
-      return;
-    }
-    this.logger.trace(`Registration check`);
-    const registrationSettings = this.repositoryService.registrationRepository.getRegistrationSettings();
-    if (!registrationSettings || !registrationSettings.checkUrl) {
-      this.logger.error(`Error while checking registration status: Could not retrieve check URL`);
-      return;
-    }
-    let response;
-    const url = `${registrationSettings.host}${registrationSettings.checkUrl}`;
-    try {
-      this.ongoingCheckRegistration = true;
-      const agent = createProxyAgent(
-        registrationSettings.useProxy,
-        url,
-        registrationSettings.useProxy
-          ? {
-              url: registrationSettings.proxyUrl!,
-              username: registrationSettings.proxyUsername!,
-              password: registrationSettings.proxyPassword
-                ? await this.encryptionService.decryptText(registrationSettings.proxyPassword)
-                : null
-            }
-          : null,
-        registrationSettings.acceptUnauthorized
-      );
-      response = await fetch(url, {
-        method: 'GET',
-        timeout: CHECK_TIMEOUT,
-        agent
-      });
-      if (!response.ok) {
-        this.logger.error(`Error ${response.status} while checking registration status on ${url}: ${response.statusText}`);
-        this.ongoingCheckRegistration = false;
-        return;
-      }
-
-      const responseData: { status: string; expired: boolean; accessToken: string } = await response.json();
-      if (responseData.status !== 'COMPLETED') {
-        this.logger.warn(`Registration not completed. Status: ${responseData.status}`);
-      } else {
-        await this.activateRegistration(DateTime.now().toUTC().toISO()!, responseData.accessToken);
-        this.logger.info(`OIBus registered on ${registrationSettings.host}`);
-      }
-    } catch (fetchError) {
-      this.logger.error(`Error while checking registration status on ${url}. ${fetchError}`);
-    }
-    this.ongoingCheckRegistration = false;
-  }
-
-  async checkCommands(): Promise<void> {
-    if (this.ongoingCheckCommands) {
-      this.logger.trace(`On going commands check`);
-      return;
-    }
-    this.ongoingCheckCommands = true;
-
-    await this.sendAckCommands();
-    await this.checkRetrievedCommands();
-    await this.retrieveCommands();
-    this.ongoingCheckCommands = false;
-  }
-
-  async sendAckCommands(): Promise<void> {
-    const commandsToAck = this.repositoryService.commandRepository.searchCommandsList({
-      status: [],
-      types: [],
-      ack: false
-    });
-    if (commandsToAck.length === 0) {
-      return;
-    }
-
-    const endpoint = `/api/oianalytics/oibus/commands/status`;
-    const registrationSettings = this.getRegistrationSettings();
-    const connectionSettings = await getNetworkSettingsFromRegistration(registrationSettings, endpoint, this.encryptionService);
-    let response;
-    const url = `${connectionSettings.host}${endpoint}`;
-    try {
-      response = await fetch(url, {
-        method: 'PUT',
-        body: JSON.stringify(commandsToAck),
-        headers: { ...connectionSettings.headers, 'Content-Type': 'application/json' },
-        timeout: CHECK_TIMEOUT,
-        agent: connectionSettings.agent
-      });
-      if (!response.ok) {
-        this.logger.error(
-          `Error ${response.status} while acknowledging ${commandsToAck.length} commands on ${url}: ${response.statusText}`
-        );
-        return;
-      }
-      for (const command of commandsToAck) {
-        this.repositoryService.commandRepository.markAsAcknowledged(command.id);
-      }
-      this.logger.trace(`${commandsToAck.length} commands acknowledged`);
-    } catch (fetchError) {
-      this.logger.error(`Error while acknowledging ${commandsToAck.length} commands on ${url}. ${fetchError}`);
-    }
-  }
-
-  /**
-   * Check if retrieved commands have been cancelled on OIAnalytics before running them
-   */
-  async checkRetrievedCommands(): Promise<void> {
-    const pendingCommands = this.repositoryService.commandRepository.searchCommandsList({ status: ['RETRIEVED'], types: [] });
-    if (pendingCommands.length === 0) {
-      return;
-    }
-
-    let endpoint = `/api/oianalytics/oibus/commands/list-by-ids?`;
-    for (const command of pendingCommands) {
-      endpoint += `ids=${command.id}&`;
-    }
-    endpoint = endpoint.slice(0, endpoint.length - 1);
-    const registrationSettings = this.getRegistrationSettings();
-    const connectionSettings = await getNetworkSettingsFromRegistration(registrationSettings, endpoint, this.encryptionService);
-    let response;
-    const url = `${connectionSettings.host}${endpoint}`;
-    try {
-      response = await fetch(url, {
-        method: 'GET',
-        headers: connectionSettings.headers,
-        timeout: CHECK_TIMEOUT,
-        agent: connectionSettings.agent
-      });
-      if (!response.ok) {
-        this.logger.error(`Error ${response.status} while checking PENDING commands status on ${url}: ${response.statusText}`);
-        return;
-      }
-      const commandsToCancel: Array<OIBusCommandDTO> = await response.json();
-      if (commandsToCancel.length === 0) {
-        this.logger.trace(`No command cancelled among the ${pendingCommands.length} commands`);
-        return;
-      }
-      this.logger.trace(`${commandsToCancel.length} commands cancelled among the ${pendingCommands.length} pending commands`);
-      for (const command of commandsToCancel) {
-        this.commandService.removeCommandFromQueue(command.id);
-        this.repositoryService.commandRepository.cancel(command.id);
-      }
-    } catch (fetchError) {
-      this.logger.error(`Error while checking PENDING commands status on ${url}. ${fetchError}`);
-    }
-  }
-
-  async retrieveCommands(): Promise<void> {
-    const endpoint = `/api/oianalytics/oibus/commands/pending`;
-    const registrationSettings = this.getRegistrationSettings();
-    const connectionSettings = await getNetworkSettingsFromRegistration(registrationSettings, endpoint, this.encryptionService);
-    let response;
-    const url = `${connectionSettings.host}${endpoint}`;
-    try {
-      response = await fetch(url, {
-        method: 'GET',
-        headers: connectionSettings.headers,
-        timeout: CHECK_TIMEOUT,
-        agent: connectionSettings.agent
-      });
-      if (!response.ok) {
-        this.logger.error(`Error ${response.status} while retrieving commands on ${url}: ${response.statusText}`);
-        return;
-      }
-      const newCommands: Array<OIBusCommandDTO> = await response.json();
-      if (newCommands.length === 0) {
-        this.logger.trace(`No command to create`);
-        return;
-      }
-      this.logger.trace(`${newCommands.length} commands to add`);
-      for (const command of newCommands) {
-        const creationCommand: OIBusCommand = {
-          type: command.type,
-          version: command.version,
-          assetId: command.assetId
-        };
-        const newCommand = this.repositoryService.commandRepository.create(command.id, creationCommand);
-        this.commandService.addCommandToQueue(newCommand);
-      }
-      await this.sendAckCommands();
-    } catch (fetchError) {
-      this.logger.error(`Error while retrieving commands on ${url}. ${fetchError}`);
-    }
+  setLogger(logger: pino.Logger) {
+    this.engine.setLogger(logger);
+    this.historyEngine.setLogger(logger);
   }
 }
diff --git a/backend/src/service/reload.service.spec.ts b/backend/src/service/reload.service.spec.ts
index 7acd01fb35..17b004612b 100644
--- a/backend/src/service/reload.service.spec.ts
+++ b/backend/src/service/reload.service.spec.ts
@@ -13,7 +13,7 @@ import LoggerService from './logger/logger.service';
 import EngineMetricsService from './engine-metrics.service';
 import NorthService from './north.service';
 import OIBusEngine from '../engine/oibus-engine';
-import { EngineSettingsDTO, LogSettings } from '../../../shared/model/engine.model';
+import { EngineSettingsDTO, LogSettings, RegistrationSettingsDTO } from '../../../shared/model/engine.model';
 import {
   SouthConnectorItemCommandDTO,
   SouthConnectorItemDTO,
@@ -29,6 +29,8 @@ import HomeMetricsService from './home-metrics.service';
 import HomeMetricsServiceMock from '../tests/__mocks__/home-metrics-service.mock';
 import ProxyServer from '../web-server/proxy-server';
 import ProxyServerMock from '../tests/__mocks__/proxy-server.mock';
+import OIBusService from './oibus.service';
+import OibusServiceMock from '../tests/__mocks__/oibus-service.mock';
 
 jest.mock('./encryption.service');
 jest.mock('./logger/logger.service');
@@ -43,12 +45,16 @@ const engineMetricsService: EngineMetricsService = new EngineMetricsServiceMock(
 const homeMetrics: HomeMetricsService = new HomeMetricsServiceMock();
 const northService: NorthService = new NorthServiceMock();
 const southService: SouthService = new SouthServiceMock();
+const oibusService: OIBusService = new OibusServiceMock();
 const loggerService: LoggerService = new LoggerService(encryptionService, 'folder');
 
 let service: ReloadService;
 describe('reload service', () => {
   beforeEach(() => {
     jest.clearAllMocks();
+    (repositoryService.registrationRepository.getRegistrationSettings as jest.Mock).mockReturnValue({
+      id: 'id1'
+    } as RegistrationSettingsDTO);
     service = new ReloadService(
       loggerService,
       repositoryService,
@@ -58,6 +64,7 @@ describe('reload service', () => {
       southService,
       oibusEngine,
       historyQueryEngine,
+      oibusService,
       proxyServer
     );
   });
@@ -70,6 +77,8 @@ describe('reload service', () => {
     expect(service.northService).toBeDefined();
     expect(service.southService).toBeDefined();
     expect(service.oibusEngine).toBeDefined();
+    expect(service.oibusService).toBeDefined();
+    expect(service.proxyServer).toBeDefined();
   });
 
   it('should update port', async () => {
@@ -100,7 +109,7 @@ describe('reload service', () => {
     service.setWebServerChangeLogger(changeLoggerFn);
     await service.onUpdateOibusSettings(null, newSettings as EngineSettingsDTO);
     expect(loggerService.stop).toHaveBeenCalledTimes(1);
-    expect(loggerService.start).toHaveBeenCalledWith(newSettings.id, newSettings.name, newSettings.logParameters);
+    expect(loggerService.start).toHaveBeenCalledWith(newSettings.id, newSettings.name, newSettings.logParameters, { id: 'id1' });
     expect(changeLoggerFn).toHaveBeenCalledTimes(1);
     expect(engineMetricsService.setLogger).toHaveBeenCalledTimes(1);
   });
diff --git a/backend/src/service/reload.service.ts b/backend/src/service/reload.service.ts
index 680a41f8dc..dd02e31852 100644
--- a/backend/src/service/reload.service.ts
+++ b/backend/src/service/reload.service.ts
@@ -20,6 +20,7 @@ import { Instant } from '../../../shared/model/types';
 import { ScanModeCommandDTO } from '../../../shared/model/scan-mode.model';
 import HomeMetricsService from './home-metrics.service';
 import ProxyServer from '../web-server/proxy-server';
+import OIBusService from './oibus.service';
 
 export default class ReloadService {
   private webServerChangeLoggerCallback: (logger: pino.Logger) => void = () => {};
@@ -34,6 +35,7 @@ export default class ReloadService {
     private readonly _southService: SouthService,
     private readonly _oibusEngine: OIBusEngine,
     private readonly _historyEngine: HistoryQueryEngine,
+    private readonly _oibusService: OIBusService,
     private readonly _proxyServer: ProxyServer
   ) {}
 
@@ -69,6 +71,10 @@ export default class ReloadService {
     return this._historyEngine;
   }
 
+  get oibusService(): OIBusService {
+    return this._oibusService;
+  }
+
   get proxyServer(): ProxyServer {
     return this._proxyServer;
   }
@@ -87,12 +93,7 @@ export default class ReloadService {
       JSON.stringify(oldSettings.logParameters) !== JSON.stringify(newSettings.logParameters) ||
       oldSettings.name !== newSettings.name
     ) {
-      this.loggerService.stop();
-      await this.loggerService.start(newSettings.id, newSettings.name, newSettings.logParameters);
-      this.webServerChangeLoggerCallback(this.loggerService.createChildLogger('web-server'));
-      this.engineMetricsService.setLogger(this.loggerService.createChildLogger('internal'));
-      this.oibusEngine.setLogger(this.loggerService.createChildLogger('internal'));
-      this.proxyServer.setLogger(this.loggerService.createChildLogger('internal'));
+      await this.restartLogger(newSettings);
     }
     if (!oldSettings || oldSettings.port !== newSettings.port) {
       await this.webServerChangePortCallback(newSettings.port);
@@ -105,6 +106,16 @@ export default class ReloadService {
     }
   }
 
+  public async restartLogger(newSettings: EngineSettingsDTO) {
+    this.loggerService.stop();
+    const registration = this.repositoryService.registrationRepository.getRegistrationSettings()!;
+    await this.loggerService.start(newSettings.id, newSettings.name, newSettings.logParameters, registration);
+    this.webServerChangeLoggerCallback(this.loggerService.createChildLogger('web-server'));
+    this.oibusService.setLogger(this.loggerService.createChildLogger('internal'));
+    this.engineMetricsService.setLogger(this.loggerService.createChildLogger('internal'));
+    this.proxyServer.setLogger(this.loggerService.createChildLogger('internal'));
+  }
+
   async onCreateSouth(command: SouthConnectorCommandDTO): Promise<SouthConnectorDTO> {
     const southConnector = this.repositoryService.southConnectorRepository.createSouthConnector(command);
     if (command.enabled) {
diff --git a/backend/src/tests/__mocks__/koa-context.mock.ts b/backend/src/tests/__mocks__/koa-context.mock.ts
index 04ba0017f1..6a528a584f 100644
--- a/backend/src/tests/__mocks__/koa-context.mock.ts
+++ b/backend/src/tests/__mocks__/koa-context.mock.ts
@@ -5,6 +5,7 @@ import NorthServiceMock from './north-service.mock';
 import SouthServiceMock from './south-service.mock';
 import OIBusServiceMock from './oibus-service.mock';
 import EngineMetricsServiceMock from './engine-metrics-service.mock';
+import RegistrationServiceMock from './registration-service.mock';
 
 /**
  * Create a mock object for Koa Context
@@ -17,6 +18,7 @@ export default jest.fn().mockImplementation(() => ({
     northService: new NorthServiceMock(),
     southService: new SouthServiceMock(),
     oibusService: new OIBusServiceMock(),
+    registrationService: new RegistrationServiceMock(),
     engineMetricsService: new EngineMetricsServiceMock(),
     logger: {
       trace: jest.fn(),
diff --git a/backend/src/tests/__mocks__/oibus-service.mock.ts b/backend/src/tests/__mocks__/oibus-service.mock.ts
index 4dbd60e95f..120e719bad 100644
--- a/backend/src/tests/__mocks__/oibus-service.mock.ts
+++ b/backend/src/tests/__mocks__/oibus-service.mock.ts
@@ -2,14 +2,9 @@
  * Create a mock object for OIBus Service
  */
 export default jest.fn().mockImplementation(() => ({
-  getOIBusInfo: jest.fn(),
-  stopOIBus: jest.fn(),
-  restartOIBus: jest.fn(),
-  addValues: jest.fn(),
+  setLogger: jest.fn(),
   addFile: jest.fn(),
-  checkForUpdate: jest.fn(),
-  downloadUpdate: jest.fn(),
-  getRegistrationSettings: jest.fn(),
-  updateRegistrationSettings: jest.fn(),
-  unregister: jest.fn()
+  addValues: jest.fn(),
+  stopOIBus: jest.fn(),
+  restartOIBus: jest.fn()
 }));
diff --git a/backend/src/tests/__mocks__/registration-service.mock.ts b/backend/src/tests/__mocks__/registration-service.mock.ts
new file mode 100644
index 0000000000..4656147d30
--- /dev/null
+++ b/backend/src/tests/__mocks__/registration-service.mock.ts
@@ -0,0 +1,7 @@
+/**
+ * Create a mock object for Registration Service
+ */
+export default jest.fn().mockImplementation(() => ({
+  onUnregister: jest.fn(),
+  updateRegistrationSettings: jest.fn()
+}));
diff --git a/backend/src/tests/__mocks__/reload-service.mock.ts b/backend/src/tests/__mocks__/reload-service.mock.ts
index a48518920e..f6f11b13c1 100644
--- a/backend/src/tests/__mocks__/reload-service.mock.ts
+++ b/backend/src/tests/__mocks__/reload-service.mock.ts
@@ -38,6 +38,7 @@ export default jest.fn().mockImplementation(() => ({
   onDeleteNorthSubscription: jest.fn(),
   onDeleteExternalNorthSubscription: jest.fn(),
   onUpdateScanMode: jest.fn(),
+  restartLogger: jest.fn(),
   oibusEngine: {
     resetSouthMetrics: jest.fn(),
     resetNorthMetrics: jest.fn(),
diff --git a/backend/src/web-server/controllers/registration.controller.spec.ts b/backend/src/web-server/controllers/registration.controller.spec.ts
index 255c309f3f..c8650f457a 100644
--- a/backend/src/web-server/controllers/registration.controller.spec.ts
+++ b/backend/src/web-server/controllers/registration.controller.spec.ts
@@ -39,20 +39,20 @@ describe('Registration controller', () => {
   });
 
   it('getRegistrationSettings() should return registration settings', async () => {
-    ctx.app.oibusService.getRegistrationSettings.mockReturnValue(registrationSettings);
+    ctx.app.repositoryService.registrationRepository.getRegistrationSettings.mockReturnValue(registrationSettings);
 
     await registrationController.getRegistrationSettings(ctx);
 
-    expect(ctx.app.oibusService.getRegistrationSettings).toHaveBeenCalled();
+    expect(ctx.app.repositoryService.registrationRepository.getRegistrationSettings).toHaveBeenCalled();
     expect(ctx.ok).toHaveBeenCalledWith(registrationSettings);
   });
 
   it('getRegistrationSettings() should return not found', async () => {
-    ctx.app.oibusService.getRegistrationSettings.mockReturnValue(null);
+    ctx.app.repositoryService.registrationRepository.getRegistrationSettings.mockReturnValue(null);
 
     await registrationController.getRegistrationSettings(ctx);
 
-    expect(ctx.app.oibusService.getRegistrationSettings).toHaveBeenCalled();
+    expect(ctx.app.repositoryService.registrationRepository.getRegistrationSettings).toHaveBeenCalled();
     expect(ctx.notFound).toHaveBeenCalledWith();
   });
 
@@ -62,7 +62,7 @@ describe('Registration controller', () => {
     await registrationController.updateRegistrationSettings(ctx);
 
     expect(validator.validate).toHaveBeenCalledWith(schema, registrationCommand);
-    expect(ctx.app.oibusService.updateRegistrationSettings).toHaveBeenCalledWith(registrationCommand);
+    expect(ctx.app.registrationService.updateRegistrationSettings).toHaveBeenCalledWith(registrationCommand);
     expect(ctx.noContent).toHaveBeenCalled();
   });
 
@@ -76,14 +76,14 @@ describe('Registration controller', () => {
     await registrationController.updateRegistrationSettings(ctx);
 
     expect(validator.validate).toHaveBeenCalledWith(schema, registrationCommand);
-    expect(ctx.app.oibusService.updateRegistrationSettings).not.toHaveBeenCalledWith();
+    expect(ctx.app.registrationService.updateRegistrationSettings).not.toHaveBeenCalledWith();
     expect(ctx.badRequest).toHaveBeenCalledWith(validationError.message);
   });
 
   it('unregister() should call unregister from oibus service', async () => {
     await registrationController.unregister(ctx);
 
-    expect(ctx.app.oibusService.unregister).toHaveBeenCalledTimes(1);
+    expect(ctx.app.registrationService.onUnregister).toHaveBeenCalledTimes(1);
     expect(ctx.noContent).toHaveBeenCalled();
   });
 });
diff --git a/backend/src/web-server/controllers/registration.controller.ts b/backend/src/web-server/controllers/registration.controller.ts
index fc82453174..a08ed11a1b 100644
--- a/backend/src/web-server/controllers/registration.controller.ts
+++ b/backend/src/web-server/controllers/registration.controller.ts
@@ -4,7 +4,7 @@ import AbstractController from './abstract.controller';
 
 export default class RegistrationController extends AbstractController {
   async getRegistrationSettings(ctx: KoaContext<void, RegistrationSettingsDTO>): Promise<RegistrationSettingsDTO> {
-    const registrationSettings = ctx.app.oibusService.getRegistrationSettings();
+    const registrationSettings = ctx.app.repositoryService.registrationRepository.getRegistrationSettings();
     if (!registrationSettings) {
       return ctx.notFound();
     }
@@ -17,15 +17,15 @@ export default class RegistrationController extends AbstractController {
     try {
       await this.validate(ctx.request.body);
       const command = ctx.request.body as RegistrationSettingsCommandDTO;
-      await ctx.app.oibusService.updateRegistrationSettings(command);
+      await ctx.app.registrationService.updateRegistrationSettings(command);
       return ctx.noContent();
     } catch (error: any) {
       ctx.badRequest(error.message);
     }
   }
 
-  unregister(ctx: KoaContext<any, any>) {
-    ctx.app.oibusService.unregister();
+  async unregister(ctx: KoaContext<any, any>) {
+    await ctx.app.registrationService.onUnregister();
     return ctx.noContent();
   }
 }
diff --git a/backend/src/web-server/controllers/validators/engine.validator.spec.ts b/backend/src/web-server/controllers/validators/engine.validator.spec.ts
index 398a447e84..4720a57786 100644
--- a/backend/src/web-server/controllers/validators/engine.validator.spec.ts
+++ b/backend/src/web-server/controllers/validators/engine.validator.spec.ts
@@ -30,12 +30,13 @@ const dataProviders: DataProvider[] = [
         console: null,
         file: null,
         database: null,
-        loki: null
+        loki: null,
+        oia: null
       }
     },
     isValid: false,
     errorMessage:
-      '"logParameters.console" must be of type object. "logParameters.file" must be of type object. "logParameters.database" must be of type object. "logParameters.loki" must be of type object'
+      '"logParameters.console" must be of type object. "logParameters.file" must be of type object. "logParameters.database" must be of type object. "logParameters.loki" must be of type object. "logParameters.oia" must be of type object'
   },
   {
     dto: {
@@ -60,15 +61,18 @@ const dataProviders: DataProvider[] = [
           level: null,
           interval: null,
           address: null,
-          tokenAddress: null,
           username: null,
           password: null
+        },
+        oia: {
+          level: null,
+          interval: null
         }
       }
     },
     isValid: false,
     errorMessage:
-      '"name" must be a string. "port" must be a number. "proxyEnabled" must be a boolean. "proxyPort" must be a number. "logParameters.console.level" must be a string. "logParameters.file.level" must be a string. "logParameters.file.maxFileSize" must be a number. "logParameters.file.numberOfFiles" must be a number. "logParameters.database.level" must be a string. "logParameters.database.maxNumberOfLogs" must be a number. "logParameters.loki.level" must be a string. "logParameters.loki.interval" must be a number. "logParameters.loki.address" must be a string. "logParameters.loki.tokenAddress" must be a string. "logParameters.loki.username" must be a string. "logParameters.loki.password" must be a string'
+      '"name" must be a string. "port" must be a number. "proxyEnabled" must be a boolean. "proxyPort" must be a number. "logParameters.console.level" must be a string. "logParameters.file.level" must be a string. "logParameters.file.maxFileSize" must be a number. "logParameters.file.numberOfFiles" must be a number. "logParameters.database.level" must be a string. "logParameters.database.maxNumberOfLogs" must be a number. "logParameters.loki.level" must be a string. "logParameters.loki.interval" must be a number. "logParameters.loki.address" must be a string. "logParameters.loki.username" must be a string. "logParameters.loki.password" must be a string. "logParameters.oia.level" must be a string. "logParameters.oia.interval" must be a number'
   },
   {
     dto: {
@@ -93,15 +97,18 @@ const dataProviders: DataProvider[] = [
           level: '',
           interval: '',
           address: '',
-          tokenAddress: '',
           username: '',
           password: ''
+        },
+        oia: {
+          level: '',
+          interval: ''
         }
       }
     },
     isValid: false,
     errorMessage:
-      '"name" is not allowed to be empty. "port" must be a number. "proxyEnabled" must be a boolean. "proxyPort" must be a number. "logParameters.console.level" is not allowed to be empty. "logParameters.file.level" is not allowed to be empty. "logParameters.file.maxFileSize" must be a number. "logParameters.file.numberOfFiles" must be a number. "logParameters.database.level" is not allowed to be empty. "logParameters.database.maxNumberOfLogs" must be a number. "logParameters.loki.level" is not allowed to be empty. "logParameters.loki.interval" must be a number'
+      '"name" is not allowed to be empty. "port" must be a number. "proxyEnabled" must be a boolean. "proxyPort" must be a number. "logParameters.console.level" is not allowed to be empty. "logParameters.file.level" is not allowed to be empty. "logParameters.file.maxFileSize" must be a number. "logParameters.file.numberOfFiles" must be a number. "logParameters.database.level" is not allowed to be empty. "logParameters.database.maxNumberOfLogs" must be a number. "logParameters.loki.level" is not allowed to be empty. "logParameters.loki.interval" must be a number. "logParameters.oia.level" is not allowed to be empty. "logParameters.oia.interval" must be a number'
   },
   {
     dto: {
@@ -126,9 +133,12 @@ const dataProviders: DataProvider[] = [
           level: 'silent',
           interval: 60,
           address: '',
-          tokenAddress: '',
           username: '',
           password: ''
+        },
+        oia: {
+          level: 'silent',
+          interval: 60
         }
       }
     },
diff --git a/backend/src/web-server/controllers/validators/oibus-validation-schema.ts b/backend/src/web-server/controllers/validators/oibus-validation-schema.ts
index 550f08eb87..3feb91ef28 100644
--- a/backend/src/web-server/controllers/validators/oibus-validation-schema.ts
+++ b/backend/src/web-server/controllers/validators/oibus-validation-schema.ts
@@ -51,12 +51,12 @@ const engineSchema: Joi.ObjectSchema = Joi.object({
       level: Joi.string().required().allow('silent', 'error', 'warning', 'info', 'debug', 'trace'),
       interval: Joi.number().integer().required().min(10),
       address: Joi.string().required().allow(''),
-      tokenAddress: Joi.string().required().allow(''),
       username: Joi.string().required().allow(''),
       password: Joi.string().required().allow('')
     }),
     oia: Joi.object({
-      level: Joi.string().required().allow('silent', 'error', 'warning', 'info', 'debug', 'trace')
+      level: Joi.string().required().allow('silent', 'error', 'warning', 'info', 'debug', 'trace'),
+      interval: Joi.number().integer().required().min(10)
     })
   })
 });
diff --git a/backend/src/web-server/koa.ts b/backend/src/web-server/koa.ts
index 244a6e61a4..d957e90a0a 100644
--- a/backend/src/web-server/koa.ts
+++ b/backend/src/web-server/koa.ts
@@ -7,6 +7,7 @@ import SouthService from '../service/south.service';
 import OIBusService from '../service/oibus.service';
 import NorthService from '../service/north.service';
 import EngineMetricsService from '../service/engine-metrics.service';
+import RegistrationService from '../service/oia/registration.service';
 
 interface KoaRequest<RequestBody> extends Request {
   body?: RequestBody;
@@ -19,6 +20,7 @@ export interface KoaApplication extends Koa {
   southService: SouthService;
   northService: NorthService;
   oibusService: OIBusService;
+  registrationService: RegistrationService;
   engineMetricsService: EngineMetricsService;
   reloadService: ReloadService;
   encryptionService: EncryptionService;
diff --git a/backend/src/web-server/middlewares/oibus.ts b/backend/src/web-server/middlewares/oibus.ts
index 0a3690061c..fae1ee8d5b 100644
--- a/backend/src/web-server/middlewares/oibus.ts
+++ b/backend/src/web-server/middlewares/oibus.ts
@@ -7,6 +7,7 @@ import OIBusService from '../../service/oibus.service';
 import NorthService from '../../service/north.service';
 import SouthService from '../../service/south.service';
 import EngineMetricsService from '../../service/engine-metrics.service';
+import RegistrationService from '../../service/oia/registration.service';
 
 /**
  * OIBus middleware for Koa
@@ -15,6 +16,7 @@ const oibus = (
   id: string,
   repositoryService: RepositoryService,
   reloadService: ReloadService,
+  registrationService: RegistrationService,
   encryptionService: EncryptionService,
   southService: SouthService,
   northService: NorthService,
@@ -27,6 +29,7 @@ const oibus = (
     ctx.app.id = id;
     ctx.app.repositoryService = repositoryService;
     ctx.app.reloadService = reloadService;
+    ctx.app.registrationService = registrationService;
     ctx.app.encryptionService = encryptionService;
     ctx.app.southService = southService;
     ctx.app.northService = northService;
diff --git a/backend/src/web-server/web-server.ts b/backend/src/web-server/web-server.ts
index 9fc5d9f98c..48dbfe9adc 100644
--- a/backend/src/web-server/web-server.ts
+++ b/backend/src/web-server/web-server.ts
@@ -22,6 +22,7 @@ import SouthService from '../service/south.service';
 import OIBusService from '../service/oibus.service';
 import NorthService from '../service/north.service';
 import EngineMetricsService from '../service/engine-metrics.service';
+import RegistrationService from '../service/oia/registration.service';
 
 /**
  * Class Server - Provides the web client and establish socket connections.
@@ -38,6 +39,7 @@ export default class WebServer {
     port: number,
     private readonly encryptionService: EncryptionService,
     private readonly reloadService: ReloadService,
+    private readonly registrationService: RegistrationService,
     private readonly repositoryService: RepositoryService,
     private readonly southService: SouthService,
     private readonly northService: NorthService,
@@ -88,6 +90,7 @@ export default class WebServer {
         this._id,
         this.repositoryService,
         this.reloadService,
+        this.registrationService,
         this.encryptionService,
         this.southService,
         this.northService,
diff --git a/backend/tsconfig.app.json b/backend/tsconfig.app.json
index 70d25a0f84..3b0d5fd3a4 100644
--- a/backend/tsconfig.app.json
+++ b/backend/tsconfig.app.json
@@ -6,7 +6,7 @@
   "files": [
     "src/index.ts",
     "src/service/logger/sqlite-transport.ts",
-    "src/service/logger/loki-transport.ts",
+    "src/service/logger/oianalytics-transport.ts",
   ],
   "include": [
     "src/db/**/*.ts",
diff --git a/frontend/src/app/engine/edit-engine/edit-engine.component.html b/frontend/src/app/engine/edit-engine/edit-engine.component.html
index 9d09a7ff55..f455b19865 100644
--- a/frontend/src/app/engine/edit-engine/edit-engine.component.html
+++ b/frontend/src/app/engine/edit-engine/edit-engine.component.html
@@ -152,21 +152,13 @@ <h1 translate="engine.edit-title"></h1>
 
                 <div class="row">
                   <!-- Address -->
-                  <div class="col-4">
+                  <div class="col-6">
                     <div class="form-group">
                       <label class="form-label" for="loki-address" translate="engine.logger.loki.address"></label>
                       <input formControlName="address" id="loki-address" class="form-control" />
                       <val-errors controlName="address"></val-errors>
                     </div>
                   </div>
-                  <!-- Token address -->
-                  <div class="col-4">
-                    <div class="form-group">
-                      <label class="form-label" for="loki-token-address" translate="engine.logger.loki.token-address"></label>
-                      <input formControlName="tokenAddress" id="loki-token-address" class="form-control" />
-                      <val-errors controlName="tokenAddress"></val-errors>
-                    </div>
-                  </div>
                 </div>
 
                 <div class="row">
@@ -199,6 +191,18 @@ <h1 translate="engine.edit-title"></h1>
                       </select>
                     </div>
                   </div>
+
+                  <!-- Interval -->
+                  <div class="col-4">
+                    <div class="form-group">
+                      <label class="form-label" for="oia-interval" translate="engine.logger.oia.interval"></label>
+                      <div class="input-group">
+                        <input type="number" formControlName="interval" id="oia-interval" class="form-control" />
+                        <span class="input-group-text" translate="common.unit.s"></span>
+                      </div>
+                      <val-errors controlName="interval"></val-errors>
+                    </div>
+                  </div>
                 </div>
               </ng-container>
             </div>
diff --git a/frontend/src/app/engine/edit-engine/edit-engine.component.spec.ts b/frontend/src/app/engine/edit-engine/edit-engine.component.spec.ts
index fd8d2528be..a9b709541b 100644
--- a/frontend/src/app/engine/edit-engine/edit-engine.component.spec.ts
+++ b/frontend/src/app/engine/edit-engine/edit-engine.component.spec.ts
@@ -71,14 +71,6 @@ class EditEngineComponentTester extends ComponentTester<EditEngineComponent> {
     return this.input('#loki-address');
   }
 
-  get lokiProxy() {
-    return this.select('#loki-proxy');
-  }
-
-  get lokiTokenAddress() {
-    return this.input('#loki-token-address');
-  }
-
   get lokiUsername() {
     return this.input('#loki-username');
   }
@@ -91,6 +83,10 @@ class EditEngineComponentTester extends ComponentTester<EditEngineComponent> {
     return this.select('#oia-level')!;
   }
 
+  get oiaInterval() {
+    return this.input('#oia-interval');
+  }
+
   get submitButton() {
     return this.button('#save-button')!;
   }
@@ -124,12 +120,12 @@ describe('EditEngineComponent', () => {
         level: 'error',
         interval: 60,
         address: 'http://loki.oibus.com',
-        tokenAddress: 'http://token-address.oibus.com',
         username: 'oibus',
         password: 'pass'
       },
       oia: {
-        level: 'silent'
+        level: 'silent',
+        interval: 60
       }
     }
   };
@@ -171,11 +167,10 @@ describe('EditEngineComponent', () => {
     expect(tester.lokiLevel).toHaveSelectedLabel('Error');
     expect(tester.lokiInterval).toHaveValue(engineSettings.logParameters.loki.interval.toString());
     expect(tester.lokiAddress).toHaveValue(engineSettings.logParameters.loki.address);
-    expect(tester.lokiTokenAddress).toHaveValue(engineSettings.logParameters.loki.tokenAddress);
-    expect(tester.lokiAddress).toHaveValue(engineSettings.logParameters.loki.address);
     expect(tester.lokiUsername).toHaveValue(engineSettings.logParameters.loki.username);
     expect(tester.lokiPassword).toHaveValue(engineSettings.logParameters.loki.password);
     expect(tester.oiaLevel).toHaveSelectedLabel('Silent');
+    expect(tester.oiaInterval).toHaveValue(engineSettings.logParameters.oia.interval.toString());
   });
 
   it('should update engine settings', () => {
@@ -219,12 +214,12 @@ describe('EditEngineComponent', () => {
           level: 'silent',
           interval: 60,
           address: 'http://loki.oibus.com',
-          tokenAddress: 'http://token-address.oibus.com',
           username: 'oibus',
           password: 'pass'
         },
         oia: {
-          level: 'error'
+          level: 'error',
+          interval: 60
         }
       }
     });
diff --git a/frontend/src/app/engine/edit-engine/edit-engine.component.ts b/frontend/src/app/engine/edit-engine/edit-engine.component.ts
index 5a5de4940d..3dcc5aca1f 100644
--- a/frontend/src/app/engine/edit-engine/edit-engine.component.ts
+++ b/frontend/src/app/engine/edit-engine/edit-engine.component.ts
@@ -43,12 +43,12 @@ export class EditEngineComponent implements OnInit {
         level: ['silent' as LogLevel, Validators.required],
         interval: [null as number | null, [Validators.required, Validators.min(10)]],
         address: ['', Validators.pattern(/http.*/)],
-        tokenAddress: ['', Validators.pattern(/http.*/)],
         username: null as string | null,
         password: null as string | null
       }),
       oia: this.fb.group({
-        level: ['silent' as LogLevel, Validators.required]
+        level: ['silent' as LogLevel, Validators.required],
+        interval: [null as number | null, [Validators.required, Validators.min(10)]]
       })
     })
   });
@@ -96,12 +96,12 @@ export class EditEngineComponent implements OnInit {
           level: formValue.logParameters!.loki!.level!,
           interval: formValue.logParameters!.loki!.interval!,
           address: formValue.logParameters!.loki!.address!,
-          tokenAddress: formValue.logParameters!.loki!.tokenAddress!,
           username: formValue.logParameters!.loki!.username!,
           password: formValue.logParameters!.loki!.password!
         },
         oia: {
-          level: formValue.logParameters!.oia!.level!
+          level: formValue.logParameters!.oia!.level!,
+          interval: formValue.logParameters!.oia!.interval!
         }
       }
     };
diff --git a/frontend/src/app/engine/engine-detail.component.html b/frontend/src/app/engine/engine-detail.component.html
index e5be18e807..1a5e0868b5 100644
--- a/frontend/src/app/engine/engine-detail.component.html
+++ b/frontend/src/app/engine/engine-detail.component.html
@@ -71,16 +71,27 @@ <h1 class="oib-title" translate="engine.title">
             <tr>
               <td translate="engine.general-settings.log-levels"></td>
               <td>
-                <span translate="engine.general-settings.console"></span>:<span>{{ engineSettings.logParameters.console.level }}</span>
-                <span class="ms-2" translate="engine.general-settings.file"></span>:<span
-                  >{{ engineSettings.logParameters.file.level }}</span
-                >
-                <span class="ms-2" translate="engine.general-settings.database"></span>:<span
-                  >{{ engineSettings.logParameters.database.level }}</span
-                >
-                <span class="ms-2" translate="engine.general-settings.loki"></span>:<span
-                  >{{ engineSettings.logParameters.loki.level }}</span
-                >
+                <span
+                  translate="engine.general-settings.console"
+                  [translateParams]="{ level: engineSettings.logParameters.console.level }"
+                ></span>
+                <span class="mx-1">|</span>
+                <span
+                  translate="engine.general-settings.file"
+                  [translateParams]="{ level: engineSettings.logParameters.file.level }"
+                ></span>
+                <span class="mx-1">|</span>
+                <span
+                  translate="engine.general-settings.database"
+                  [translateParams]="{ level: engineSettings.logParameters.database.level }"
+                ></span>
+                <span class="mx-1">|</span>
+                <span
+                  translate="engine.general-settings.loki"
+                  [translateParams]="{ level: engineSettings.logParameters.loki.level }"
+                ></span>
+                <span class="mx-1">|</span>
+                <span translate="engine.general-settings.oia" [translateParams]="{ level: engineSettings.logParameters.oia.level }"></span>
               </td>
             </tr>
             <tr>
diff --git a/frontend/src/app/engine/engine-detail.component.spec.ts b/frontend/src/app/engine/engine-detail.component.spec.ts
index 4e415e03e0..1d12a76ccc 100644
--- a/frontend/src/app/engine/engine-detail.component.spec.ts
+++ b/frontend/src/app/engine/engine-detail.component.spec.ts
@@ -76,8 +76,13 @@ describe('EngineDetailComponent', () => {
       },
       loki: {
         level: 'error'
+      },
+      oia: {
+        level: 'silent'
       }
-    }
+    },
+    proxyEnabled: true,
+    proxyPort: 8888
   } as EngineSettingsDTO;
 
   beforeEach(() => {
@@ -120,10 +125,11 @@ describe('EngineDetailComponent', () => {
     expect(table[1]).toContainText('Port');
     expect(table[1]).toContainText('2223');
     expect(table[2]).toContainText('Log levels');
-    expect(table[2]).toContainText('Console:silent');
-    expect(table[2]).toContainText('File:trace');
-    expect(table[2]).toContainText('Database:silent');
-    expect(table[2]).toContainText('Loki:error');
+    expect(table[2]).toContainText('Console: silent|');
+    expect(table[2]).toContainText('File: trace|');
+    expect(table[2]).toContainText('Database: silent|');
+    expect(table[2]).toContainText('Loki: error');
+    expect(table[3]).toContainText('Proxy serverEnabled on port 8888');
 
     expect(tester.scanModeList).toBeDefined();
     expect(tester.externalSourceList).toBeDefined();
diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json
index befa822890..db023da2aa 100644
--- a/frontend/src/i18n/en.json
+++ b/frontend/src/i18n/en.json
@@ -311,10 +311,11 @@
       "name": "Name",
       "port": "Port",
       "log-levels": "Log levels",
-      "console": "Console",
-      "file": "File",
-      "database": "Database",
-      "loki": "Loki"
+      "console": "Console: {{ level }}",
+      "file": "File: {{ level }}",
+      "database": "Database: {{ level }}",
+      "loki": "Loki: {{ level }}",
+      "oia": "OIAnalytics: {{ level }}"
     },
     "proxy-settings": {
       "title": "Proxy server",
@@ -343,17 +344,17 @@
       "loki": {
         "title": "Loki",
         "level": "Loki level",
-        "interval": "Loki Interval",
+        "interval": "Loki interval",
         "address": "Loki url",
         "proxy": "Loki proxy",
         "no-proxy": "No proxy",
-        "token-address": "Loki token url",
         "username": "Loki username",
         "password": "Loki password"
       },
       "oia": {
         "title": "OIAnalytics logs",
-        "level": "OIA level"
+        "level": "OIAnalytics level",
+        "interval": "OIAnalytics interval"
       }
     },
     "monitoring": {
diff --git a/shared/model/engine.model.ts b/shared/model/engine.model.ts
index bf9a983b7d..d7a8c23b02 100644
--- a/shared/model/engine.model.ts
+++ b/shared/model/engine.model.ts
@@ -42,7 +42,6 @@ interface DatabaseLogSettings extends BaseLogSettings {
 interface LokiLogSettings extends BaseLogSettings {
   interval: number;
   address: string;
-  tokenAddress: string;
   username: string;
   password: string;
 }
@@ -50,7 +49,9 @@ interface LokiLogSettings extends BaseLogSettings {
 /**
  * Settings to write logs into a remote loki instance
  */
-interface OiaLogSettings extends BaseLogSettings {}
+interface OiaLogSettings extends BaseLogSettings {
+  interval: number;
+}
 
 /**
  * Logs settings used in the engine
diff --git a/shared/model/logs.model.ts b/shared/model/logs.model.ts
index 026924f5dc..cfb5573721 100644
--- a/shared/model/logs.model.ts
+++ b/shared/model/logs.model.ts
@@ -1,4 +1,5 @@
 import { LogLevel, ScopeType } from './engine.model';
+import { Instant } from './types';
 
 /**
  * DTO used for Log entries
@@ -22,7 +23,7 @@ export interface PinoLog {
   scopeType: ScopeType;
   scopeId: string | null;
   scopeName: string | null;
-  time: number;
+  time: Instant;
   level: string;
 }