Library / SDK
knowledgefutures/pubpub avatar
knowledgefutures/pubpub

PubPub: a publishing platform where the community owns its infrastructure

Open Community Publishing

547 stars66 forksTypeScriptGPL-2.0

At a glance

What is it?
PubPub is the open source platform behind pubpub.org, built for scholarly communities that publish their own journals, courses and discussion. The interesting engineering is in the seams: a separate auth service, encrypted secrets, a font pipeline on S3, and a demo community seeded on first run.
Who is it for?
Adopt PubPub if you are standing up a scholarly community with its own journals, reviews and courses, and you want the platform to be self-hosted and community governed rather than a commercial publisher's tooling. Do not expect it to be a finished product with an easy on-ramp, because the README says outside contributions are not yet well set up and that the user-facing documentation is a work in progress.
Can I use it commercially?
Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Collaborative community publishing, with a governance model

PubPub is the open source platform for Collaborative Community Publishing, the system behind pubpub.org, and the design assumption is that a scholarly community runs its own publishing infrastructure rather than renting it.

The governance is visible in the repository itself. Issues live in a PubPub repository with a template, but feature requests, ideas, general feedback and help go to a forum, with a note to search it first and join an existing discussion if the topic has been covered. All activity on the repository is governed by the Knowledge Futures Code of Conduct, and the documentation, roadmap, contributing guide and style guide are all in the same place as the code.

The contribution policy is stated with unusual candour. At the moment, PubPub isn't particularly well set up for outside contributors, however the project would like to get to that point, and if there is a specific feature or idea from the roadmap or the issue list that you are interested in working on, they would like to hear from you, with a note to [email protected] introducing yourself and describing how you would like to contribute. The same offer is repeated for documentation, which is described as a work in progress at help.pubpub.org.

So the project is a functioning platform run by an organisation, with an open codebase and an explicit invitation to talk first. The project is GPL-2.0 licensed, the primary language is TypeScript, the default branch is main, and the last push was on 2026-10-20. The release history is a series of release candidates, v6.1.0-rc.22 from 2026-10-14 among them, so the 6.1 line is still settling.

Running it locally means starting an auth service first

The Local Development section is the most complete documentation in the README, and it starts with a dependency that is not obvious from the project name. PubPub requires KF Auth for authentication, and you start it first.

bash
# Terminal 1: Start KF Auth
cd ../kf-auth-v3
pnpm install
pnpm dev

Then the application itself:

bash
# Terminal 2: Start PubPub
pnpm install
pnpm dev

The prerequisites are Docker and Docker Compose, pnpm activated through corepack, SOPS with age for decrypting environment secrets, and pandoc with poppler on macOS, with an Aptfile for the equivalent Debian packages. The age key has to be asked for from a team member and placed at ~/.config/sops/age/keys.txt.

On first run, pnpm dev will auto-decrypt infra/.env.local.enc into infra/.env.local, which needs the age key, then start PostgreSQL, RabbitMQ, the app server and the worker via Docker, and then seed a demo community if the database is empty. You navigate to http://localhost:9876 and sign in with the KF Auth seed admin account, [email protected] with the password admin123.

The rest of the local topology is a table of four URLs: PubPub on 9876, KF Auth for sign-in on 3000, the KF Auth account dashboard on 3001, and Inbucket as the email inbox for development on 9999. That last one is the detail that makes local development workable, because a community platform sends email and you need to read it without a real mail server.

Secrets are encrypted in the repository and decrypted on demand

Environment secrets are encrypted with SOPS and age, and the decryption happens automatically on pnpm dev, but you can also run it manually:

bash
pnpm secrets:decrypt:local   # infra/.env.local.enc → infra/.env.local
pnpm secrets:encrypt:local   # infra/.env.local → infra/.env.local.enc (after changes)

The workflow is a round trip. The encrypted file is what lives in the repository, the plaintext file is what the dev server reads, and the encrypt command is how you put your changes back. Keeping the plaintext out of version control is the point, and SOPS with age is the mechanism, with the key living outside the repository on the developer's machine.

The troubleshooting section covers the two failures you will actually hit. If you see env file infra/.env.local not found, the auto-decrypt may have failed, and the fix is to ensure the age key is at ~/.config/sops/age/keys.txt and then run pnpm secrets:decrypt:local. If every page returns 404, the demo community may not have been seeded, and you check the Docker logs for seed messages, resetting from scratch with docker compose -f infra/docker-compose.dev.yml down -v followed by pnpm dev. The down -v matters, because it removes the volume rather than just the containers, and the seed only runs when the database is empty.

The third entry, cannot log in or a redirect loop, has the shortest fix. KF Auth has to be running on port 3000, because PubPub expects the OIDC issuer at http://localhost:3000.

The font pipeline is a small CDN with content hashing

The Fonts section is the most self-contained piece of engineering in the README, and it is a good example of doing one thing carefully.

