Web client for Vernissage, a federated, photo-first social platform connected to the fediverse through ActivityPub.
This repository contains the Angular application used to browse timelines, publish content, manage accounts, moderate content, and work with server settings from the browser. It supports SSR, hydration, service worker updates, and production deployment through Docker.
- Angular 21 application with server-side rendering and client hydration
- Photo-focused timelines, profiles, search, upload, bookmarks, favourites, and notifications
- Authentication flow based on access token refresh, cookies, and XSRF protection
- PWA support with Angular service worker update handling
- Configurable instance branding through public settings, custom CSS, and custom JS
- Shared product surface with the Vernissage API and iOS client
- Project website: joinvernissage.org
- Documentation: docs.joinvernissage.org
- API server: VernissageServer
- iOS client: VernissageMobile
- Create an account: vernissage.photos
- Node.js 24
- npm
- a running Vernissage API server, usually available on
http://localhost:8080during local development
For backend setup, see VernissageServer.
- Clone the repository.
- Install dependencies:
$ npm install --force- Start the local development server:
$ npm start- Open http://localhost:4200.
The app reloads automatically when source files change.
$ npm start # Angular dev server
$ npm run build # Production browser + server build
$ npm run lint # ESLint
$ npm test # Karma/Jasmine tests
$ npm run serve:ssrThe web client talks to the Vernissage Server HTTP API. In local development it expects:
- web app on
http://localhost:4200 - API server on
http://localhost:8080
In deployed environments the client usually talks to the API on the same public host. The backend then connects to PostgreSQL or SQLite, Redis, and S3-compatible object storage depending on deployment mode.
The repository follows a UI-oriented Angular structure:
src/app/pages- route-level screens such as home, profile, upload, settings, search, and moderation pagessrc/app/components- reusable UI building blocks, split intocorelayout parts andwidgetssrc/app/dialogs- modal flows and edit dialogssrc/app/services/http- API clients grouped by backend resourcesrc/app/services/common- browser, SSR, loading, routing, preferences, and UI support servicessrc/app/services/authorization- sign-in state, refresh-token flow, and route guardssrc/app/models- API and view modelssrc/app/directives,src/app/pipes,src/app/validators- shared template utilitiesserver.ts- Express-based SSR entrypoint with optional security headers
At startup the app:
- tries to refresh the access token,
- loads instance metadata and public settings from the API,
- injects custom scripts and styles exposed by the server,
- hydrates the Angular application in the browser,
- registers the service worker outside development mode.
Request handling is centered around:
APIInterceptor, which addswithCredentialsand theX-XSRF-TOKENheader,AuthorizationService, which refreshes expired sessions,GlobalErrorHandler, which maps failures to dedicated error pages and reports unexpected client-side errors.
Routes are defined in src/app/pages/pages-routing.module.ts. The application mixes public pages and authenticated areas:
- public: home, news, public profiles, status pages, FAQ, terms, privacy
- authenticated: upload, notifications, invitations, account, settings, reports, shared cards, moderation views
Several gallery-like views use route reuse and shared context state to preserve timeline data when navigating between screens.
src/main.tsandsrc/main.server.ts- browser and server bootstrapsrc/app/app.module.ts- root module, hydration, service worker, interceptors, global error handlersrc/app/pages/pages.module.ts- all route-level page declarationssrc/styles- global SCSS variables, fonts, utilities, and layout helperssrc/assets- icons, fonts, screenshots, and bundled client assetsDockerfile- multi-stage SSR image build.github/workflows/build.yml- CI build, lint, test, and production build verification
The production build includes both browser assets and a server bundle. SSR is served through Express and @angular/ssr, while static assets are emitted to dist/VernissageWeb/browser.
Build locally:
$ npm run build
$ npm run serve:ssrBuild a Docker image:
$ docker build -t vernissage-web .
$ docker run --rm -p 8080:8080 vernissage-webProduction images are published to Docker Hub.
When running the SSR server in production, it is recommended to enable the image source used in the Content Security Policy through the VERNISSAGE_CSP_IMG environment variable:
export VERNISSAGE_CSP_IMG=https://s3.eu-central-1.amazonaws.comThis value is used by server.ts to extend the Content-Security-Policy header for remote image loading.
Angular SSR also validates request hostnames (SSRF protection). To allow your production host, configure one of the following environment variables:
export VERNISSAGE_ALLOWED_HOSTS=yourdomain.photos,*.otherdomain.social
# or
export NG_ALLOWED_HOSTS=yourdomain.photos,*.otherdomain.socialIf no variable is provided, the server falls back to:
localhost, 127.0.0.1, ::1, vernissage.photos, *.vernissage.photos.
Contributions are welcome.
- Keep changes focused and aligned with the existing module structure.
- Run
npm run lint,npm test, andnpm run buildbefore opening a pull request. - Document any behavior changes that affect SSR, authentication, or deployment.
This project is licensed under the Apache License 2.0.

