No description
Find a file
2026-07-12 16:38:15 -06:00
backend Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
dev Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
docker Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
scripts Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
tests Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
.dockerignore Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
.env.example Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
.envrc.example Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
.gitignore Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
Caddyfile Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
docker-compose.yml Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
Dockerfile Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
entrypoint.sh Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
LICENSE Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
main.py Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
pyproject.toml Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
README.md Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
requirements-dev.txt Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
requirements.txt Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00
uv.lock Knowledge Hub — self-hosted blog, bookmarks & community platform (FastAPI + PostgreSQL) 2026-07-12 16:38:15 -06:00

Knowledge Hub

A self-hosted content platform built with FastAPI and PostgreSQL — a blog, bookmark manager, writeups collection, and a small community layer, wrapped in a hardened, security-first HTTP stack. It powers a personal technology workshop: a place to publish long-form writing, curate links, and let readers comment.

This repository is a public, portfolio snapshot of the application. It is a real, running codebase, trimmed of deployment-specific infrastructure so it stands on its own.

What it does

  • Blog — full CRUD, tags, server-rendered pages with client-side Markdown rendering (marked.js + DOMPurify for safe HTML).
  • Bookmarks — categorized, tagged link collection.
  • Writeups — long-form technical notes with the same tag model.
  • Library — books organized into shelves with per-chapter reading.
  • Community — polymorphic comments (on posts, writeups, and chapters), comment upvotes, in-app notifications, and public profile pages. All writes are moderated; admins can remove content.
  • Accounts — approval-gated registration, login, and profiles. Optional single-sign-on via any standard OIDC provider (feature-flagged, off by default).
  • Feedback — an anonymous contact form.
  • Privacy — a proxy layer for an external privacy-scanner service, plus an opt-in, first-party, aggregate-only page-hit counter (no per-user tracking, no third-party analytics; disabled by default).

Architecture

backend/
  core/
    app.py            # FastAPI application + middleware wiring
    routes/           # one router module per feature area
    analytics.py      # first-party aggregate hit counter (opt-in)
  db/
    models.py         # SQLAlchemy models (UUID primary keys throughout)
    db.py             # engine / session management
    migrations/       # Alembic migrations
  security/
    auth.py           # JWT-cookie auth, role checks, CSRF verification
    deps.py           # FastAPI dependencies
  static/             # Bootstrap 5 + vanilla-JS ES-module frontend
  utils/              # helpers, constants
dev/                  # local dev tooling (TUI runner, dev browser)
tests/                # pytest suite (Python) + Node test for the JS URL guard

Security model

  • Auth: JWT stored in an HTTP-only cookie (access_token).
  • CSRF: double-submit — a csrf_token cookie must match an X-CSRF-Token header on every state-changing request.
  • Authorization: admin-only writes gated by a requires_role("admin") dependency.
  • Rate limiting: slowapi on sensitive endpoints.
  • Headers: a middleware sets CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy.
  • Output safety: all user-supplied content is escaped before DOM insertion; outbound link targets are normalized and validated against open-redirect and javascript: payloads.
  • Data model: UUID primary keys everywhere (no enumerable integer IDs).

Tech stack

Layer Choice
Backend FastAPI, Starlette, Uvicorn
ORM / DB SQLAlchemy 2, PostgreSQL 17, Alembic migrations
Auth PyJWT, bcrypt, CSRF double-submit
Frontend Bootstrap 5.3, vanilla-JS ES modules, marked.js, DOMPurify (no build step)
Rate limit slowapi
Runtime Docker (non-root container via gosu)

Running it

The app is containerized. A stranger can bring it up with Docker Compose:

cp .env.example .env          # then fill in the blanks (see below)
docker compose up -d --build

This starts three services: the FastAPI app, a PostgreSQL 17 database, and an optional privacy-scanner sidecar. The app listens on port 6969 inside the container; put a reverse proxy in front of it (a minimal Caddyfile is included).

Migrations run automatically on container start (alembic upgrade head via the entrypoint).

Configuration

Copy .env.example and set at minimum:

  • SECRET_KEY — JWT signing key. Generate with python -c "import secrets; print(secrets.token_urlsafe(64))".
  • POSTGRES_* — database name, user, and password.

SSO/OIDC is off unless explicitly enabled and configured; the app runs fully with local accounts only.

Tests

pip install -r requirements.txt -r requirements-dev.txt
python -m pytest

Tests run against an in-memory SQLite database — no Docker or Postgres needed. The frontend URL-safety guard has its own Node test under tests/js/.

License

Released under the GNU Affero General Public License v3.0 (AGPL-3.0-only) — see LICENSE. AGPL is the network-copyleft choice: anyone who runs a modified version of this app as a hosted service must offer their users the corresponding source.

Bundled fonts (Inter, JetBrains Mono) are distributed under the SIL Open Font License; the color theme and favicon are original to the project.

License

AGPL-3.0-only — see LICENSE for the full text.

A commercial license is available for use outside the AGPL-3.0 terms — contact Info@Seglamater.com. (Formal commercial-license terms are being finalized.)

Versions previously published under GPL-3.0 remain available under GPL-3.0.