# vnctalk-avatar The VNCtalk **avatar service** — a small Node/nginx service that preprocesses (GraphicsMagick resize) and serves user avatars. This is the code that backs `avatar.vnc.biz`; deployed in-cluster it becomes the dev cluster's avatar store. ## What it does On `PUT /avatarupload/` it receives raw image bytes, resizes them to a fixed set of resolutions, and writes them to `outputDir` keyed by `md5(jid)`: ``` /.jpg # default resolution (80) /-.jpg # 46, 47, 57, 80, 420, 800 ``` The frontend reads these back via `avatarServiceUrl + "/" + md5(jid) + "-.jpg"`. Prosody's `vnc_vcard_avatar` module is what `PUT`s the photo here on every vCard avatar change (`avatar_upload_url` → `/avatarupload/`). ## API | Method | Path | Purpose | |---|---|---| | `PUT` | `/avatarupload/:jid` | Upload avatar bytes (`Content-Type: image/*`); returns `md5(jid)`. | | `DELETE` | `/avatarupload/:jid` | Remove all of a jid's avatar files. | | `GET` | `/avatarupload/health` | `{"status":"OK"}`. | | `POST` | `/avatarupload/info` | Batch `ctime` lookup for a list of md5 ids. | Static image serving (`GET /.jpg`) is **not** handled by `app.js` — it is served by an nginx that shares the `outputDir` volume. The container installs nginx but the static-serving server config must be supplied at deploy time (see Deploy below). ## Auth `PUT`/`DELETE` require either: - the legacy global Basic credentials (`avatar` / the shared avatar-server password), or - a JWT (Bearer/basic-password slot) signed with `config.jwtsecret` whose `vncdomain` claim matches the jid's domain. > **Open item:** both the global Basic credential hash and `jwtsecret` are > hardcoded in `app/app.js` / `config/vnc-avatarservice.js`. These are shared > service credentials already committed elsewhere (vncdirectory/vncproject). > For a clean in-cluster deploy they should move to env vars / Infisical — > tracked separately. ## Config (`config/vnc-avatarservice.js`) | Key | Default | Purpose | |---|---|---| | `servicePort` | `3896` | Node upload API port. | | `outputDir` | `/var/opt/out` | Avatar filesystem store (needs a persistent volume). | | `defaultresolution` | `80` | `.jpg` (no suffix) resolution. | | `resolutions` | `[46,47,57,80,420,800]` | All sizes written on upload. | | `jwtsecret` | hardcoded | JWT verification secret (move to env). | `NODE_ENV` selects the config block (`development` / `development2` / `gr13`). ## Build ```bash docker build -t vnctalk-avatar . # CI: push to main → .gitea/workflows/build.yml → kaniko ``` ## Deploy Not yet declared in `vnc-iac-env`. Two prerequisites for a working in-cluster deploy (tracked separately): 1. **Persistent `outputDir`** (`/var/opt/out`) — otherwise avatars are lost on pod restart. 2. **nginx static serving** — a server block (or ingress) that serves the `outputDir` files at `avatarServiceUrl`, plus the CORS headers the browser needs. Once deployed, point `vnctalk-prosody`'s `avatar_upload_url` at it (`https:///avatarupload/`) and set `avatar_upload_user` / `avatar_upload_pass` (from Infisical `AVATAR_PASSWORD`, not git). ## Layout | Path | Purpose | |---|---| | `app/app.js` | Express upload API (`PUT /avatarupload/:jid`, health, info). | | `config/vnc-avatarservice.js` | Env-selected config (port, outputDir, resolutions, jwtsecret). | | `Dockerfile` | Alpine multi-stage, non-root (`vnc`, uid 1001), gm + nginx + node. | | `Dockerfile.ubuntu` | Legacy GCP base-image variant (pm2 + deb `vnc-avatarservice`). | | `debian/` | Legacy Debian packaging (used by the GCP-era image). | | `conf.template/` | Legacy passenger/nginx templates (mostly notification-proxy leftovers). | | `.gitea/workflows/build.yml` | kaniko build → `vnclagoon/vnctalk-avatar:{sha-…,latest}`. |