A dim room with a lit world map on the wall and server racks beyond a doorway

Good Phorum

Good Phorum

A self-hosted Perl forum, powered by Mojolicious, Mojo::Pg and PostgreSQL.

CI Perl 5.40 or newer PostgreSQL 16–18 BSD-3-Clause license

Quick start · Production · Architecture · Documentation · Roadmap

Good Phorum is a self-hosted forum for conversations that last. It runs as one Mojolicious application behind nginx or Caddy, with PostgreSQL for storage and search. Pages render on the server; read routes also answer in JSON. Application reads and writes call versioned PostgreSQL functions through **Mojo::Pg**. Local processes reach PostgreSQL over [Unix sockets](docs/DEPLOYMENT.md#unix-socket-mode). **GP** means **Good Phorum**; the **P** stands for **Perl**. [About the name](docs/BRANDING.md). ## What it does | Area | Features | | --- | --- | | **Conversation** | Threads, replies and revisions; reply to and quote a post; likes and reactions; polls; drafts with autosave and live preview. | | **Discovery** | Full-text search by title or post, tags, similar threads while composing, bookmarks in folders, follows of threads and categories. | | **Members** | Verified accounts, [passkeys](docs/passkeys-registration.md), TOTP, invitations and staff approval, direct messages, avatars, ignore lists, digests and one-click unsubscribe, data export and erasure. | | **Moderation** | Queues, first-post review, automod and watched words, IP bans, timed suspensions, reversible actions, a hash-chained audit trail. | | **Files** | Images, PDFs and text up to 25 MiB, scanned by ClamAV before delivery. | | **Interfaces** | English and Italian; light, dark and high-contrast themes; keyboard-first semantic HTML. | | **Protocols** | Atom feeds, mbox export, Ed25519-signed revisions, mail-in replies, WebSub, a Gemini mirror ([guide](docs/ops/community-protocols.md)). | | **Integrations** | YouTube, Vimeo, GitHub, GitLab, Codeberg, Facebook, Instagram, Spotify, Apple Music, Wikipedia and Mathematics; optional allowlisted Mastodon/PeerTube; selected-topic Discord announcements, moderated feeds and opt-in OIDC; players load on click ([guide](docs/integrations.md)). | | **Operation** | One command, `gpforum`, for setup, checks, backups and upgrades; an admin console for forum and plugin settings. | ## Requirements You need **Perl 5.40 or newer**, App::cpm and Carton, and PostgreSQL 16, 17 or 18. Distribution Perl, Homebrew, perlbrew, plenv, asdf and custom builds are supported. Select an executable with `GPFORUM_PERL=/absolute/path/to/perl`, or activate it on `PATH`; `make perl-runtime` reports the selection. Dependencies in `local/` are tied to that runtime's ABI and CPU architecture, independently of its installation prefix. See [Perl runtime selection](docs/ops/perl-runtime.md).
Host packages Debian 13 or Ubuntu 26.04: ```sh sudo apt install perl build-essential cpanminus libpq-dev libssl-dev zlib1g-dev postgresql postgresql-client sudo cpanm -M https://cpan.metacpan.org/ App::cpm Carton ``` FreeBSD (client only): ```sh sudo pkg install perl5 p5-App-cpanminus postgresql16-client sudo cpanm -M https://cpan.metacpan.org/ App::cpm Carton ``` macOS with Homebrew: ```sh brew install perl cpanminus postgresql@18 pgvector brew services start postgresql@18 export PATH="$(brew --prefix postgresql@18)/bin:$PATH" "$(brew --prefix)/bin/perl" "$(brew --prefix cpanminus)/bin/cpanm" -M https://cpan.metacpan.org/ App::cpm Carton ```
## Quick start From a clone: ```sh make install-deps-postgres # cpm installs from cpanfile.snapshot into local/ sudo bin/gpforum setup --environment development # macOS: without sudo gpforum admin create --email you@example.com --username you gpforum start --foreground # http://127.0.0.1:3000 ``` `setup` puts `gpforum` on your `PATH` and asks three questions — address, database, mail — where Enter takes the suggestion. It writes `/etc/gpforum/gpforum.env` (under `$(brew --prefix)/etc` on macOS) with generated secrets, creates the database and brings the schema up to date; running it again changes nothing. In development mail goes to the terminal; this prints the verification link a new member receives: ```sh bin/gpforum outbox --once ``` `gpforum` alone lists its commands. `gpforum doctor` checks the whole setup, one line each, and says how to fix what is wrong ([guide](docs/ops/doctor.md)). For development, `make check` runs the quality gates and `make fresh-checkout` verifies a clean clone; `script/benchmark-http` and `script/profile-nytprof` measure. ## Production From a bare Debian 13 or Ubuntu 26.04 host to a signed-in owner behind TLS: ```sh sudo apt install git perl build-essential cpanminus libpq-dev libssl-dev zlib1g-dev \ postgresql nginx certbot python3-certbot-nginx clamav-daemon clamav-freshclam sudo cpanm -M https://cpan.metacpan.org/ App::cpm Carton sudo git clone https://github.com/gipidevdor/GPForum.git /opt/gpforum sudo /opt/gpforum/bin/gpforum setup sudo gpforum service print --to /etc/systemd/system sudo systemctl daemon-reload && sudo systemctl enable --now gpforum gpforum-outbox gpforum-scheduled-jobs.timer gpforum-partition-maintenance.timer sudo certbot certonly --nginx -d forum.example.org sudo gpforum service print nginx --to /etc/nginx/sites-enabled sudo nginx -t && sudo systemctl reload nginx sudo -u gpforum gpforum admin create --email EMAIL --username NAME ``` Each command ends by naming the next. The prefork manager listens on a Unix socket that the printed nginx and Caddy files already use, and PostgreSQL is reached over its socket with peer authentication. Mail leaves through the host's `sendmail` unless you answer `smtp HOST:PORT USER`; ClamAV needs `StreamMaxLength 251M`. [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) explains every step, plus FreeBSD and macOS; [deploy/gpforum.env.example](deploy/gpforum.env.example) lists every setting.

## How it is built - **Native EV.** Persistent Mojolicious workers use `Mojo::Reactor::EV`: epoll on Linux, kqueue on macOS and FreeBSD. Startup verifies the [actual backend](docs/ops/reactor-backend.md). - **PostgreSQL function API.** Mojo::Pg calls the typed, versioned `api.*_vN` catalogue. A [restricted runtime login](docs/ops/sql-api-role.md) can enforce that boundary. DBIx::Class remains a maintainer dependency for historical fixtures and comparisons ([ADR 0174](docs/adr/0174-mojo-pg-is-the-runtime-stored-function-transport.md)). - **Asynchronous HTTP services.** Mojolicious async/await drives native PostgreSQL queries, transactions and connection handshakes. Bounded subprocesses isolate expensive CPU work and synchronous service workflows; cache and realtime state remain in the web process. See [deployment limits](docs/DEPLOYMENT.md). - **Templates compiled before fork.** Startup waits for asynchronous warmup to finish. Workers inherit compiled EP templates and partials; [HTTP benchmarks verify each serving worker](benchmarks/README.md#complete-http-workload). - **Transactional events.** A write commits its rows, domain event and audit record together. Outbox workers deliver events with safe retries. - **PostgreSQL search.** Full-text search and `pg_trgm`, optional [pgvector semantic search](docs/ops/hybrid-search.md), and keyset pagination. - **Rebuildable caches.** Process-local caching, with optional GlifiStore sharing. PostgreSQL remains authoritative. - **Explicit security boundaries.** Argon2id, server sessions, CSRF protection and authorization across pages, search, feeds, files and realtime. The verification target is [OWASP ASVS 5.0](docs/adr/0149-owasp-asvs-5-is-the-security-bar.md) L2, with L3 for accounts and moderation. - **Reproducible measurements.** [Performance evidence](docs/architecture/sql-functions-performance.md) includes workloads, raw results and commands. [Architecture](ARCHITECTURE.md) explains the boundaries; [decision records](docs/adr/README.md) explain the choices. ## Status **In development.** Local setup and evaluation are available. A release requires passing quality gates and evidence from the target deployment. | Target | Remaining qualification | | --- | --- | | Private beta | Staging TLS, mail delivery and representative load | | Public production | Beta qualification, an attachment restore drill and a rehearsed rollback | There is no tagged release or independent security review yet. Use the [release criteria](ROADMAP.md#release-readiness) and dated [readiness review](docs/release/readiness-review.md) to assess a deployment. Focused checks and historical benchmarks apply to the revisions they tested. ## Contributing See [CONTRIBUTORS.md](CONTRIBUTORS.md) for individual credits. Read [CONTRIBUTING.md](CONTRIBUTING.md) and run `make check` before a pull request; `make integration` runs the PostgreSQL suite on temporary clusters. Report vulnerabilities privately through [SECURITY.md](SECURITY.md). [Changelog](CHANGELOG.md) · [Support](SUPPORT.md) · [Governance](GOVERNANCE.md) · [Code of conduct](CODE_OF_CONDUCT.md)


Two open arcs and one amber node, after the hero's warm network lights. Artwork · BSD-3-Clause