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.

Documentation: reference table
StageFormat / limit
Source filePNG, JPEG, WebP; up to 10 MiB
Browser processingCenter-square crop; at most 512 × 512; JPEG re-encoding; transparency becomes white
API uploadAt most 512 KiB; independent raster/MIME validation
DisplaySidebar, 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.

API And Persistence: reference table
MethodPathContract
PUT/auth/profile/photo{contentType,data} canonical base64; authenticated account only; unknown/target-user fields rejected
DELETE/auth/profile/photoRemove own photo; repeating is harmless
GET/users/:id/photo?v=:revisionCurrent private raster; current authentication, sharing, revision rechecked before bytes
API And Persistence: reference table
RuleBehavior
Mutation response{photoUrl:string|null}
Mutation securityReauthenticate inside transaction; ordinary cookie same-origin checks
AuditPhoto and size/type-only update or count-only delete audit commit together
Missing/invisible photoRemoved/replaced revision, disabled account, or no visibility: 404
URLNot an access capability; authenticated same-origin shape only; new revision resets image loading
Read headersByte-derived PNG/JPEG/WebP MIME; inline, nosniff, private, no-store, same-origin policy, sandbox/default-none CSP
JSON / exportsSafe photoUrl/authorPhotoUrl metadata only; never bytes in user/comment JSON, audits, or workspace export
Full database backupIncludes account photos
Raster validation, migration, and metadata wiring
  • rasterContentType independently 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_photos creates 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/ProfilePhoto use apps/web/src/styles/profile-photos.css and existing geometry/initial styling.

Verification

Historical upstream evidence: 2026-10-02, Node 24.20.0. This is not a new application test run.

Verification: reference table
LayerRecorded evidence
TypecheckAPI passes; web 117 files, no diagnostics
AccountsNine focused tests; audit rollback and credentials withdrawn before transaction
HTTPSelf-only mutation, origin/auth denial, input/bytes/size/MIME validation, private headers, shared-access revocation, admin preview, revisions, metadata wiring, safe audit/export
PostgreSQL 17Disposable migrations and binary read/upload/replace/remove/revision/audit check passes; resources removed
SourceWhitespace check passes
BrowserDecode/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-generate applied 0011_profile_photos to 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.