PubPub uses three main font families plus CJK variants. Source Sans 3 for headers, titles and UI text, Source Serif 4 for body text in pubs, Outfit for the landing page, and Noto Serif and Noto Sans in the TC, JP, KR and SC variants for CJK content. All fonts are variable-weight woff2 files from Fontsource, hosted on S3 at assets.pubpub.org/fonts/<hash>/, and the font files are not stored in the repository.

The mechanism is a single fonts.css on S3 containing all the @font-face declarations, around 940 rules, for every font family. Each rule uses unicode-range subsetting, so browsers only download the woff2 slices for characters actually on the page. The font CSS is loaded through link tags in the server's Html.tsx for the web and in the export worker for PDF exports, so the browser can start fetching it in parallel with other resources.

Cache busting is content hashing. Files on assets.pubpub.org have a long TTL, so to avoid cache issues all font files live under a content-hashed directory, and the hash is derived from the sha256 of all generated files. If nothing changes, the hash stays the same and nothing gets re-uploaded; if anything changes, you get a new directory and new URLs automatically. Old versions remain on S3 harmlessly, they just stop being referenced.

The update script does the work in five steps: it downloads fontsource packages directly from npm so nothing gets installed in the project, copies the woff2 files to a staging directory, generates fonts.css with corrected font-family names and relative url() paths, computes a content hash from everything in the staging directory, and uploads to the bucket. A dry run flag shows what would happen without uploading.

A conventional stack with a long history behind it

The package manifest and the repository layout describe a codebase that has grown rather than been rewritten.

The package is pubpub at version 6.1.0, type commonjs, with the main entry point at init.js, and the dev script is a shell script in the infra directory rather than a node invocation. The build splits into a client build through webpack with separate dev and prod configs, and a server build through tsc with its own tsconfig.server.json, and the production build additionally runs an export CSS step. There is a separate worker build, with the worker assets copied out of the workers directory after the server build.

The type declarations are split the same way, with tsconfig.json, tsconfig.client.json and tsconfig.server.json, and the tests run under vitest with a config in vitest.config.ts. The formatting and linting use biome with a biome.jsonc at the root.

The top level listing shows the shape of the application. There is a client/ and a server/ directory, a workers/ directory for the background jobs, a facets/ directory, a deposit/ directory, a pubstash/ directory, and an infra/ directory that holds the Docker Compose setup and the encrypted environment file. There is static/ for assets, tools/, utils/, types/, patches/ for patched dependencies, a .storybook/ directory, a .test/ directory, and hotReloadServer.js at the root. There is also a Dockerfile, an Aptfile listing the Debian packages, a Procfile for a Heroku style deployment, app.json, and firebase.json with a firebaserc.

The Dockerfile shows a multi-stage build on node:24-slim with corepack enabled for pnpm, the Aptfile parsed with awk and installed with xargs, the PostgreSQL apt repository added and a pinned pandoc 2.19.2 deb downloaded with the architecture selected at build time, a production dependency stage using a pnpm cache mount, and a builder stage. The apt repository key is fetched and dearmored rather than trusted blindly, which is the right way to do it.

Editorial conclusion

Adopt PubPub if you are standing up a scholarly community with its own journals, reviews and courses, and you want the platform to be self-hosted and community governed rather than a commercial publisher's tooling. Do not expect it to be a finished product with an easy on-ramp, because the README says outside contributions are not yet well set up and that the user-facing documentation is a work in progress. Before you start, get an age key from a team member, because the local environment secrets are encrypted with SOPS and age and the dev server will not decrypt them without it.

Frequently asked questions

How do I run PubPub locally?

You need Docker and Docker Compose, pnpm, SOPS with age, and an age key placed at ~/.config/sops/age/keys.txt. Start KF Auth first in one terminal with pnpm install and pnpm dev, then start PubPub in a second, and open http://localhost:9876 signing in as [email protected] with the password admin123.

Why does my local PubPub show 404 on every page?

The demo community was probably not seeded. Check the Docker logs for seed messages, and to reset from scratch run docker compose -f infra/docker-compose.dev.yml down -v followed by pnpm dev, since the seed only runs when the database is empty.

How does PubPub handle environment secrets?

They are encrypted with SOPS and age. Decryption happens automatically on pnpm dev, and you can also run pnpm secrets:decrypt:local to turn infra/.env.local.enc into infra/.env.local, and pnpm secrets:encrypt:local to put your changes back.

How do I access the PubPub API?

The easiest way is through the PubPub SDK, and the API documentation is at pubpub.org/apiDocs, or at the apiDocs path of your own community URL.

Official sources

  1. knowledgefutures/pubpub on GitHub
  2. License: GPL-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/knowledgefutures-pubpub.svg)](https://hysenlabs.com/projects/knowledgefutures-pubpub)

Was this useful?