- JavaScript 82%
- Go 11.6%
- Go Template 4%
- Python 1.2%
- Less 1%
- Other 0.1%
|
|
||
|---|---|---|
| .github | ||
| appstats | ||
| author | ||
| blogfrei | ||
| cmd/writefreely | ||
| config | ||
| db | ||
| key | ||
| keys | ||
| less | ||
| LICENSES | ||
| mailer | ||
| migrations | ||
| oauth | ||
| page | ||
| pages | ||
| parse | ||
| prose | ||
| scripts | ||
| smoketest | ||
| spam | ||
| static | ||
| templates | ||
| testdata | ||
| themes/default | ||
| .dockerignore | ||
| .drone.yml | ||
| .editorconfig | ||
| .gitignore | ||
| .golangci.yml | ||
| account.go | ||
| account_import.go | ||
| activitypub.go | ||
| activitypub_post_test.go | ||
| activitypub_test.go | ||
| admin.go | ||
| admin_config_test.go | ||
| AGENTS.md | ||
| ap_subscriber_maintenance.go | ||
| ap_subscriber_maintenance_test.go | ||
| app.go | ||
| auth.go | ||
| AUTHORS.md | ||
| bootstrap_test.go | ||
| cache.go | ||
| collections.go | ||
| CREDITS.md | ||
| database-lib.go | ||
| database-no-sqlite.go | ||
| database-sqlite.go | ||
| database.go | ||
| database_activitypub.go | ||
| database_test.go | ||
| docker-compose.prod.yml | ||
| docker-compose.yml | ||
| docker-setup.sh | ||
| Dockerfile | ||
| Dockerfile.prod | ||
| email.go | ||
| errors.go | ||
| export.go | ||
| federation_blocklist.go | ||
| federation_blocklist_test.go | ||
| feed.go | ||
| go.mod | ||
| go.sum | ||
| gopher.go | ||
| handle.go | ||
| hostmeta.go | ||
| hugo_archive.go | ||
| hugo_archive_test.go | ||
| images.go | ||
| images_test.go | ||
| instance.go | ||
| invites.go | ||
| jobs.go | ||
| keys.go | ||
| LEGAL.md | ||
| LICENSE | ||
| main_test.go | ||
| Makefile | ||
| monetization.go | ||
| nodeinfo.go | ||
| oauth.go | ||
| oauth_generic.go | ||
| oauth_gitea.go | ||
| oauth_gitlab.go | ||
| oauth_signup.go | ||
| oauth_slack.go | ||
| oauth_test.go | ||
| oauth_writeas.go | ||
| ossl_legacy.cnf | ||
| outbound.go | ||
| outbound_test.go | ||
| pad.go | ||
| pad_test.go | ||
| pages.go | ||
| post_list_read_count_test.go | ||
| post_title_test.go | ||
| postrender.go | ||
| postrender_test.go | ||
| posts.go | ||
| posts_test.go | ||
| profile.go | ||
| read.go | ||
| README.md | ||
| request.go | ||
| routes.go | ||
| routes_test.go | ||
| schema.sql | ||
| SECURITY.md | ||
| semver.go | ||
| session.go | ||
| sitemap.go | ||
| smoketest.py | ||
| sqlite.sql | ||
| subscriber_stats.go | ||
| subscriber_stats_test.go | ||
| templates.go | ||
| unregisteredusers.go | ||
| users.go | ||
| view_stats.go | ||
| view_stats_test.go | ||
| webfinger.go | ||
Blogfrei
Blogfrei is a self-hosted publishing platform focused on independent writing, simple operation, and a modern editing experience.
The project starts as a fork of WriteFreely, but follows its own direction from this point onward.
First Implementation Milestone
The first Blogfrei implementation milestone replaces the inherited default editor with TOAST UI Editor, an open-source Markdown WYSIWYG editor licensed under the MIT License.
The first milestone also includes image handling for posts:
- Writers can upload and insert images directly from the editor.
- Blogfrei saves uploaded image files as
images/<md5>.<extension>. - The editor inserts the URL of the stored image into the canonical Markdown post instead of embedding base64 image data.
- Blogfrei serves the stored images directly.
The container exposes /go/images as a volume. Uploaded images are available
from the application at /images/<md5>.<extension>.
Select the editor under [app] in config.ini:
[app]
editor = wysywig
wysywig and wysiwyg select the Blogfrei TOAST UI editor and are the default
when the option is absent. pad and bare keep the inherited plain Markdown
editors available.
Public Theme
Blogfrei bundles an MIT-licensed default public theme derived from the Bilberry Hugo Theme. All static assets are vendored in the repository, so public pages do not need to contact an external CDN.
Themes follow Hugo's directory conventions under themes/<name>/layouts.
Select the public theme under [app]:
[app]
blog_theme = default
default is used when the option is absent. Existing bilberry values are
migrated transparently for compatibility. The inherited theme
configuration option and per-blog Custom CSS editor are no longer used;
public appearance belongs to the selected Hugo-style theme.
Network Policy
Blogfrei does not perform vendor update checks, telemetry, or load remote embedded content by default. In particular, it does not contact WriteFreely, write.as, or GitHub for updates or telemetry.
Outbound traffic is limited to features explicitly configured or requested: ActivityPub federation, OAuth providers, email delivery, Web Monetization, and links opened by a user. When federation is enabled, Blogfrei verifies remote ActivityPub subscribers in the background immediately after startup and every eight hours. Valid actors have their inboxes and public keys refreshed; unreachable or invalid actors and their local follow records are removed.
The ActivityPub followers page summarizes followers by originating instance
before the complete follower list. Instances with one follower are grouped as
Others; repeated instances receive their own pie-chart segment, and the
summary table shows at most ten entries.
Email Newsletters
Admins configure newsletter delivery from Admin → Settings → Email Delivery. The SMTP relay, newsletter sender address, and sender display title are stored in the application database; the SMTP password is encrypted and stored as text safe for both SQLite and MySQL.
When email delivery is configured and a blog enables email subscriptions, the
public subscription form is available at /subscribe for single-user blogs and
/<blog>/subscribe for multi-user blogs. Newsletter emails contain the complete
rendered post HTML, including Markdown images rewritten with absolute image
URLs.
Audience Statistics
The Stats page retains the inherited per-post view and ActivityPub like counts and adds top-ten audience distributions with local CSS pie charts:
- browser families derived from the visitor's user-agent header;
- mobile or computer device classes derived from the user-agent header;
- primary browser languages, with regional variants grouped together; and
- referrer origins with links.
Audience percentage tables display two decimal places.
Only visits already classified as human are recorded. Blogfrei stores neither
visitor IP addresses nor complete referrer URLs: referrers are reduced to their
http:// or https:// origin before storage. Detailed statistics begin
accumulating after the database reaches migration V19.
Language values are intentionally synthetic: for example, de, de-DE, and
de-AT are all recorded as de.
Container Compatibility
The Blogfrei container is initially a drop-in replacement for the WriteFreely
container. An existing deployment should be able to replace its image with
git.keinpfusch.net/loweel/blogfrei:latest while keeping the same configuration file,
database, keys, mounted paths, port, and startup command.
Until an explicit migration is provided, changes must preserve compatibility with existing WriteFreely installations and data.
The container is built with Go 1.26.4 and uses Debian 13 as its runtime base.
Goals
Develop new Blogfrei work under the EUPL
New original Blogfrei work is licensed under the European Union Public Licence v1.2 (EUPL-1.2). Code inherited from WriteFreely, and modifications derived from that code, remain covered by the GNU Affero General Public License v3.0 (AGPL-3.0).
Add a true WYSIWYG editor
Replace the current editing experience with TOAST UI Editor, providing a real JavaScript WYSIWYG editor whose canonical output remains Markdown.
The editor should provide direct visual formatting while preserving clean, portable content and a focused writing experience. Blogfrei will preserve the TOAST UI Editor attribution and MIT licence notice.
Manage uploaded images
Allow writers to insert and upload images directly from the WYSIWYG editor.
Blogfrei should save uploaded images in a dedicated, configurable folder and serve them directly as part of the published blog. Image files should remain easy to inspect, back up, restore, and migrate together with Markdown content.
Store posts as Markdown files
Store blog posts primarily as Markdown files on the filesystem instead of in the database.
Each post should have a stable path based on its author and identifier, for example:
data/<author>/<post-id>.md
The Markdown files are the source of truth and should be easy to inspect, version, copy, restore, and migrate. The database may still hold accounts, configuration, and rebuildable indexes, but not the canonical post content.
Export portable archives
Export creates a persistent Hugo-compatible tree under
/go/export/<user>/content. Every post is a Markdown file with standard Hugo
front matter plus Blogfrei restore metadata, including its user and blog.
Referenced local images are copied to Hugo's static/images tree. Subsequent
exports add missing posts and images and atomically refresh changed posts before
creating the downloadable ZIP.
Import accepts this ZIP, safely extracts it under /go/import, and restores or
updates posts in the database from the front matter. The declared user and blog
must belong to the logged-in account.
An export should not require a database dump. Its contents should remain readable and usable without Blogfrei, making backup and migration simple.
Support Hugo themes
Allow blogs to load and use themes in the Hugo theme format.
This should make it possible to reuse the existing Hugo theme ecosystem while keeping theme installation and selection straightforward for Blogfrei administrators and writers.
Current Status
Blogfrei is at the beginning of its independent development. The current codebase provides the technical foundation inherited from WriteFreely; project identity, documentation, user experience, and deployment practices are being reworked for Blogfrei.
Development
Blogfrei is written primarily in Go, with JavaScript and LESS used for the web interface.
make
make test
Docker and Compose files are also available for local development and testing.
At normal startup Blogfrei automatically creates any missing encryption keys
under /go/keys and initializes an empty configured database. Existing keys
and initialized databases are left unchanged. Persist /go/keys,
/go/images, /go/export, /go/import, and the database storage as shown in
the included Compose files; do not use docker compose down -v unless those
volumes should be deleted.
For NFS-backed volumes, /go/keys, /go/images, /go/export, and
/go/import must be writable by the container runtime user daemon (numeric
UID/GID 1:1). Database storage requires an NFS server and mount options that
provide reliable POSIX locking; do not share one live database volume between
multiple database containers.
Definition of Done
Every Blogfrei change is complete only when:
- The requested implementation is finished.
- Relevant automated tests have been added or updated.
- The complete test suite passes.
- The race detector passes.
- Go source files pass
gofmt. golangci-lintpasses.gosecpasses.- The completed change is committed and pushed to the canonical repository.
Run the local quality checks with:
make dod
The full gate includes the smoke-test suite in smoketest/. It verifies the
editor, valid and invalid image storage cases, zero gosec findings, and the
built container contract. Run it separately with ./smoketest.py; use
./smoketest.py --build to include an additional clean Docker build.
The security gate requires zero gosec findings, zero reachable Go
vulnerabilities reported by govulncheck, and zero known npm vulnerabilities.
golangci-lint enforces govet, errcheck, staticcheck, and ineffassign.
Repository
The canonical repository is:
https://git.keinpfusch.net/loweel/blogfrei
License
Blogfrei uses a mixed-licence model:
- New original Blogfrei work: EUPL-1.2.
- Code inherited from WriteFreely and derivative modifications: AGPL-3.0.
- Independent third-party components: their respective licences.
See LICENSE, LEGAL.md, CREDITS.md, and the
licence texts under LICENSES/. Copyright for inherited and
third-party material remains with its respective copyright holders.