- Swift 99.5%
- Bru 0.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .github/workflows | ||
| .vscode | ||
| Public | ||
| Resources | ||
| Sources | ||
| Temp | ||
| TestRequests | ||
| Tests | ||
| .dockerignore | ||
| .gitignore | ||
| .swiftlint.yml | ||
| appsettings.json | ||
| docker-compose.yml | ||
| Dockerfile | ||
| FEDERATION.md | ||
| fly.toml | ||
| LICENSE | ||
| Package.resolved | ||
| Package.swift | ||
| README.md | ||
| SECURITY.md | ||
Vernissage Server
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.
Highlights
- 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
Quick Links
- 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
Architecture
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.
Requirements
- 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
macOS
$ brew install gd libexif libiptcdata
Linux
$ apt install -y libgd-dev libexif-dev libiptcdata0-dev
Getting Started
For 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 run
By default, the API starts on http://localhost:8080.
Useful first checks:
- root:
GET / - health:
GET /api/v1/health
Example Login
$ 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
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.
Storage, Queues, and Scheduled Jobs
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.
Federation
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.
Repository Layout
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
Development
Build the project:
$ swift build
Run tests:
$ swift test --no-parallel
Preview the local DocC documentation:
$ swift package --disable-sandbox preview-documentation \
--exclude-extended-types \
--product VernissageServer
Generate 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/docs
Developer 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.
Docker
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 app
You can also build and run the Dockerfile directly, without docker compose:
$ docker build -t vernissage-server .
$ docker run --rm -p 8080:8080 vernissage-server
To 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:secretpass@host.docker.internal:5432/postgres \
-e VERNISSAGE_QUEUEURL=redis://host.docker.internal:6379 \
vernissage-server
The 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.
Security
If you find a security issue, please use GitHub Security Advisories or contact info@vernissage.photos.
See SECURITY.md for the disclosure policy.
License
This project is licensed under the Apache License 2.0.