Architecture

The tale of sewifurs.org’s architecture is that of gradual evolution, not up-front design.

History

When sewifurs.org was created in November 2021, it was a static website. It was converted to a Flask web app and an accompanying bot in March 2022. The bot gained moderation functions in February 2024. Basic Telegram-based logins (for Bluesky Social custom handles) were supported in March 2025.

Following this, another developer joined the project, and I started to get inquiries from furries elsewhere who were interested in using sewifurs.org as a base for local furry community websites of their own. Major improvements were needed to allow sewifurs.org to grow. July and August 2025 saw the replacement of hardcoded privileges with a runtime-configurable permission system, a split into multiple independently-runnable modules, and a cloud-friendlier settings system. Integration testing came in September 2025, and Docker Compose in November 2025.

As of writing, sewifurs.org still requires a hard fork to use; branding, chat directory entries, and other details are hardcoded as SEWI Furs. However, as this project’s architecture continues to evolve, a future split into two projects may be possible: an unbranded core (tentatively called FurHome), and a branded client consuming that core (the ‘future’ SEWI Furs).

Modules

sewifurs.org is split into the following modules:

  • sewifurs.bot, the Telegram bot
    • Requires Telegram bot credentials.

    • Does not require a web server.

  • sewifurs.linkchecker, the chat directory link checker
    • Does not require Telegram bot credentials.

    • Does not require a web server.

  • sewifurs.website, the Flask web app
    • Does not require Telegram bot credentials, but some functionality won’t be available without them.

    • Requires a web server.

When doing local development, if you only need to interact with one part of sewifurs.org, you can run only that module with python3 -m sewifurs.module, replacing module with the appropriate name. sewifurs.website will spin up Flask’s built-in development server on port 5000 if run directly.

When deployed using Docker, all modules are started in a single container by run.sh, which also prepares static assets for serving by the nginx container, runs database migrations, and uses uWSGI to serve sewifurs.website.app behind nginx.

Current state

        architecture-beta
    group compose(cloud)[Docker Compose]
    group world(cloud)[Persistence]

    service bot(server)[Bot] in compose
    service linkchecker(server)[Link checker] in compose
    service website(server)[Website] in compose

    service script(disk)[Run script] in compose
    service sqlite(database)[sqlite3 database] in world
    service uwsgi(server)[uWSGI] in compose
    service telegram(internet)[Telegram] in world
    service dns(internet)[DNS providers] in world
    service nginx(server)[nginx] in compose
    service static(disk)[Static assets] in compose
    service docs(disk)[Documentation] in compose
    service config(disk)[Configuration] in world
    service sphinx(server)[Sphinx] in compose

    website:B -- T:uwsgi
    uwsgi:B -- T:nginx
    static:R -- L:nginx
    docs:L -- R:nginx
    docs:B -- T:sphinx

    script:B -- T:website
    bot:R -- L:script
    linkchecker:L -- R:script

    config{group}:B -- T:script{group}