Working on FaceKom — the senior engineer’s playbook
This is the doc I wish someone had handed me on day one. It ties the whole knowledge base together: the mental model, your environment, the day-to-day workflows, the gotchas you only learn by getting burned, and a routing table for “where do I look for X.” It is mostly synthesis + links — every deep fact lives in a sibling doc, cited there against read-only source. When I make a new concrete claim here, I cite it inline. Start at INDEX for the map; come back here for the how.
Two names, one product
vuer is the codename in the source (repos
vuer_oss/vuer_css/vuer_cv, appvuer_oss 1.9.11, DBvuer_oss). FaceKom is the product/brand. TechTeamer is the GitHub org / company. You will see all three; they mean the same system. (Source: INDEX §“Two names, one product”.)
The mental model in 60 seconds
FaceKom is a video KYC / eKYC + video-banking + e-signing platform. The shape, top to bottom:
- vuer_oss (OSS = Operator Side Server) is the spine and the system of record. It owns PostgreSQL (61 Sequelize models), all crypto/keys, and the WebRTC record→convert→archive pipeline. It runs ≥8 supervised processes (
server.js,convert.js,media.js,background.js,cron.js,storage.js,integrationLog.js, + nginx + redis). This is where the business logic lives. - vuer_css (CSS = Client Side Server) is the customer-facing front door (homepage → system-check → waiting-room → videochat + self-service). No DB, no business logic — it bridges the browser to OSS over RabbitMQ and relays Janus coordinates for media.
- OSS ↔ CSS talk over RabbitMQ (mTLS AMQPS), ~40+ RPC pairs matched by identical queue name (
getRPCClient('X')↔getRPCServer('X')). RabbitMQ is the spine — see inter-service-comms for the full queue catalog. - vuer_cv (CV) is the Python ML engine (face / document / MRZ / barcode / liveness / PAD / hologram). OSS calls it over HTTPS
/api/v1..v3/*+ a streaming WSS/ws; internally it’s a Redis-RPC worker fleet. It’s a leaf service — it doesn’t call back. - Janus carries the WebRTC media (external image, not in any FaceKom repo). A newer TS janus-sdk is a separate v2 media plane.
- A swarm of single-purpose RabbitMQ-RPC microservices hang off the bus: PDF signing (pdfservice), antivirus (antivirus_service), timestamping, EU trust-lists, Hungarian ID-registry scraping (nyilvantarto_scraper), reporting (report-engine), admission control (resource-manager).
- A separate eSign product pair (esign_oss / esign_css) reuses the exact same OSS/CSS shape and is wired to vuer_oss via the
rpc-esign:externalqueue.
The thing that makes FaceKom FaceKom, and the thing you must internalize:
Clients are GIT BRANCHES, not config flags
develis the core product. Each partner deployment (K&H, MBH/MKB, Raiffeisen, UniCredit, Cofidis, DAP, NÚSZ, …) is a long-livedcustomization/<client>Git branch layered ondevel. The intended extension surface is a parallelcustomization/overlay tree (a mirror of the core layout:flow/,email/,sms/,cv/,listeners/,overrides/,portal/,assets/, …) plus aServiceBushook/override engine (addHook/callHooks/callOnlyHookandregisterOverride/callOverride) bolted onto a process-wideserviceContainerDI singleton. A well-behaved partner change touches zero core files — new flow = new overlay folder, new behaviour = a hook/override. Read architecture-overview for the topology and customization-architecture for the engine. This duality (one core, N branch-deployments) governs almost every decision you make.
There are 242 customization/* branches on vuer_oss alone, and 21 of 22 analysed clients edit core files anyway — which is the central maintenance hazard (see Gotchas a senior knows cold). Counts: INDEX §“Key numbers”, customization-clients.
Your environment
The dev-environment / running / testing / devel-update docs are forward references
dev-environment, running-the-stack, testing, and devel-update-workflow are the deep docs for this section. If a link is dead, it hasn’t been written yet — the summary below is the load-bearing minimum, and the bracketed facts are cited to source.
You work in two parallel realities and must know which one you’re in:
- Local checkouts under
/Users/levander/coding/facekom/(the 🟢 core repos:vuer_oss,vuer_css,portal_css,esign_oss,esign_css,vuer_build,vuer_docker,vuer-release) and analysis clones under/Users/levander/coding/facekom-v2-clones/(the 🔵 services + libs).Read/Grep/Globandgitwork normally here. Prefer this for all code work. (Source: INDEX §“Repository map”, techteamer-org-inventory.) - The remote dev box — the Tailscale VM
fk-dev:command ssh ops@fk-dev.taild4189d.ts.net(100.91.108.61; usecommand ssh— thekakualias shadows plainsshnon-interactively). This is where live runtime state lives (logs, DB,supervisorctl statusper container) and where native image builds run. The oldlederera@lederera-447-fk-hardver/ssh Facekombox is decommissioned (2026-07-01) — with it, the SSHFS mount at/Users/levander/coding/mnt/Facekom/described below. Host reference: dev-build-host.
The stack is containerized (Podman/Docker via OrbStack as the local container runtime; disk-offload-bandi). The whole platform stands up from vuer_docker (compose files + bin/vuer.sh) against host-mounted source — git checkout devel there, docker login harbor.techteamer.com. Images are pulled from Harbor (harbor.techteamer.com) and built locally only when iterating. Full build/deploy/release mechanics (the three pipelines: vuer-release CLI+CI, legacy vuer_build bash, vuer_docker dev): build-deploy-release.
The SSHFS mount lies on directory listings
ls/find/Globagainst/Users/levander/coding/mnt/Facekom/return empty. Reading a specific known file path over the mount works; discovery does not. For discovery either use the local checkout, or SSH and runfind/grepserver-side. (Source:_shared-context.mdTier 2: “NEVER use ls/find/Glob on the SSHFS mount — they return empty”.)
Day-to-day playbooks
Concrete, numbered, each linking the deep doc. Tickets are YouTrack (FKITDEV-####, FKQA-####), not Jira — PR templates ask for “YouTrack ticket(s)” (conventions-and-glossary §2.4).
(a) Ship a feature / fix to the core
- Branch off
devel. Code repos default todevel(conventions-and-glossary §2.1). Usefeature/FKITDEV-####-<slug>(orfix//bugfix/for bugs). Prefix discipline is loose — typos and synonyms abound — treat the prefix as a hint (conventions-and-glossary §2.2). - Run it locally — stand up the stack via vuer_docker / running-the-stack. OSS
server.jsis the only entrypoint that migrates the DB (architecture-overview §3.6); if you touch models, that’s the process that syncs them. - Test — Jest 30 unit + Playwright 1.56 e2e (testing). Unit:
jest --config ./test/jest.config.js --runInBand --forceExit. E2e:yarn test:e2e(needsyarn buildon OSS and CSS + the CI test config copied toconfig/local.json). See the e2e DB-wipe warning in Gotchas a senior knows cold before running e2e. - Lint — zero tolerance.
eslint . --max-warnings 0. Base is StandardJS: no semicolons, single quotes, 2-space indent, One True Brace Style (conventions-and-glossary §1.1–1.2). CI gates onlint,test,audit,sonar(SonarCloud) on every PR (conventions-and-glossary §1.4). - PR — fill the standard template (Summary / References → YouTrack ticket / Related PR(s)) (conventions-and-glossary §2.6).
(b) Bring a client up to date with devel
- The re-sync convention is a dated branch
update/customization/<client>-YYYY-MM-DDoff the partner line (conventions-and-glossary §2.3). (The local trees are literally checked out onupdate/customization/instacash-2026-05-27right now — see gotchas.) - Merge
develforward into the partner branch. The conflicts will be in CORE files, not in the overlay — because partners rewriteserver.js/config/*.json/engines/**/bin/**despite the engine being designed to avoid exactly that. Expect to resolve removed/added feature blocks with real engineering judgement, not a mechanical merge (customization-architecture §7). - Watch the recurring hot-spots:
config/{dev,docker,roles}.json(everyone),server/web/WebServerAuth.js(auth/firewall bypasses),server/auditlog.js,server/e-mail/Letter.js,client/ui/regions/sidebar/*, and shareddb/migrate/injections (cofidis 5, kh 5 — a shared migration timeline is high-risk) (customization-clients §“Recurring CORE hot-spots”). Full workflow + per-client drift: devel-update-workflow, customization-branch-catalog.
(c) Add or modify a client customization
- Default to the overlay, never the core. New flow →
customization/flow/<name>/(3 files:.flow.{proto,handler,trans}.js, see flows §3). New letter →customization/email/<type>/. New SMS →customization/sms/<type>/. New CV recipe →customization/cv/instructions/. (customization-architecture §5.) - New behaviour → a hook or an override, registered in
customization/listeners/*(viaaddHook) orcustomization/overrides/*(viaregisterOverride). Hooks stack (callHooksfans out to all;callOnlyHooktakes the first and returns its value). Overrides are single-provider, default-with-fallback (callOverrideruns the registered fn, else the core default passed as the last arg). (customization-architecture §2.) - Mind load order — the
*-customization.jsfile must berequired beforesetupServices()so its hooks exist when the core fires them;customization()runs last to pull in listeners/overrides (customization-architecture §3). Re-order these and hooks silently don’t fire. - Override names are a flat global namespace and collisions throw at boot (vuer_oss
registerOverridehard-fails on duplicate; customization-architecture §2b). Two modules registering the same override name crash the process. - Check what the client already does before you start — customization-clients is the per-client synthesis (what each partner customizes, their SOAP/Oracle/CV quirks, their CORE blast radius).
- If you must edit a core file, you’ve just created upgrade debt for up to 242 branches. Treat it as a last resort and document it. (See Gotchas a senior knows cold.)
(d) Debug a production / customer issue
Use the FaceKom debug-agent pipeline — 5 Claude Code subagents at ~/.claude/projects/-Users-levander-coding-facekom/agents/ (mirrored at the mnt/Facekom project), coordinated by debug-orchestrator, all sharing _shared-context.md:
Customer complaint / YouTrack ticket
→ complaint-decoder (vague/Hungarian text → technical signal; Tier 1)
→ ticket-triage (signal → component map + severity + checklist; Tier 1-2)
→ reproduction-planner (checklist → step-by-step repro using room exports + dev env; Tier 3-4)
→ rca-investigator (5-Whys evidence chain → root cause + fix; Tier 3-4, may need SSH)
→ synthesize on the main thread
Quick mode (clear/obvious issue) runs triage only and produces a checklist; full mode runs all four in sequence. The agents follow progressive disclosure — Tier 1 (arch + service table, always loaded) → Tier 2 (codebase search, local checkout first) → Tier 3 (these Obsidian docs) → Tier 4 (live server state via SSH: docker exec -it vuer_oss supervisorctl status, logs, DB). (Source: agents/debug-orchestrator.md, agents/_shared-context.md.) Room exports are the primary reproduction artifact — they capture a customer session you can replay locally. The legacy room-export-blueprint.md covers their anatomy; prefer the verified facekom-v2 base for facts.
Gotchas a senior knows cold
The non-negotiables
server/db/models.jsis the single point of failure. All 61 Sequelize models and every association (hasMany/belongsTo/scopes) live in one 408-line file; the model files themselves know nothing of their relationships. To understand any entity’s graph, readmodels.js, not the model file — and a mistake there breaks the whole schema. (data-model §2.)- StandardJS = NO semicolons. Lint is
--max-warnings 0and gates the PR. Single quotes, 2-space indent, One True Brace Style, noconsole. (conventions-and-glossary §1.1.)- The SSHFS mount returns empty on
ls/find/Glob. Use the local checkout for discovery, or SSH and runfind/grepserver-side. Reading a known file path over the mount is fine. (_shared-context.mdTier 2.)- Playwright e2e in vuer_oss can ERASE THE DB. The
globalTeardowninplaywright.config.tstears down DB state; the README’s escape hatch is to removeglobalTeardownfrom the config. Run e2e only against a throwaway DB. (Source: vuer_oss §testing —playwright.config.ts:306,309.) Notebin/db/sync --force(drop+recreate) is also wired to boot whendb.syncOnStart(vuer_oss §bin).- Client customization branches modify core files → merge/upgrade risk. The hook/override engine exists precisely to avoid this, but nothing enforces it — 21/22 analysed clients edit core anyway (only instacash is config-only). Every
develfix must be hand-merged into the partner branches that rewrote the same files. (customization-architecture §7, customization-clients §“Who modifies CORE”.)- The local trees are on the instacash update branch, not
devel. Verified now:vuer_ossandesign_ossare onupdate/customization/instacash-2026-05-27,portal_cssoncustomization/instacash,vuer_cssonbugfix/FKITDEV-8787-selfservice-v2-abort-clear-state. Sofind/file-count commands over the working tree count the instacash overlay, not the core. Always read facts againstdevelviagit show devel:<path>unless you specifically want the partner tree. (Verified:git -C <repo> rev-parse --abbrev-ref HEAD, 2026-05-30; consistent with data-model, flows, conventions-and-glossary §3 which all readdevelexplicitly.)- “Self-Service V2” is a product FLOW, not a branch. It’s the
self-service-v2-phase-1/-phase-2flow definitions + theSelfServiceV2Serviceorchestrator (flows §5.6, §6). Thecustomization/there is the in-repo overlay, not a branch name. (The slug does appear in ad-hocfeature//fix/branch names, but there is no long-livedcustomization/self-service-v2line — conventions-and-glossary §2.6 clarification.)- Count commands under the
rtkproxy can read 0. When a count looks suspiciously empty, fall back togit ls-tree -r <ref> | grep ...(orgit ls-tree <ref>:<dir>) for an authoritative count instead of trusting a filtered/proxiedfind|wc. (Operational note; see RTK for the proxy.)
Quieter ones worth knowing
- Tracker is YouTrack, not Jira, and tickets link to
youtrack.techteamer.com(conventions-and-glossary §2.4).sequelizeis the@techteamer/sequelizefork, not upstream —require('sequelize')resolves to it. Treat as Sequelize v6 + fork patches (data-model §1).customization/portal/PortalData.jsis effectively a required core dependency — OSS won’t boot without it (required bycustomer.js,helpers.js, several endpoints). devel ships a default; partners override it (customization-architecture §5).- The single container registry is Harbor (
harbor.techteamer.com); base OS is mid-migration to UBI10 (FKITDEV-8252), blocked on Oracle’s missing OL10 yum repo forkh/bb(build-deploy-release §6, FKITDEV-8252-oracle-ol10-memo).- Bilingual codebase — Hungarian appears in comments, commit messages, and customer complaints (e.g. proto steps marked
"nem működik"= “does not work”; flows §5.6). The complaint-decoder agent exists for exactly this.- Naming inconsistency across the OSS/CSS family:
service_container.js(vuer_) vsserviceContainer.js(esign_); singularcustomization.js+ per-process files on OSS vs single pluralcustomizations.json CSS (customization-architecture §8).
Where to look for X
A senior’s routing instinct, compressed. (Mirrors INDEX §“Start here” with the operational framing.)
| Your question | Go to |
|---|---|
| The whole system, services, what-talks-to-what, ports, topology | architecture-overview |
| How transports work (RabbitMQ RPC/queue/pubsub, HTTP, Socket.IO, Janus) + full queue catalog | inter-service-comms |
| How one core becomes N client builds (DI container + hook/override engine + overlay) | customization-architecture |
| What a specific partner customizes + cross-client patterns (SOAP/Oracle/CV/upgrade-risk) | customization-clients |
Which customization/* branch exists, who owns it, how stale | customization-branch-catalog |
| The flow engine (Self-Service V2 step machine) + every flow definition | flows |
The Postgres schema (61 Sequelize models, the single models.js) | data-model |
| Login / SAML / WebAuthn / TOTP / JWT-JWE / ACL | auth |
| Build / dockerize / release from source (Harbor, UBI, the 3 pipelines) | build-deploy-release |
| Naming, branch conventions, acronyms (KKSZB, eMRTD, PAD, KAÜ, QTSP…), glossary | conventions-and-glossary |
What a given bin/ CLI or helper script does (cross-repo) | bin-and-scripts |
| Every repo in the org, local vs clone vs not, what to clone next | techteamer-org-inventory |
| A single repo in depth | the matching repos/<name>.md (e.g. vuer_oss, vuer_cv) |
| Set up / run the stack locally | dev-environment, running-the-stack |
| Run / write tests | testing |
Re-sync a client onto newer devel | devel-update-workflow |
| Debug a customer/prod issue | the debug-agent pipeline (§(d) above) + room exports |
| How any of this was verified / what’s unproven | VERIFICATION |
Sources
This doc is synthesis over the verified facekom-v2 base; the load-bearing siblings it ties together:
- INDEX — the map and key numbers.
- architecture-overview — topology, service/process table, transports.
- customization-architecture — the DI container +
ServiceBushook/override engine +customization/overlay + the core-file upgrade risk. - customization-clients — per-client synthesis (22 findings) + cross-client patterns + CORE hot-spots.
- conventions-and-glossary — lint/StandardJS, branch naming, YouTrack, glossary.
- flows — flow engine + Self-Service V2 catalog.
- inter-service-comms — RabbitMQ/HTTP/Socket.IO/Janus + queue catalog.
- data-model — the 61-model schema and the single
models.jsSPOF. - build-deploy-release — Harbor, UBI10 migration, the three build pipelines.
- techteamer-org-inventory — local-vs-clone layout, the dev box.
- Forward refs (deep docs for §“Your environment” / §“Day-to-day”): dev-environment, running-the-stack, testing, devel-update-workflow, customization-branch-catalog, bin-and-scripts, auth, VERIFICATION.
- Non-vault sources cited inline: the debug-agent pipeline at
~/.claude/projects/-Users-levander-coding-facekom/agents/(debug-orchestrator.md,_shared-context.md,complaint-decoder.md,ticket-triage.md,reproduction-planner.md,rca-investigator.md); working-tree branch checkgit -C <repo> rev-parse --abbrev-ref HEAD(2026-05-30); FKITDEV-8252-oracle-ol10-memo; disk-offload-bandi; RTK.