Self-hostable, multi-author blogging platform — server-rendered with Quarkus, Qute, and HTMX. No React, Vue, or SPA build step.
Publish on your own domain, run several blogs per author, syndicate to the Fediverse, and keep a Git-backed archive — while readers get search, RSS, reading lists, highlights, and comments without a heavy client bundle.
Live reference: blogs.commit-mestre.dev (production instance of this codebase).
| HTMX-first UX | SPA-like navigation (hx-push-url, fragment swaps) with HTML from the server — predictable, crawlable, accessible. |
| Multi-blog authors | One main blog plus secondary blogs per user; platform paths and optional per-author subdomains. |
| Editorial workflow | Immutable publication snapshots, version history, editor-curated homepage, tag administration. |
| Reader engagement | Follow & email subscribe, reading list, text highlights & notes, threaded comments, in-app notifications, direct messages. |
| Open web & syndication | RSS (site, blog, tag, serie), ActivityPub (Mastodon-compatible), optional Git ↔ Jekyll sync. |
| Production-ready ops | PostgreSQL + Flyway, Docker/nginx deployment guide, Redis sessions, wildcard TLS for author subdomains. |
- Multi-blog — main + secondary blogs per user; public URLs by username and blog slug
- Draft / publish — working copy on
Post; immutablePostPublicationper publish; version history - Markdown & AsciiDoc — server-side rendering with image library
- Tags & series — taxonomy, tag landing pages, serialized posts
- Custom pages — global, per-user main blog, or per secondary blog (
/page/...URLs) - Git sync — optional Jekyll-shaped repos per blog (convention)
- Featured homepage — editor-curated hero and grid
- Author & blog directories — explore authors and blogs from the home page
- Search — HTMX modal + paginated
/search - RSS — site, blog, serie, and tag feeds
- Reading list — save posts for later; mark as read from the Reading hub
- Highlights & notes — select passages on posts; personal library in Reading hub
- Comments — threaded discussion with author moderation inbox
- Audience — follow (in-app notifications) and email subscribe per blog
- Notifications — in-app inbox, badge, mark-as-read
- Direct messages — user-to-user mailbox with block list
- Fediverse (ActivityPub) — authors opt in to syndicate posts from all blogs to Mastodon and compatible servers (feature doc)
- Share actions — LinkedIn, Bluesky, copy link on posts and blogs
- Roles —
USER,EDITOR,USER_ADMINISTRATOR,ADMIN - Featured posts — editors curate the homepage (
/editor/review) - User administration — platform operators manage accounts (
/administration/users) - Author analytics — dashboard views in Manage hub
- i18n — locale preference in header and account settings
- HTMX chrome — scoped refresh, toasts, modals (htmx-events.md)
Full UI route index with click paths: docs/feature-catalog.md.
| Layer | Technology |
|---|---|
| Backend | Quarkus (Java 17+) |
| Templates | Qute |
| UI dynamics | HTMX |
| Database | PostgreSQL, Hibernate ORM, Flyway |
| Cache / sessions | Redis (production) |
| Tests | JUnit 5, Selenium, App DSL + Given builders |
| Build | Maven |
Prerequisites: JDK 17+, PostgreSQL (or use Quarkus dev services).
git clone https://github.com/vepo/contraponto.git
cd contraponto
./mvnw quarkus:devOpen http://localhost:8080. Dev mode runs Flyway clean+migrate (%dev.quarkus.flyway.clean-at-start=true) and loads sample data from dev-import.sql.
All seeded users share the admin password (bcrypt hash in dev-import.sql).
| Username | Roles | Try |
|---|---|---|
alice, bob, carol, vepo |
USER |
Write, multi-blog URLs, library |
alice |
USER |
ActivityPub actor, secondary blog lab-notes |
dave |
USER |
Follower, notifications |
eve |
USER |
Email subscriber |
editor |
USER, EDITOR |
Featured curation /editor/review, tag admin |
admin |
ADMIN, USER_ADMINISTRATOR |
/administration/users, welcome post |
Default admin (from migration): admin — rotate password after first deploy (deployment checklist).
Step-by-step first deploy: docs/greenfield-deployment-tutorial.md
Environment variables: docs/deployment.md
Docker compose layout: sibling repo contraponto-prod.
| Doc | Contents |
|---|---|
| ARCHITECTURE.md | Patterns, URL map, schema overview, how to add features |
| docs/feature-catalog.md | UI features, roles, navigation paths |
| docs/domain-specification.md | Ubiquitous language, bounded contexts |
| docs/htmx-events.md | HTMX lifecycle, scoped refresh, auth allowlist |
| docs/adr/ | Architecture Decision Records |
| feature/ | Feature specs and changelog |
| AGENTS.md | Index for AI / Cursor agents |
| docs/application-guidelines.md | Route and flow descriptions |
| docs/ui-guidelines.md | Typography, colors, components |
| docs/git-jekyll-convention.md | Git repository layout for sync |
| docs/greenfield-deployment-tutorial.md | First production deploy (server, DNS, TLS, Docker) |
| docs/deployment.md | Production env-var checklist |
| .cursor/rules/ | Cursor project rules |
src/main/java/dev/vepo/contraponto/
├── activitypub/ # Fediverse federation (ActivityPub)
├── admin/ # Platform administration
├── auth/ # Authentication
├── blog/ # Blogs (multi per user), public blog pages
├── comment/ # Post comments
├── components/ # Header, menu, auth/publish forms
├── custompage/ # Static pages + cache filter
├── dashboard/ # Author dashboard
├── git/ # Git ↔ Jekyll import/export
├── highlight/ # Text highlights & notes
├── home/ # Featured homepage
├── image/ # Image upload API
├── library/ # Drafts & published lists
├── messaging/ # Direct messages
├── notification/ # Follow, subscribe, in-app + email
├── post/ # Posts, publication snapshots
├── profile/ # Profile settings
├── readinglist/ # Save for later
├── renderer/ # Markdown / AsciiDoc
├── rss/ # RSS feeds
├── search/ # Modal + full-page search
├── serie/ # Post series
├── shared/ # LoggedUser, pagination, infra
├── tag/ # Tag pages & curation
├── user/ # Users & roles
├── view/ # View counts
└── write/ # Editor
| Area | Path |
|---|---|
| Home | / |
| Write | /write, /write/draft/{id} |
| Writing hub | /writing, /writing/library, /writing/blogs, /writing/appearance |
| Reading hub | /reading, /reading/saved, /reading/highlights |
| Manage hub | /manage, /manage/dashboard, /manage/pages, /manage/comments |
| Account hub | /account, /account/security, /account/notifications, /account/messages |
| Editor hub | /editor, /editor/review, /editor/tags |
| Administration | /administration/users |
| Search | /search |
| Blog edit | /blogs/{id}/edit, /blogs/new |
| Tags (public) | /tags/{slug} |
| Site RSS | /feed |
Post and blog URLs: /{username}/post/{slug}, /{username}/{blogSlug}/post/{slug}, etc. (see ARCHITECTURE.md).
GITHUB_ACTIONS=true ./mvnw -B verify@WebTest— Quarkus + Selenium; injectAppfor navigation (headless whenGITHUB_ACTIONS=true).Given.user(),Given.post(),Given.blog(),Given.customPage()for data setup.Given.cleanup()when tests leave shared data behind.
Contraponto uses a documented seven-phase development process (feature analysis → architecture → tasks → approval → TDD → review). Start from feature/README.md and development-process.mdc.
AI-assisted workflows: AGENTS.md.
GNU General Public License v2.0.
Rules live in .cursor/rules/ (contraponto-core.mdc always applies). Start from AGENTS.md.