Use Hopya
Profile pictures
Add a private profile picture and understand formats, limits, visibility, and account-photo APIs.
v0.3.0 referenceReviewed
On this page
Open Account settings → Profile picture, then choose Upload, Replace, or Remove.
| Stage | Format / limit |
|---|---|
| Source file | PNG, JPEG, WebP; up to 10 MiB |
| Browser processing | Center-square crop; at most 512 × 512; JPEG re-encoding; transparency becomes white |
| API upload | At most 512 KiB; independent raster/MIME validation |
| Display | Sidebar, profile preview, task/Document comments; initial fallback if absent/loading fails |
Photos are private: visible to you, current shared-workspace members, and site administrators. Saving a photo preserves profile/password drafts; photo/account controls prevent overlapping mutations. No external avatar service or cloud account is required.
API And Persistence
Paths below use /api/v1.
| Method | Path | Contract |
|---|---|---|
| PUT | /auth/profile/photo | {contentType,data} canonical base64; authenticated account only; unknown/target-user fields rejected |
| DELETE | /auth/profile/photo | Remove own photo; repeating is harmless |
| GET | /users/:id/photo?v=:revision | Current private raster; current authentication, sharing, revision rechecked before bytes |
| Rule | Behavior |
|---|---|
| Mutation response | {photoUrl:string|null} |
| Mutation security | Reauthenticate inside transaction; ordinary cookie same-origin checks |
| Audit | Photo and size/type-only update or count-only delete audit commit together |
| Missing/invisible photo | Removed/replaced revision, disabled account, or no visibility: 404 |
| URL | Not an access capability; authenticated same-origin shape only; new revision resets image loading |
| Read headers | Byte-derived PNG/JPEG/WebP MIME; inline, nosniff, private, no-store, same-origin policy, sandbox/default-none CSP |
| JSON / exports | Safe photoUrl/authorPhotoUrl metadata only; never bytes in user/comment JSON, audits, or workspace export |
| Full database backup | Includes account photos |
Raster validation, migration, and metadata wiring
rasterContentTypeindependently validates container/dimensions, detected vs declared MIME, and ≤512 KiB decoded bytes. SVG/GIF/HTML/malformed data/MIME mismatches are rejected. Direct API containers allow ≤40 megapixels / 16,384 pixels per side; browser uploads use the smaller profile dimensions. The API does not transcode pixels.- Additive migration
0011_profile_photoscreates one size/byte-length/MIME-bounded binary row per account, with cascading user foreign key. Replacement gets a fresh revision. SQLite/PostgreSQL use native binary storage without rebuilding user/content tables. /auth/me, profile updates, admin lists, and authorized members return safe photo metadata. Comments include author photo URL only for an active author still in that workspace; unavailable/removed authors fall back to initials.- Shared
Avatar/ProfilePhotouseapps/web/src/styles/profile-photos.cssand existing geometry/initial styling.
Verification
Historical upstream evidence: 2026-10-02, Node 24.20.0. This is not a new application test run.
| Layer | Recorded evidence |
|---|---|
| Typecheck | API passes; web 117 files, no diagnostics |
| Accounts | Nine focused tests; audit rollback and credentials withdrawn before transaction |
| HTTP | Self-only mutation, origin/auth denial, input/bytes/size/MIME validation, private headers, shared-access revocation, admin preview, revisions, metadata wiring, safe audit/export |
| PostgreSQL 17 | Disposable migrations and binary read/upload/replace/remove/revision/audit check passes; resources removed |
| Source | Whitespace check passes |
| Browser | Decode/resize, file picker/focus, draft retention, final mobile rendering unverified; no automated browser suite |
Upstream development migration record
- After focused checks, the owner-authorized
node ace.js migration:run --no-schema-generateapplied0011_profile_photosto the upstream development database in 28 ms. - That earlier operation made hot-reloading metadata usable; no service restart, browser operation, or owner photo/data mutation occurred. It is not an instruction to rerun setup or modify this documentation site’s data.