vuer_oss — FaceKom OSS (Operator Side Server)

The back-office / operator-side server of FaceKom and the system of record for the whole product. It hosts the admin & agent web console (Twig-rendered), owns the PostgreSQL database (Sequelize, 61 models) and the encryption/key infrastructure, runs the WebRTC recording→conversion→archive pipeline, and exposes the bulk of the platform’s business logic to other services over RabbitMQ RPC. The customer-facing counterpart that browsers/kiosks hit is vuer_css; the two share an almost identical codebase shape but boot different process sets.

  • Repo: /Users/levander/coding/facekom/vuer_oss
  • Base branch: devel (origin/HEAD -> origin/devel). CI audit.yaml runs on devel + customization/*; PR CI on every PR.
  • Package name / version: vuer_oss 1.9.11 (package.json:2-3). devel-line tip seen: 521c44a5d7; a recent tagged build commit is 165fdb323e 1.9.11.49 - final.
  • Owner: TechTeamer (package.json:35, private: true). Remote: git@github.com:TechTeamer/vuer_oss.git.
  • README title: “FaceKom OSS” (README.md:1).

The working tree was checked out on update/customization/instacash-2026-05-27, NOT devel. Everything in this doc was verified against the devel branch via git show devel:<path>. The checked-out customization branch adds files that are not on devel (e.g. bin/instacash-cli.js, jest.config-unit.js, SYSTEM_CHECK.md) — those are flagged inline.

Heavily customized via Git branches. devel is the core product; deployments ship from customization/<partner> branches (e.g. customization/dap, customization/nusz, customization/instacash) that layer partner code into the customization/ tree (887 files on the checked-out branch). See customization-architecture and customization-branch-catalog.


1. Purpose & role in FaceKom

OSS is the operator/back-office half of the FaceKom video-identification (KYC) + video-banking platform. From the source:

  • It is a multi-process Node.js monolith — one codebase, seven OS processes, selected per NODE_ENV and started by supervisord (supervisor_vuer_oss_docker.conf). See §3.
  • The main process (server.js) builds a giant service container (server/service_container.js) and instantiates ~150 services (server/service/ has 138 files) covering: rooms/videochat, self-service flows, customers, crypto/keys, certificates, trusted timestamps, archive/restore, exports, OCR, computer-vision (CV) orchestration, reporting, SAML/AD/TOTP/WebAuthn auth, e-mail/SMS, and a RabbitMQ RPC surface (server.js:29-611).
  • It owns the databaseserver.js is the only entrypoint that runs migrations/sync on boot (server/bootstrap/connection/db.js:18-37, called with initDb=true only from server.js:106). All other processes connect read/write but do not migrate.
  • It owns the WebRTC recording pipeline: Janus records media → convert.js (the converter daemon) consumes a queue-convert queue, transcodes via FFmpeg, timestamps, and stores it.
  • It exposes platform capabilities to vuer_css and other services almost entirely over RabbitMQ RPC (server.js registers 32 RPC servers + 18 RPC clients + 16 queue servers = 66 queue endpoints — counted as getRPCServer/getRPCClient/getQueueServer(...) call sites, lines ~372-581). CSS calls OSS; OSS rarely serves the public internet directly.

Domain entities (Sequelize models, server/db/model/ — 61 .js files) include: customer, room, flow, flowproto, selfServiceRoom, activity, attachment, cert, mediafile, document, faceComparison, faceRecognition, emrtdInfo, recognitionAttempt, customervalidation, emaillog, smslog*, integrationLog, auditlog, encryption, importedCustomer/Room/Flow, openhour*, job, backgroundProcess (git ls-tree devel:server/db/model).

OSS vs CSS division of labour: vuer_css is the public Express+Socket.IO node a remote customer hits (homepage → waiting-room → videochat). OSS is the internal node the operator/admin uses, and it holds the DB + crypto + heavy processing. They talk over RabbitMQ.


2. Tech stack & runtime

AspectValueSource
LanguageJavaScript (CommonJS, mostly require) + some TypeScript (.ts) and ESM (.mjs)package.json, tsconfig.json
RuntimeNode.js >=22.18.0package.json:38-40 (engines.node)
CI Node matrix22 and 24 (PR build); audit job uses 22.github/workflows/pull-request.yaml, audit.yaml
Package managerYarn (classic; yarn.lock 525 KB, .yarnrc--install.ignore-optional true).yarnrc, yarn.lock
Web frameworkExpress 5 (express@^5.1.0) + helmet, express-session (connect-session-sequelize), body-parser, multer, csurfpackage.json, server/web/WebServer.js:1-20
RealtimeSocket.IO 4 (socket.io@^4.8.1 server + client)package.json, server/socket/socket-server.js
TemplatingTwig server-side rendering (twig@^3)package.json, engines/twig/render-server.js
MessagingRabbitMQ via @techteamer/mq@^7.2.0 (ConnectionPool)server/bootstrap/connection/rabbitmq.js
ORM / DBSequelize = @techteamer/sequelize@^6.30.1 (forked), sequelize-cli, umzug migrations; drivers pg+pg-hstore (PostgreSQL primary); README documents OracleDB / MySQL (mysql2) / MSSQL (tedious) opt-inspackage.json, README.md, .sequelize-config.js
Cache / locksRedis (redis@^5.8.0), optional — only if HARedis.config setserver/bootstrap/connection/redis.js:7-9
Configgetconfig@^4.1.0 (NODE_ENV-keyed JSON) + dot-object + optional Spring Cloud Config (cloud-config-client) + dotenvconfig.js
Config validationAJV (ajv@^8, draft 2020-12) against docs/config/main.json + ~79 schema filesbin/validate-config.js, docs/config/schemas/
Build/bundleesbuild (client JS) + Stylusclean-css/autoprefixer (CSS); chokidar watch; livereload (dev)bin/script/script.compiler.js, bin/style/style.compiler.js, engines/build/
Auth strategiespassport + @node-saml/passport-saml, passport-activedirectory, passport-fido2-webauthn (WebAuthn), passport-totp, passport-localpackage.json
Crypto/PKInode-jose, @lapo/asn1js, cbor, @techteamer/cert-utils, @techteamer/timestamp (RFC3161), @techteamer/acl (ACL), AES-256-GCM (server/gcm.js)package.json
Media/docsFFmpeg (external bin, via FFmpegService), pdf-lib/pdfkit/pdfjs-dist, docx, mjml/juice (e-mail), @techteamer/xlsx, archiver (+ encrypted zip)package.json, bin/check (calls ffprobe)
Cloud / storageaws-sdk (S3 storage engine), ssh2/SFTPengines/storage/s3/, package.json
Lint / testESLint 9 (flat config eslint.config.mjs, eslint-config-standard), Jest 30 (unit), Playwright 1.56 (e2e), c8/nyc coveragepackage.json, jest.config.js, playwright.config.ts
Reverse proxynginx in front of all node processesnginx_vuer_oss_docker.conf, supervisor confs

No Dockerfile / docker-compose exists in the repo (searched devel tree + working tree). "docker" here is a NODE_ENV name (config/docker.json, supervisor_vuer_oss_docker.conf), not a container build. Deployment is supervisord + nginx, presumably inside an externally-built image. The README's "inside docker" instructions refer to that runtime.


3. Build & run

Process model (the key mental model)

One codebase → 7 supervised processes (supervisor_vuer_oss_docker.conf), each node <entry>.js with NODE_ENV=docker, plus nginx + a bundled redis:

Supervisor programEntryRoleListens
vuer_ossserver.jsMain web/socket/RPC server; runs DB migrations on bootweb 127.0.0.1:10081 (server.js:645), socket.io :10080 (server.js:662, config web.socket.port)
vuer_mediamedia.jsMedia/streaming web server (records, screenshots, replay, attachments)127.0.0.1:10079 (media.js:133)
vuer_oss_convertconvert.jsConverter daemon — consumes queue-convert, transcodes recordingsRPC convert-ping only
vuer_croncron.jsScheduled jobs (archive, cleanup, expiry, reports)RPC cron-ping
vuer_backgroundbackground.jsHeavy async work: reports, exports, archive, recognition, integrityRPC background-*
vuer_oss_storagestorage.js (root)Storage-engine worker (move media between storages, e.g. S3)
vuer_integration_logintegrationLog.jsIntegration-log RPC sink (encrypted audit of external comms)RPC integration-log

All five long-running daemons share the same boot recipe: process-settingsprocess-listenersconfig.loadedsetupServices → connect db → connect redis → connect rabbitmqinitServicessetupQueuecustomization() → start (e.g. server.js:104-119, media.js:151-162, cron.js, background.js:257-262, convert.js:81-93).

nginx routing (nginx_vuer_oss_docker.conf): root /workspace/vuer_oss/web; static /js /css /img … served directly with 1y cache; /socket.io:10080; media paths (records/, selfservice-records/, api/screenshot/, api/emrtd-photo/, …) → :10079; everything else → :10081.

package.json “scripts” — every entry, verbatim

ScriptCommandWhat it does
cronnode cron.jsRuns the cron daemon process directly.
convertnode convert.jsRuns the converter daemon process directly.
stylenode bin/style/style.taskCompiles all Stylus → CSS bundles into web/css (resolves bin/style/style.task.js).
scriptnode bin/script/script.taskCompiles/bundles all client JS (esbuild) into web/js (resolves bin/script/script.task.js).
watchnode bin/watch/watchWatch+rebuild JS, CSS and server files on change (no restart).
devnode bin/watch/watch --restartDev server with live reload and auto-restart of the node process on server-file change (README’s yarn dev).
buildnode bin/build/build.jsOne-shot full build: runs script.task + style.task in parallel, then exits (bin/build/build.js).
linteslint . --max-warnings 0 --ignore-pattern "test/*" && echo 'yarn eslint --max-warnings 0'Lints everything except test/, zero-warning gate (the CI lint step).
lint:fixeslint . --fix && eslint --max-warnings 0 .Auto-fixes then re-checks with zero-warning gate.
jestyarn node --experimental-vm-modules $(yarn bin jest) --config ./jest.config.jsRuns Jest with ESM VM modules and the repo jest config.
test:unityarn node --experimental-vm-modules $(yarn bin jest) unit/*Runs unit tests (path filter unit/*test/tests/unit/**).
test:e2eyarn playwright testRuns the Playwright e2e/regression suite.
creditsnode bin/credits > CREDITS.htmlGenerates an HTML third-party license report (license-checker).
cov_cleanerrm -rf coverage/* && rm -rf coverage*/* && rm -rf .nyc_output/*Clears coverage output dirs.
validate_config:devnode bin/validate-config.js config/dev.json AJV-validates config/dev.json against the schemas.
validate_config:dockernode bin/validate-config.js config/docker.jsonAJV-validates config/docker.json against the schemas.
  • main: server.js (package.json:4). There is no bin field; CLIs live under bin/ and are run directly (most are #!/usr/bin/env node executables).
  • Start (production): supervisord launches node server.js etc. (see table). Start (dev): yarn dev; build assets first with yarn build (README).

Build pipeline internals

  • bin/build/build.js pushes --no-exit, then Promise.all([script.task, style.task]).
  • bin/script/script.task.js defines esbuild bundles for client/ui/pages|layouts/** and customization/ui/pages|layouts/**web/js; optional istanbul instrumentation when build.istanbul; minify/sourcemaps gated by config (bin/script/script.compiler.js). sourceRoot is hardcoded /workspace/vuer_oss.
  • bin/style/style.task.js compiles *.style.styl/*.layout.styl from client + customization → web/css via Stylus, injecting breakpoints, device-sizes.json, colors from client/resources/, autoprefixer, optional clean-css minify (bin/style/style.compiler.js).
  • bin/watch/watch.js simply pushes --watch and requires script+style+server tasks; bin/server/server.task.js watches *.twig, *.trans.js, server/**, config/*.json, db/**, engines/**, root entrypoints and (with --restart) restarts the process (engines/build/restart).
  • Generic builder/bundler/watcher engine: engines/build/{build,bundle,watch,restart,livereload}.js.

4. Top-level structure (meaningful dirs; vendored excluded)

git ls-tree devel root + sampled. (Excluded: node_modules, .git, logs, tempPath, IDE dirs.)

PathWhat it is
server.jsMain process entry (42 KB): service container + web/socket/RPC bootstrap.
media.jsMedia web-server process (:10079).
convert.jsConverter daemon process (consumes queue-convert).
cron.jsCron daemon process.
background.jsBackground-worker daemon process.
storage.jsStorage-engine worker process (root file; distinct from bin/storage.js CLI).
integrationLog.jsIntegration-log RPC sink process.
config.jsConfig loader wrapper around getconfig (+ Spring Cloud, .get/.has, secret redaction). See §7.
bin/First-party CLI/maintenance scripts (see §5).
server/All backend code (services, db, queue, web, cv, ocr, crypto, …). See §6.
client/Browser frontend source (1059 files): ui/ pages+layouts+styles, engine/, features/, services/, resources/, utils/. Bundled into web/.
web/Build output + static assets (js, css, img, font, fonts served by nginx).
engines/Reusable build/storage/translator/twig/worker engines (34 files; see §6).
db/Sequelize migrations: db/migrate/ (127 files) + db/beforeMigrate/. Models live in server/db/model/.
config/Env configs: dev.json, docker.json, roles.json (ACL rules).
docs/Markdown docs + docs/config/ JSON-Schema config spec (main.json + 79 schemas).
customization/Per-partner override tree (hooks/overrides/ui/services/db/translations…). Loaded last in every boot (customization()).
maintenance/Dated maintenance changelog notes (YYYY-MM-DD.md), bundled into PDF via docs.json.
security/Dated security changelog notes (YYYY-MM-DD.md), bundled into PDF via docs.json.
test/Jest + Playwright tests, mocks, seeds, test configs (21,939 files incl. fixtures). See §8.
workers/Web/inline worker source (1 file).
*.confnginx + supervisor configs for dev / docker / e2e.
changelog.mdProduct changelog (75 KB).
registration-form.yml (91 KB), eslint.config.mjs, babel.config.js, tsconfig.json, .sequelizerc, jest.config.js, playwright.config.ts, sonar-project.propertiesRoot config files.

5. First-party bin/ helper scripts (every script, READ)

All paths under bin/ (git ls-tree -r devel:bin). Pattern for nearly all of them: shebang #!/usr/bin/env noderequire('../server/bootstrap/process-settings')() + process-listenerscommander arg parsing → wire a subset of services into serviceContainersetupDb() (Sequelize authenticate + models + helpers) → run. They are operator/ops tools, not part of the request path.

Build pipeline (subdirs — invoked by npm scripts, not standalone ops)

  • bin/build/build.js — one-shot full build (script + style). §3.
  • bin/script/script.task.js, bin/script/script.compiler.js, bin/script/inlineWorkerPluign.js, bin/script/browserify-istanbul.js — esbuild client-JS bundling (+ istanbul/inline-worker plugins).
  • bin/style/style.task.js, bin/style/style.compiler.js — Stylus→CSS bundling.
  • bin/watch/watch.js — watch JS+CSS+server.
  • bin/server/server.task.js — watch/restart server files.

Top-level ops scripts

  • bin/archive — archive or restore rooms / flows (--restore); supports single id, all, or a file-of-ids list. Wires Archive/RoomArchive/FlowArchive/AttachmentArchive + crypto services.
  • bin/batch_restore.js — restore all archived rooms / flows / self-service rooms (reconciles archive-folder ids vs DB status:'archived').
  • bin/attachment.jsexports <path> dumps customer screenshots to filesystem, or copy sends screenshots to e-sign.
  • bin/check — scans converted rooms; uses ffprobe to flag suspicious video durations vs expected (from activity log), optionally --convert to force re-convert via bin/convert.
  • bin/clean-rooms — list (or --delete) leftover -video./-audio. files in the replay dir.
  • bin/comptest.jsconnectivity + diagnostics CLI (TCP/Redis/TLS probes, then RabbitMQ-RPC diagnostics from the running server). Flags --conn-only, --json, --verbose. (Documented by SYSTEM_CHECK.md — working-tree only.)
  • bin/convert — (re)convert one or many room ids by pushing to the convert queue; --self-service, -f/--force. Decrypts room files then queueClient.convert.convert(...).
  • bin/create-decrypted-room-copy.js — copy + decrypt a room’s streamed/converted videos to /tmp/vuer_oss/ (--self-service).
  • bin/create-turn-static-auth-secret.js — print TURN static-auth credentials via TurnPasswordService (<name> [validitySec] [turnServer] [secret]).
  • bin/create_rab_token.js — generate an encrypted JWT “rab” access token for an <accessPath> <expiry> (uses TokenService, config.jwt.secret).
  • bin/credits — emit HTML third-party license table (license-checker); used by yarn credits.
  • bin/crypt-room — encrypt/decrypt all files of a <roomId> (AES-256-GCM); --decrypt to reverse.
  • bin/customer-keydev-only (exits unless NODE_ENV=dev): insert/remove a customer’s encryption key (--insert/--memory/--remove).
  • bin/data-export.js — export rooms / self-service rooms / customers by id-lists/types to an output dir; pulls portal data + custom archive (uses customization/portal/PortalData, sets NODE_TLS_REJECT_UNAUTHORIZED=0).
  • bin/export_batch.js — batched room / self-service / raw-video export with --offset/--limit, per-zip --password and --encryption aes256|zip20.
  • bin/export_customers.js — export customers (and timestamp/mediafiles xlsx) with offset/limit/file-of-ids, --dry-run, --stats (uses @techteamer/xlsx, PortalData).
  • bin/flow-editor.js — “Flow Editor CLI” (flow definitions).
  • bin/flow-extractor-portable.mjs — ESM script to query/extract self-service room flows by name/date/status to a file (Sequelize + archive + crypto).
  • bin/import-legacy-data.js — import legacy data from a DB configured under importData.db (ImportService).
  • bin/integration-log.js — query/export integration-log entries with rich filters (--event/--direction/--category/--channel/--status/--from-date/--to-date/--output-dir).
  • bin/migrate-folder-hierarchy.js — move room records/replay from roomId/files to 0-99REC-REP/roomId/files (interactive confirm; per-room or all).
  • bin/migrate-rooms — create MediaFile models (+ optional --timestamp) for every closed/converted room.
  • bin/migrate-self-service-task-data — migrate self-service v2 step task.data.<field> into selfServiceRoom.data.steps[...] (--force; <stepType>.<fieldName> args).
  • bin/remove-empty-records-directories.js — remove empty dirs under convert.recordDirectory/convert.replayDirectory (--skip-record, --skip-replay, --dry-run).
  • bin/self_service_settings_db_checker — get/set faceComparison thresholds in the Settings table (--perfect, --match, --write).
  • bin/sms-logs-cli.mjs — query SMS logs by phone numbers, produce a CSV report (--interactive/--phoneNos/--output).
  • bin/storage.js — move media files between storages (--move, --type, --storage, --ids, --all); the CLI behind the vuer_oss_storage worker’s manual operations.
  • bin/timestamp-check.js — verify (or re---verify) RFC3161 timestamp tokens of a --roomId/--selfServiceRoomId.
  • bin/validate-config.jsESM: AJV-validate a config JSON (argv[2]) against core (docs/config/main.json) + custom (customization/docs/config/custom.json) schema dirs. Backs validate_config:*.

bin/config.getter.ts / bin/doc.gen.ts (TypeScript, Deno-or-Node)

  • bin/config.getter.ts — scans source for config.get('a.b.c', default) calls (regex) to enumerate every config key in use. Shebang runs via Deno, falls back to node --experimental-strip-types.
  • bin/doc.gen.ts — unifies docs/config/main.json + referenced schemas into config documentation. Same Deno/Node shebang.

bin/db/ — database admin

  • bin/db/create_user — create a user <role> <username> <password> [email] [firstName] [lastName] [phone] (UserService/PasswordService/ACL).
  • bin/db/create_user_batch — create many users from a batch file.
  • bin/db/enable_user — re-enable a disabled user by username.
  • bin/db/create_standard_days — seed the 7 OpenHourStandard rows (Mon–Fri 10:00–16:00 open, weekend closed).
  • bin/db/syncsequelize.sync() schema sync; -f/--force (drop+recreate), --noninteractive. Called automatically at boot when db.syncOnStart (bootstrap/connection/db.js:24).
  • bin/db/truncate — truncate all models (interactive confirm) except an excluded set, in a transaction.
  • bin/db/migrate-data — Umzug-based data migration between DBs (uses db/beforeMigrate storage).
  • bin/db/migrate-rdbms.js — migrate between two RDBMS URLs --origin/--destination (--testConnectionOnly); runs sequelize db:migrate with .sequelizercBefore then default config on both.
  • bin/db/customer-index.js — re-index all customers (re-saves each Customer to refresh search hashes; serial async queue).
  • bin/db/pg_sanity_check — gather PostgreSQL stats (tables, sizes, row counts, relations) as JSON to stdout.

bin/crypto/ — bulk encryption migration

  • bin/crypto/migrate — after enabling encryption in config, encrypt existing data; optional part arg (emaillog/smslog/customervalidation/customerhistory/callbackrequest/recognitionattempt) or everything. Uses bin/crypto/helpers/customer.js.
  • bin/crypto/decrypt — one-off decrypt of <key> [aad] <data> via server/gcm (debug helper).
  • bin/crypto/helpers/*.js — per-entity encryption helpers (activity, attachment, callbackrequest, customer, customerDataChange, customerHistory, customervalidation, emaillog, emrtdinfo, general, recognitionattempt, room, smslog).

bin/instacash-cli.js exists in the working tree but NOT on devel (git cat-file -e devel:bin/instacash-cli.js → fatal). It is a customization/instacash addition.


6. Key modules / services / entities (server/ — 679 files)

Immediate children of server/ (git ls-tree devel:server) with verified counts:

PathFilesWhat it is
server/service/138The business-logic layer (one class per concern). Key ones below.
server/web/179Express app + 70+ *.endpoint.js admin/agent routes + api/ REST + middleware.
server/queue/92RabbitMQ layer: rpc_server/, rpc_client/, queue_server/, queue_client/, publisher/, subscriber/.
server/db/66sequelize.js (connection), models.js, model/ (61 models), helpers.
server/cv/42Computer-vision orchestration — talks to vuer_cv over WebSocket.
server/socket/21Socket.IO server + event handlers (operator console realtime).
server/util/18Shared utilities.
server/ocr/15OCR recognition client/handlers.
server/model/12Non-DB domain models.
server/backgroundProcess/10Long-running background process framework.
server/cron/20Cron job classes (Archive, Cleanup, DeleteExpiredURL, …).
server/transport/7Transport/signalling helpers.
server/portal/7Portal integration.
server/flow/6Flow engine (FlowService).
server/bootstrap/5process-settings, process-listeners, connection/{db,redis,rabbitmq}.js.
server/listeners/4Event listeners.
server/{e-mail,sms,logger,faceRecognition,user,room-inspector}/3 eachEmail (EmailService, MJML), SMS, log4js logger, face-recognition, user, room inspector.
server/{errors,convert}/, server/{partner,webrtc}/2/4, 1/1Error types; convert pipeline (convert_check); partner; webrtc.

Singletons at server/ root: service_container.js (the DI/event bus), acl.js (@techteamer/acl), auditlog.js, auth.js, gcm.js (AES-256-GCM), diagnostic.js, SocketTokenStorage.js, logger.js, child_process_promise.js, taskCompatibility.js.

server/service_container.js — the backbone

Exports { emitter: new ServiceBus() }, where ServiceBus extends WildEmitter and adds a hook + override registry: addHook/callHooks/callOnlyHook/hasHook (used so customization() can inject behaviour at named lifecycle tags like 'services', 'queue') and registerOverride/callOverride (swap default implementations). Every process mutates this single shared object (serviceContainer.service.*, .dbModels, .queue, .rpcServer, …).

Representative server/service/ classes (verified present)

RoomService, VideoChatService, WaitingRoomService/DurableWaitingRoomService, SelfServiceRoomService/SelfServiceV2Service/SelfServiceCheckerService, JanusService (WebRTC/Janus control), RecognitionService/FaceRecognitionService, FlowService/FlowLiveUpdateService/FlowFilterService, CryptoServices/ (CryptoService, MediaCryptoService, AttachmentCryptoService, DataCryptoService, CertCryptoService, TranslationCryptoService), CustomerKeyStorageService, CertService, TrustedTimestampService/TrustedTimestampRoomService, StorageService, Export*Service (room/self-service/imported/raw-video/screenshots), TokenService, JWT/JWE TokenStrategyService, TurnPasswordService, SettingsService, IntegrationLogService, ArchiveService + ArchiveServices/.

server/web/ — operator console

  • WebServer.js builds the Express app: helmet, trust proxy, host-header allow-list (rejects unknown Host), per-request CSP nonce, Twig views (app.set('views', cwd)), sessions persisted in Sequelize (connect-session-sequelize), @techteamer/acl enforcement (WebServerAuth.js).
  • server/web/routes/*.endpoint.js (70+) = the admin/agent UI pages: customerlist, customerProfile, roomlist, room, videochat, flow*, archive/restore, auditlog, reports*, dashboard, users/newuser/edituser, systemsettings, selfservice*, importData*, webAuthn/totp/twoFactorAuth, compat-test, room-inspect, etc.
  • server/web/api/{admin,operator,supervisor,common,import-data}/ = role-scoped REST API. OpenAPI: server/web/api-doc/room-bulk-delete.yaml (express-openapi-validator + swagger-parser are deps).

server/cv/ — vuer_cv integration

CVApi.js, CVRecipe/CVTask/CVDetection/CVInstruction/CVOutput/CVResultSummary, VuerCVSession.js, VuerCVWebSocket.js, VuerCVListenerSession.js, VuerCVPADService.js + internals/, tasks/, visions/. This is the client side of the CV/biometrics service vuer_cv (presentation-attack detection, document/face vision tasks) over WebSocket.

engines/ — reusable engines (34 files)

  • build/ — generic build/bundle/watch/restart/livereload (drives the npm build scripts).
  • storage/ — pluggable media storage: Engine.js, StorageTypeHandler.js, default/ (filesystem handlers for room/self-service/attachment), s3/ (AWS S3 handlers + s3.js).
  • translator/ — i18n Dictionary.js + config (browser + server).
  • twig/render-server.js — server-side Twig rendering.
  • worker/Runner.js/Worker.js worker abstraction.
  • util/ — Twig filters (formatDate, formatDuration, fileSizeFilter, timestampFilter, zeropad, …) + inheritance.js.

7. Configuration

  • Loader: config.js wraps getconfig — it reads config/<NODE_ENV>.json (e.g. config/dev.json, config/docker.json). It adds a custom file type (inline-reads a file path into the value), pretty-prints getconfig JSON syntax errors with surrounding context and process.exit(2), and applies env-specific tweaks (dev hostnames from /etc/hostname/DEV_DOMAIN, travisci AMQP override, derive hosts.cvs from hosts.cv).
  • Spring Cloud Config (optional): if config.springCloudConfigServer is set, config.loaded resolves only after pulling remote config via cloud-config-client (with promise-retry) and merging it with dot-object. Otherwise config.loaded = Promise.resolve(). All entrypoints await config.loaded before booting — so remote config is a hard boot dependency when enabled.
  • Accessors: config.get('a.b.c', default) (safe path lookup) and config.has(...) are attached to the config object and used pervasively across the codebase.
  • Security helpers: config.getSecureConfig() deep-clones and redacts keys listed in security.sensitiveConfigFields (supports a.*.b wildcards) → 'SECRET'; config.traceConfig() flattens config for logging. CORS hardening: web.cors.origin === '*' is rewritten to hosts.oss; same for socket.io origins.
  • DB CA / retry: a db.options.dialectOptions.ssl.ca path under /workspace/ is read from file; db.options.retry.match strings are compiled to RegExp.
  • Version stamping: if .env has VUER_BUILD_NUMBER, it’s appended to config.version (so 1.9.111.9.11.<n>).

Config schema (AJV)

  • docs/config/main.json ($id: docs/config/main.schema.json, draft 2020-12, “Vuer OSS Config”) references 79 files under docs/config/schemas/ — one per top-level config section: db, queue, cv, webrtc*, convert, crypto, certService/certManager, email, esign, faceRecognition, flow, hosts, jwt, jwe/jwtTokenStrategy, saml, activeDirectory, kiosk, ocr, reporting, integrationLog, integrityCheck, sanctionList, roomBulkDelete, features, logging, HARedis, … additionalProperties $refs the customization schema (customization/docs/config/custom.json).
  • Config files declare "$schema": "../docs/config/main.json" (config/dev.json:2) for editor validation; bin/validate-config.js enforces it at CLI time.
  • config/roles.json is the master ACL rule catalogue (alphabetical list of every permission string, e.g. archive.view, auditLog.view, cert.upload.any, …) consumed by @techteamer/acl.

Env vars (verified in code)

NODE_ENV (selects config file; dev/docker/travisci special-cased), DEV_DOMAIN/CI_DOMAIN, TZ (forced to Etc/UTC in process-settings.js), NODE_TLS_REJECT_UNAUTHORIZED (set to 0 when settings.allowSelfSignedCerts or in some export scripts), VUER_BUILD_NUMBER (from .env), DO_NOT_GENERATE_ENCRYPTION_KEY (set by bin/db/migrate-rdbms.js). Sequelize config (.sequelize-config.js) requires exactly one of db.database or db.url, and an explicit db.options.dialect.

config/dev.json and config/docker.json contain committed example secrets (session secret, TURN credentials, SMTP pass, db creds). These are template/dev values; real deployments override via partner config and/or Spring Cloud. Do not treat them as live credentials, and do not copy them into production.


8. Tests

  • Frameworks: Jest 30 (unit) + Playwright 1.56 (e2e/regression). Coverage via coverageProvider: 'v8'test/coverage/jest (jest.config.js).
  • Jest config (jest.config.js): roots at repo root; ignores /node_modules/, /web/, /e2e/; transforms *.ts with babel-jest; mocks service_container with test/__mocks__/service_container.js (moduleNameMapper); 10 s default timeout.
  • Location: test/tests/unit/, e2e/, integration/, regression/, playwright/, support/, plus setup files (auth.setup.ts, open-hours.setup.ts). test/{__mocks__,seed,files,lib,scripts,testconfigs,customization,web}/. Unit tests live under test/tests/unit/** mirroring server/ (api, cron, cv, db, flow, model, ocr, services, socket, …).
  • Counts (devel): test/tests/unit/**349 *.test.{js,ts} files; test/tests/**396 *.spec/*.test.{ts,js} overall.
  • How to run:
    • Unit: yarn test:unit (or yarn jest unit/, or one suite yarn jest <file>).
    • e2e: yarn test:e2e (= yarn playwright test); install browsers first (yarn playwright install --with-deps in docker, else npx playwright install); requires yarn build on OSS + CSS and copying test/testconfigs/test-config-ci.jsonconfig/local.json (README “Running e2e tests”).
    • Test resources: clone test_resources into /workspace/test_resources (README).
  • Playwright (playwright.config.ts): workers:1, fully parallel within a worker, fake camera/mic + --disable-web-security, default storageState test/tests/playwright/.auth/user.json, locale from config.locales.default, globalTeardown present (README warns it can erase the DB).
  • CI (.github/workflows/pull-request.yaml): on every PR, Node 22 & 24 → yarn install --frozen-lockfileyarn lintyarn build. (No automated test job in CI on devel/PR.) audit.yaml (push to devel/customization/*): yarn audit + improved-yarn-audit --min-severity critical (with excludes) + an executable-file location check (fails if executables appear outside bin/, .yarn/, node_modules/, a few test/customization dirs).

e2e tests can wipe your database — the Playwright globalTeardown tears down DB state. The README's escape hatch is to remove globalTeardown from playwright.config. Run e2e only against a throwaway DB.

jest.config-unit.js exists in the working tree (uses ts-jest) but is not on devel — it's a customization-branch addition. The committed test:unit script uses jest.config.js.


9. Dependencies on other FaceKom services (evidenced in code)

RabbitMQ (primary integration bus — @techteamer/mq ConnectionPool)

server/bootstrap/connection/rabbitmq.js builds the pool from config.queue, wires serviceContainer.queue, and process.exit(2) on connection close. RPC servers/clients are registered per process:

  • server.js exposes (RPC servers, server.js:347-471): rpc-create-customer, rpc-customer-portal-data, rpc-jwt-auth, rpc-device-change, rpc-get-customer, rpc-waiting-room, rpc-videochat-oss, rpc-callback-request, rpc-openhours, rpc-openhours-calendars, rpc-partner-service, rpc-custom-content, rpc-selfservice-actions, rpc-selfservice-upload, rpc-selfservice-v2, rpc-vuer-portal, client-gate, rpc-system-check, rpc-jwt-kiosk-auth, rpc-kiosk-alive-check, rpc-media-content, rpc-identification-router, rpc-device-compatibility-check, background, rpc-appointment, rpc-document-upload/-delete, rpc-customer-documents, rpc-flow-documents, rpc-presentation, …
  • server.js consumes (RPC clients, server.js:426-477+): Ping, SystemOss, Reports, Archive, CronManager, KioskDocumentScan, Portal (rpc-portal-vuer), IntegrationLog, Recognition, EsignApi (rpc-esign:external), XmlReport, … — i.e. it calls cron, background, portal, recognition, e-sign (esign_oss), integration-log services.
  • background.js: servers background-ping/-reports/-archive/-room-export/-self-service-room-export/-recognition, rpc-xml-report, rpc-xlsx-report; client rpc-esign:external (optional dedicated esign queue connection).
  • cron.js: servers cron-ping, cronManager (if features.cronManager); client rpc-xml-report; queue clients queue-storage, queue-selfservice-cron, queue-room-cron.
  • convert.js: queue server queue-convert (consumes conversion jobs), convert-ping; pub/sub settings-change.
  • media.js: media-ping.
  • integrationLog.js: server integration-log; publisher settings-change.

Database

PostgreSQL (primary, pg/pg-hstore) via forked Sequelize; server.js runs migrations on boot (db.syncOnStartsequelize db:migrate with .sequelizercBefore then default, plus bin/db/sync). README documents Oracle (12g) / MySQL / MSSQL alternatives. .sequelize-config.js reads db.{database|url, username, password, options.dialect} from config.

Redis (optional)

server/bootstrap/connection/redis.js connects only if HARedis.config is set (“HA Redis” — high-availability shared state/locks), with a reconnect strategy and a PING/PONG health check; failure throws and aborts boot.

Janus / WebRTC + TURN

JanusService controls Janus media servers (config.webrtc.janusServers/janusConfig); TurnPasswordService mints TURN credentials. Recordings land in webrtc.server.recordDirectory/convert.replayDirectory. bin/comptest.js probes Janus + CV + CSS hosts directly.

vuer_cv (computer vision / biometrics)

server/cv/ connects to CV servers (config.hosts.cvs) over WebSocket (VuerCVWebSocket.js) for face/document/PAD tasks. See vuer_cv.

External integrations

SMTP (nodemailer + MJML), SMS (server/sms/), SFTP/SSH (ssh2, SFTPService in convert.js), e-sign (rpc-esign:externalesign_oss), Google Play Integrity / Apple integrity (@googleapis/playintegrity), SAML/AD IdPs, sanction-list checks, S3 (aws-sdk).


10. Verified gotchas

Working tree ≠ devel. Checked out on update/customization/instacash-2026-05-27. Use git show devel:<path> to read the core product; the branch adds non-devel files. All facts here are from devel.

Multi-process, one codebase. node server.js is not the whole app — you must also run media.js, convert.js, cron.js, background.js, storage.js, integrationLog.js (supervisord does this). Running only server.js leaves media, conversion, cron, exports and integration-log dead.

Only server.js migrates the DB. bootstrap/connection/db.js runs migrations/sync only when called with initDb=true, which happens solely from server.js:106. Start the others against an un-migrated DB and they'll fail. With db.syncOnStart true, boot mutates schema (incl. bin/db/sync).

RabbitMQ is mandatory & fail-fast. A closed AMQP connection triggers process.exit(2) (rabbitmq.js). No broker → no boot. Most bin/ ops scripts also open the queue pool.

Hardcoded ports & paths. Web :10081, socket :10080 (configurable), media :10079 are literals in server.js/media.js; many compilers/configs assume the absolute workdir /workspace/vuer_oss (script.compiler.js sourceRoot, nginx root, db CA path gate). Deploy there or expect breakage.

No Dockerfile in-repo. "docker" = a NODE_ENV/supervisor profile, not a container build. Runtime = supervisord + nginx (+ bundled redis) in an externally-built image.

Committed example secrets in config/dev.json/config/docker.json (session secret, TURN/SMTP creds). Override in real deployments; never promote to prod.

Spring Cloud Config gates boot. If springCloudConfigServer is configured, every process blocks on config.loaded until the remote config server answers (with retry) — a remote outage stalls startup.

Customization is load-order-sensitive. Each entry requires its customization/*-customization.js before setupServices so hooks register in time (comment in cron.js:16), and calls customization() last in the boot chain. The hook/override mechanism lives in service_container.js. See customization-architecture.

Forked deps. sequelize is @techteamer/sequelize, and passport-activedirectory is pinned to a GitHub tarball — package.json is not all upstream npm. .yarnrc sets --install.ignore-optional true.

TZ is forced to Etc/UTC at process start (process-settings.js); don't rely on host timezone.

Lint gate is zero-warning (--max-warnings 0) and executables must stay in bin/ (audit CI fails otherwise). CI does not run the test suites on PRs.


Unverified / gaps

Surveyed structurally (listing + targeted sampling) rather than line-by-line — no per-file reading of these large subtrees:

  • server/service/ (138 files): read service_container.js and confirmed names of ~40 key services + which are instantiated by each entrypoint’s setupServices; did not read each service’s implementation.
  • server/web/ (179 files): confirmed WebServer.js/WebServerAuth.js design, enumerated the 70+ *.endpoint.js and the api/{role} split; did not read each endpoint/middleware.
  • server/queue/ (92 files): enumerated the six subfolders and harvested queue/RPC names from the entrypoints; did not read each handler body.
  • server/cv/, server/ocr/, server/cron/, server/socket/, server/db/model/ (61 models), server/backgroundProcess/: listed + sampled headers (CVApi.js); did not read every file.
  • client/ (1059), customization/ (887), web/ (build output), test/ (21,939 incl. fixtures), db/migrate/ (127 migrations), docs/config/schemas/ (79 schemas): listed and counted; read representative manifests/schemas (main.json, dev.json, a few schema names) but not every file.
  • Not independently re-derived: the prose/output in SYSTEM_CHECK.md (working-tree only) — its example output was not reproduced by running comptest.js; the script bin/comptest.js was read directly.
  • Exact set/semantics of every config key: enumerable via bin/config.getter.ts and the schema files, not exhaustively listed here.

Sources

Files/dirs actually read (via git show devel:<path> / git ls-tree devel, plus a few working-tree reads where noted):

  • Root: README.md, package.json, config.js, server.js, media.js, convert.js, cron.js, background.js, storage.js, integrationLog.js, babel.config.js, tsconfig.json, .yarnrc, .sequelizerc, .sequelize-config.js, jest.config.js, playwright.config.ts, docs.json, nginx_vuer_oss_docker.conf, supervisor_vuer_oss_docker.conf, changelog.md (head); working tree: SYSTEM_CHECK.md, jest.config-unit.js.
  • bin/ (read): build/build.js, script/script.task.js, script/script.compiler.js, style/style.task.js, style/style.compiler.js, watch/watch.js, server/server.task.js, validate-config.js, credits, convert, check, clean-rooms, create_rab_token.js, create-turn-static-auth-secret.js, archive, crypt-room, customer-key, migrate-rooms, storage.js, create-decrypted-room-copy.js, attachment.js, batch_restore.js, data-export.js, export_batch.js, export_customers.js, import-legacy-data.js, integration-log.js, migrate-folder-hierarchy.js, migrate-self-service-task-data, remove-empty-records-directories.js, self_service_settings_db_checker, timestamp-check.js, flow-editor.js, flow-extractor-portable.mjs, sms-logs-cli.mjs, comptest.js, config.getter.ts, doc.gen.ts, db/{create_user,create_user_batch,enable_user,create_standard_days,sync,truncate,migrate-data,migrate-rdbms.js,pg_sanity_check,customer-index.js}, crypto/{decrypt,migrate}; listed crypto/helpers/. Verified bin/instacash-cli.js absent on devel.
  • server/: service_container.js, bootstrap/process-settings.js, bootstrap/connection/{db,redis,rabbitmq}.js, web/WebServer.js (head), cv/CVApi.js (head); git ls-tree of server/, server/web, server/web/routes, server/web/api, server/web/api-doc, server/queue, server/cv, server/service, server/db/model.
  • config/: dev.json (head), roles.json (head), docker.json (grep). docs/config/: main.json (head) + git ls-tree of schemas/ (79 files).
  • engines/: git ls-tree + build/build.js (head). db/: git ls-tree (migrate/ = 127).
  • .github/workflows/{pull-request.yaml,audit.yaml}. test/: git ls-tree of test, test/tests, test/tests/unit; test-file counts.
  • git -C … branch -a, git log --oneline, git remote -v.