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, app vuer_oss 1.9.11, DB vuer_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:external queue.

The thing that makes FaceKom FaceKom, and the thing you must internalize:

Clients are GIT BRANCHES, not config flags

devel is the core product. Each partner deployment (K&H, MBH/MKB, Raiffeisen, UniCredit, Cofidis, DAP, NÚSZ, …) is a long-lived customization/<client> Git branch layered on devel. The intended extension surface is a parallel customization/ overlay tree (a mirror of the core layout: flow/, email/, sms/, cv/, listeners/, overrides/, portal/, assets/, …) plus a ServiceBus hook/override engine (addHook/callHooks/callOnlyHook and registerOverride/callOverride) bolted onto a process-wide serviceContainer DI 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:

  1. 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/Glob and git work normally here. Prefer this for all code work. (Source: INDEX §“Repository map”, techteamer-org-inventory.)
  2. The remote dev box — the Tailscale VM fk-dev: command ssh ops@fk-dev.taild4189d.ts.net (100.91.108.61; use command ssh — the kaku alias shadows plain ssh non-interactively). This is where live runtime state lives (logs, DB, supervisorctl status per container) and where native image builds run. The old lederera@lederera-447-fk-hardver / ssh Facekom box 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/Glob against /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 run find/grep server-side. (Source: _shared-context.md Tier 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

  1. Branch off devel. Code repos default to devel (conventions-and-glossary §2.1). Use feature/FKITDEV-####-<slug> (or fix//bugfix/ for bugs). Prefix discipline is loose — typos and synonyms abound — treat the prefix as a hint (conventions-and-glossary §2.2).
  2. Run it locally — stand up the stack via vuer_docker / running-the-stack. OSS server.js is the only entrypoint that migrates the DB (architecture-overview §3.6); if you touch models, that’s the process that syncs them.
  3. Test — Jest 30 unit + Playwright 1.56 e2e (testing). Unit: jest --config ./test/jest.config.js --runInBand --forceExit. E2e: yarn test:e2e (needs yarn build on OSS and CSS + the CI test config copied to config/local.json). See the e2e DB-wipe warning in Gotchas a senior knows cold before running e2e.
  4. 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 on lint, test, audit, sonar (SonarCloud) on every PR (conventions-and-glossary §1.4).
  5. 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

  1. The re-sync convention is a dated branch update/customization/<client>-YYYY-MM-DD off the partner line (conventions-and-glossary §2.3). (The local trees are literally checked out on update/customization/instacash-2026-05-27 right now — see gotchas.)
  2. Merge devel forward into the partner branch. The conflicts will be in CORE files, not in the overlay — because partners rewrite server.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).
  3. 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 shared db/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

  1. 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.)
  2. New behaviour → a hook or an override, registered in customization/listeners/* (via addHook) or customization/overrides/* (via registerOverride). Hooks stack (callHooks fans out to all; callOnlyHook takes the first and returns its value). Overrides are single-provider, default-with-fallback (callOverride runs the registered fn, else the core default passed as the last arg). (customization-architecture §2.)
  3. Mind load order — the *-customization.js file must be required before setupServices() 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.
  4. Override names are a flat global namespace and collisions throw at boot (vuer_oss registerOverride hard-fails on duplicate; customization-architecture §2b). Two modules registering the same override name crash the process.
  5. 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).
  6. 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.js is 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, read models.js, not the model file — and a mistake there breaks the whole schema. (data-model §2.)
  • StandardJS = NO semicolons. Lint is --max-warnings 0 and gates the PR. Single quotes, 2-space indent, One True Brace Style, no console. (conventions-and-glossary §1.1.)
  • The SSHFS mount returns empty on ls/find/Glob. Use the local checkout for discovery, or SSH and run find/grep server-side. Reading a known file path over the mount is fine. (_shared-context.md Tier 2.)
  • Playwright e2e in vuer_oss can ERASE THE DB. The globalTeardown in playwright.config.ts tears down DB state; the README’s escape hatch is to remove globalTeardown from the config. Run e2e only against a throwaway DB. (Source: vuer_oss §testing — playwright.config.ts:306,309.) Note bin/db/sync --force (drop+recreate) is also wired to boot when db.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 devel fix 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_oss and esign_oss are on update/customization/instacash-2026-05-27, portal_css on customization/instacash, vuer_css on bugfix/FKITDEV-8787-selfservice-v2-abort-clear-state. So find/file-count commands over the working tree count the instacash overlay, not the core. Always read facts against devel via git 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 read devel explicitly.)
  • “Self-Service V2” is a product FLOW, not a branch. It’s the self-service-v2-phase-1/-phase-2 flow definitions + the SelfServiceV2Service orchestrator (flows §5.6, §6). The customization/ there is the in-repo overlay, not a branch name. (The slug does appear in ad-hoc feature//fix/ branch names, but there is no long-lived customization/self-service-v2 line — conventions-and-glossary §2.6 clarification.)
  • Count commands under the rtk proxy can read 0. When a count looks suspiciously empty, fall back to git ls-tree -r <ref> | grep ... (or git ls-tree <ref>:<dir>) for an authoritative count instead of trusting a filtered/proxied find|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).
  • sequelize is the @techteamer/sequelize fork, not upstream — require('sequelize') resolves to it. Treat as Sequelize v6 + fork patches (data-model §1).
  • customization/portal/PortalData.js is effectively a required core dependency — OSS won’t boot without it (required by customer.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 for kh/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_) vs serviceContainer.js (esign_); singular customization.js + per-process files on OSS vs single plural customizations.js on CSS (customization-architecture §8).

Where to look for X

A senior’s routing instinct, compressed. (Mirrors INDEX §“Start here” with the operational framing.)

Your questionGo to
The whole system, services, what-talks-to-what, ports, topologyarchitecture-overview
How transports work (RabbitMQ RPC/queue/pubsub, HTTP, Socket.IO, Janus) + full queue cataloginter-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 stalecustomization-branch-catalog
The flow engine (Self-Service V2 step machine) + every flow definitionflows
The Postgres schema (61 Sequelize models, the single models.js)data-model
Login / SAML / WebAuthn / TOTP / JWT-JWE / ACLauth
Build / dockerize / release from source (Harbor, UBI, the 3 pipelines)build-deploy-release
Naming, branch conventions, acronyms (KKSZB, eMRTD, PAD, KAÜ, QTSP…), glossaryconventions-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 nexttechteamer-org-inventory
A single repo in depththe matching repos/<name>.md (e.g. vuer_oss, vuer_cv)
Set up / run the stack locallydev-environment, running-the-stack
Run / write teststesting
Re-sync a client onto newer develdevel-update-workflow
Debug a customer/prod issuethe debug-agent pipeline (§(d) above) + room exports
How any of this was verified / what’s unprovenVERIFICATION

Sources

This doc is synthesis over the verified facekom-v2 base; the load-bearing siblings it ties together: