Skip to content
feather-pagePublic

About

CMS for small static websites that look hand-coded, load nothing external and live on your own domain.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

Feather-Page CMS

CI

Feather-Page CMS is a web interface for managing small static websites. It is a Phoenix application (LiveView admin, SQLite) that renders sites to static HTML and deploys them by FTPS to the site owner's own hosting.

Design Goals

  • A simple interface that can be used by non-technical users.
  • Website should look like a real web-developer hand-coded them.
  • Deployed websites should be static.
  • Deployed websites should NOT load any external resources.
  • Deployed websites should be as small as possible.
  • Deployed websites should be SEO friendly.
  • Deployed websites are on domains that belong to the user.

Development

Requirements

  • Erlang/OTP 28 and Elixir 1.19
  • rclone for deployments (FTPS)
  • d2 (at least v0.9.0; the Dockerfile pins the exact version) to render diagram blocks; without it diagrams show their D2 source
  • chroma (v2; the Dockerfile pins the exact version) to highlight code blocks; without it code blocks stay plain
  • brotli (optional) to precompress exported sites as .br; without it only .gz is written
  • No system libvips needed: vix downloads a precompiled libvips on first compile
  • Noto Color Emoji (optional, fonts-noto-color-emoji on Debian) for colour emoji in the favicons and share cards the export draws; without it emoji come out in whatever font has them
  • inotify-tools (optional, Linux) for live reload in development
  • Node.js with npm: the asset build installs the editor's packages from assets/package-lock.json (npm ci); mix setup downloads the standalone tailwind and esbuild binaries
  • Git: mix deps.get (part of mix setup) fetches the component library setzkasten and daisyUI from GitHub

Setup

mix setup          # deps, database (SQLite file feather_dev.db), seeds, assets
mix phx.server     # http://localhost:4000

The seeds create the super admin admin@example.com and a demo site. There are no passwords: enter the email on the login page and open the magic link from the dev mailbox at http://localhost:4000/dev/mailbox.

Uploaded images are stored in storage/ (git-ignored).

Mix tasks

Task Purpose
mix feather.create_user EMAIL [--super-admin] Create (or update) a user who can log in by magic link
mix test Run the test suite
mix precommit Compile with warnings as errors, unlock unused deps, format, run Credo, run the tests
mix ecto.reset Drop, migrate and seed the development database

In a release the same is available through bin/feather eval, see Feather.Release (migrate/0, create_user/2). bin/server migrates and starts the app. Feather.Release.backfill_suggestions/0 fills in the model's alt texts, descriptions and short post titles for content from before OLLAMA_URL was set; deploy the sites by hand afterwards.

Environment variables

Variable Description Default
PHX_HOST Public host name of the CMS (prod); the inboxes of the sites' fediverse accounts live on it example.com
PORT HTTP port 4000
SECRET_KEY_BASE Signs cookies and tokens (prod, required)
DATABASE_PATH SQLite database file (prod, required)
POOL_SIZE Database connections (prod) 5
STORAGE_PATH Images, rendered diagrams, the pictures of the authors of approved fediverse replies and build output (prod, required) dev: storage/
CONFIG_ENCRYPTION_KEY 32 random bytes, base64; encrypts deployment credentials, archive.org keys and the private keys, mentions, Timeline posts, members' replies and outgoing deliveries of the sites' fediverse accounts (prod, required) fixed dev/test keys
UNSPLASH_ACCESS_KEY Unsplash API key (optional)
HARDCOVER_TOKEN Hardcover API key for the book search (optional; without it books are searched on OpenLibrary)
OLLAMA_URL Base URL of an Ollama instance (e.g. http://ollama:11434), which suggests alt texts, descriptions and titles of short posts (optional; without it no suggestions are made)
OLLAMA_MODEL The Ollama model that makes the suggestions; it must read images gemma4:26b-a4b-it-qat
FEDIVERSE_PROXY URL of an HTTP proxy (e.g. http://proxy:3128) that all requests to fediverse servers tunnel through, so they do not learn where the CMS runs (optional); the CMS still checks every address, but the proxy itself must reach only public addresses. Even with the proxy the CMS resolves the servers' names itself, so its DNS should go through a resolver that does not reveal where it runs
IMAGE_CLEANUP false disables the daily deletion of orphaned images enabled
SMTP_ADDRESS, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD Outgoing mail (prod) localhost, 587

Generate a CONFIG_ENCRYPTION_KEY with elixir -e 'IO.puts(Base.encode64(:crypto.strong_rand_bytes(32)))'.

Fediverse servers post to the inboxes of the sites' accounts below /fediverse/inbox/ on PHX_HOST. A proxy in front of the CMS that asks for a login or blocks countries must open that prefix to the internet, matching it after decoding the path and resolving dot segments, and must reject encoded slashes. The CMS answers every other path below the prefix with 404 and limits the requests per client; when the proxy connects from a private network, the client is the last public address in its X-Forwarded-For.

Connecting an agent

Add https://<PHX_HOST>/mcp as a custom connector in Claude (or as the MCP server of another client) and allow it on the consent page. Tokens are bound to this URL, built from PHX_HOST.

Project structure

  • lib/feather/: contexts with the domain logic (Accounts, Sites, Content, Books, Media, Publishing, Redirects, OAuth, Fediverse)
  • lib/feather/static_site/: static export and preview (sinks, feed, sitemap, Markdown alternates, llms.txt, the fediverse documents, the theme registry Themes); each theme's EEx templates live in themes/<theme>/
  • lib/feather_web/: the LiveView admin, built with the shared component library setzkasten (Tailwind CSS and daisyUI), and the MCP server at /mcp (FeatherWeb.MCP, its tools in mcp/tools/)
  • priv/static_site/: the stylesheets and fonts of the themes (themes/<theme>/) and the translations of the generated static sites
  • test/: ExUnit tests, the executable specification (DECISIONS.md, 0008)
  • ops/: production setup (compose.yml, stack.env.example); CI pushes the image to ghcr.io/feather-page/cms on every push to main
  • CONTEXT.md, DECISIONS.md: domain language and architecture decisions

License

TODO: Add license information.

About

CMS for small static websites that look hand-coded, load nothing external and live on your own domain.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages