API server for Vernissage, written in Swift and built with Vapor. Vernissage is a federated, photo-first social platform connected to the fediverse through ActivityPub.
This repository is the backend for the Vernissage ecosystem. It handles authentication, timelines, statuses, media processing, moderation, instance settings, federation, queues, and scheduled jobs for the mobile and web clients.
- Swift 6.2 backend built on Vapor 4
- SQLite for local development, PostgreSQL for production deployments
- Redis-backed queues and scheduled jobs, with optional in-process workers
- S3-compatible object storage support for media files
- ActivityPub federation with WebFinger, HTTP Signatures, and NodeInfo
- Automatic migrations and seed data, including a default administrator account
- DocC-based API and hosting documentation
- Documentation: docs.joinvernissage.org
- API documentation: docs.joinvernissage.org/documentation/vernissageserver
- Federation notes: FEDERATION.md
- Mobile client: VernissageMobile
- Web client: VernissageWeb
- Project website: joinvernissage.org
Vernissage Server is organized around a conventional backend split:
Controllersexpose HTTP and ActivityPub endpoints.Servicescontain domain logic for accounts, timelines, statuses, federation, storage, notifications, and search.Models,DTOs, andMigrationsdefine persistence and the public API contract.QueueJobsandScheduledJobshandle asynchronous and recurring work.ActivityPubKitcontains reusable federation-specific code.
At runtime, the API sits between client applications and the infrastructure it depends on:
- clients: Vernissage Mobile, Vernissage Web, external OAuth clients, federated servers,
- persistence: SQLite or PostgreSQL via Fluent,
- async infrastructure: Redis queues and schedulers,
- media layer: local storage for development or S3-compatible object storage in real deployments.
The application startup sequence is worth understanding: on startup it loads configuration, registers routes, configures the database, runs migrations, seeds dictionaries and the default admin user, initializes cached settings, then enables middleware, queues, schedulers, email, and storage.
- Swift 6.2
- macOS 15+ or a modern Linux distribution supported by Swift
- system libraries for image and EXIF processing:
gdlibexiflibiptcdata
- optional for fuller setups:
- PostgreSQL
- Redis
- S3-compatible storage such as AWS S3 or MinIO
$ brew install gd libexif libiptcdata$ apt install -y libgd-dev libexif-dev libiptcdata0-devFor local development, the default configuration is intentionally simple:
- the app reads
appsettings.json, - it creates or uses a local SQLite database file,
- migrations and seed data run automatically,
- a default
adminuser is created with passwordadmin.
Run the server from the repository root:
$ swift runBy default, the API starts on http://localhost:8080.
Useful first checks:
- root:
GET / - health:
GET /api/v1/health
$ curl --location 'http://localhost:8080/api/v1/account/login' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"userNameOrEmail": "admin",
"password": "admin"
}'Example response:
{
"accessToken": "<jwt>",
"refreshToken": "<refresh-token>",
"xsrfToken": "<xsrf-token>",
"expirationDate": "<iso-8601-date>",
"userPayload": {
"userName": "admin"
}
}Use Authorization: Bearer <accessToken> for authenticated requests.
If authentication is based on cookies, state-changing endpoints also require XSRF protection via the X-XSRF-TOKEN header. When the access token is sent in the Authorization: Bearer header, the extra XSRF header is not required.
Configuration is loaded in this order:
appsettings.jsonappsettings.<environment>.jsonappsettings.local.json- environment variables
For local overrides, create appsettings.local.json next to appsettings.json:
{
"vernissage": {
"baseAddress": "http://localhost:8080",
"connectionString": "postgres://postgres:secretpass@localhost:5432/postgres",
"dbMaxConnectionsPerEventLoop": 1,
"dbConnectionPoolTimeoutSeconds": 10,
"dbConnectTimeoutSeconds": 10,
"queueUrl": "redis://127.0.0.1:6379",
"s3Address": "https://s3.eu-central-1.amazonaws.com",
"s3Region": "eu-central-1",
"s3Bucket": "your-bucket-test",
"s3AccessKeyId": "YOUR_ACCESS_KEY",
"s3SecretAccessKey": "YOUR_SECRET_KEY",
"s3Http1OnlyMode": "false",
"disableQueueJobs": "false",
"disableScheduledJobs": "false"
}
}Most important keys:
| Key | Purpose |
|---|---|
baseAddress |
Public base URL of the instance. Important for federation and links. |
connectionString |
SQLite file path or PostgreSQL connection string. |
dbMaxConnectionsPerEventLoop |
Maximum number of PostgreSQL connections per event loop (FluentPostgresDriver, default: 1). |
dbConnectionPoolTimeoutSeconds |
How long a request waits for a free DB connection before timeout (default: 10). |
dbConnectTimeoutSeconds |
Timeout for opening a new PostgreSQL TCP connection (default: 10). |
queueUrl |
Redis connection string used for queues and cache. |
s3Address |
Base URL of S3-compatible storage. |
s3Region |
AWS region. When set, AWS S3 settings take precedence over custom S3 address handling. |
s3Bucket |
Bucket name for uploaded media. |
s3AccessKeyId / s3SecretAccessKey |
Credentials for object storage. |
s3Http1OnlyMode |
Forces HTTP/1 mode for S3 client integrations when needed. |
disableQueueJobs |
Disables in-process queue workers. |
disableScheduledJobs |
Disables in-process schedulers. |
In production, configuration can be overridden with environment variables such as VERNISSAGE_BASEADDRESS, VERNISSAGE_CONNECTIONSTRING, VERNISSAGE_DBMAXCONNECTIONSPEREVENTLOOP, VERNISSAGE_DBCONNECTIONPOOLTIMEOUTSECONDS, VERNISSAGE_DBCONNECTTIMEOUTSECONDS, VERNISSAGE_QUEUEURL, and related VERNISSAGE_* keys.
The application also supports standard Vapor runtime configuration:
- Vapor runs in the
developmentenvironment by default, which is the mode used when starting locally withswift run. - The Docker image starts the server with
serve --env production, so containers run in theproductionenvironment by default. - You can change the environment explicitly with
--env, for exampleswift run VernissageServer serve --env production. - You can override the log level with Vapor's
--logflag or theLOG_LEVELenvironment variable.
Supported log levels are: trace, debug, info, notice, warning, error, and critical.
Vapor defaults to info logging in development and notice in production, unless you override it.
The server runs in several modes depending on configuration:
- without
queueUrl, Redis-backed jobs are disabled and the app falls back to a no-op queue driver, - with
queueUrl, Redis is used for queues and cache, - with
disableQueueJobs=false, workers can run in the same process, - with
disableScheduledJobs=false, recurring jobs also run in-process.
Examples of background work handled by the server:
- email delivery,
- web push notifications,
- ActivityPub inbox and outbox processing,
- status creation and federation side effects,
- import tasks,
- archive generation and cleanup,
- trending calculations,
- cleanup of temporary files, captchas, failed logins, and stale statuses.
For media storage, local files are enough for development, but S3-compatible storage is the expected setup for real deployments.
Vernissage Server is a federated backend, not only a private API.
Supported protocols and standards:
- ActivityPub server-to-server
- WebFinger
- HTTP Signatures
- NodeInfo
Important public endpoints include:
/.well-known/webfinger/.well-known/nodeinfo/.well-known/host-meta/api/v1/nodeinfo/2.0/shared/inbox
See FEDERATION.md for the supported extensions and more precise federation notes.
Sources/VernissageServer- main application targetSources/VernissageServer/Controllers- HTTP and ActivityPub endpointsSources/VernissageServer/Services- business logicSources/VernissageServer/Models- Fluent modelsSources/VernissageServer/Migrations- database migrationsSources/VernissageServer/QueueJobs- asynchronous jobsSources/VernissageServer/ScheduledJobs- recurring jobsSources/VernissageServer/VernissageServer.docc- DocC documentationSources/ActivityPubKit- reusable ActivityPub support codeTests- unit testsTestRequests- request samples useful during manual API testingPublicandResources- static and bundled resources used by the server
Build the project:
$ swift buildRun tests:
$ swift test --no-parallelPreview the local DocC documentation:
$ swift package --disable-sandbox preview-documentation \
--exclude-extended-types \
--product VernissageServerGenerate static documentation output:
$ swift package --allow-writing-to-directory .build/docs \
generate-documentation --target VernissageServer \
--disable-indexing \
--exclude-extended-types \
--transform-for-static-hosting \
--output-path .build/docsDeveloper notes:
- object IDs are Snowflake-like
bigintvalues, but the JSON API exposes them as strings, - startup runs migrations and seeders automatically,
- many authenticated write endpoints are protected by both auth and XSRF validation,
- logs go to stdout, which keeps local development and containerized deployments simple.
The repository includes a Dockerfile and a minimal docker-compose.yml for running the API in a production-like environment.
Build and start locally with Docker:
$ docker compose build
$ docker compose up appYou can also build and run the Dockerfile directly, without docker compose:
$ docker build -t vernissage-server .
$ docker run --rm -p 8080:8080 vernissage-serverTo pass custom configuration, provide standard VERNISSAGE_* environment variables when starting the container:
$ docker run --rm -p 8080:8080 \
-e VERNISSAGE_BASEADDRESS=http://localhost:8080 \
-e VERNISSAGE_CONNECTIONSTRING=postgres://postgres:[email protected]:5432/postgres \
-e VERNISSAGE_QUEUEURL=redis://host.docker.internal:6379 \
vernissage-serverThe image entrypoint starts the server in the production environment and listens on port 8080.
Production images are published to Docker Hub.
For a fuller deployment, including proxy, web client, push service, and Redis, refer to the DocC hosting guides at docs.joinvernissage.org.
If you find a security issue, please use GitHub Security Advisories or contact [email protected].
See SECURITY.md for the disclosure policy.
This project is licensed under the Apache License 2.0.