Willem van den Ende 3eae3afcbb demo skill: look up routing and demo users in the project
Steps 2 and 4 carried copy-paste from another project (lib/hub_web/router.ex,
alias Hub.Accounts, /users/log-in with user[email]). A stale path there sends
the agent to a router that does not exist, and a guessed module alias fails at
run time.

Both are now generic instructions to read the project:

- route map: locate the router by searching for `use Phoenix.Router`, then read
  its scopes, pipelines and live_sessions to see what is public and what needs
  a session
- authentication: only when the route actually needs it; take credentials from
  documented demo accounts or the project's seeds, create a demo user through
  the project's own path (seeds, registration route, or the accounts function
  found by grep - never a guessed alias), and read the login route and form
  selectors off the router and rendered form

The port example keeps its concrete app/endpoint pair, now labelled as an
example with the grep that finds the right names, plus a note to use the
project's toolchain/build wrapper instead of bare `mix`.

make check green.
2026-10-26 21:26:19 +00:00
2026-03-19 22:14:19 +00:00
2026-03-18 13:22:12 +00:00
2026-03-19 22:14:19 +00:00
2026-03-24 14:21:22 +00:00
2026-03-18 11:30:27 +00:00

Firehose

Willem's personal blog — a modular static blog built on Blogex, powered by Phoenix and NimblePublisher.

Posts live as markdown files in git and compile into the BEAM at build time: zero database, zero runtime I/O, sub-millisecond reads.

The project structure is modular, I wanted to have a personal blog, and an easy way to add an 'engineering' blog to fledgling SaaS products, as well as release notes. The release notes now also serve to announce new features on my blog.

Status: Experimental. Works on my server ;-).

Enjoy, Willem van den Ende

Project structure

This is a monorepo with two parts:

firehose/
├── app/                    # Phoenix application (OTP app: :firehose)
│   ├── lib/firehose/       # Application logic
│   ├── lib/firehose_web/   # Web layer (controllers, live views, components)
│   ├── lib/firehose/blogs/ # Blog definitions (engineering, release notes)
│   ├── priv/blog/          # Markdown posts
│   └── mix.exs
├── blogex/                 # Blogex library (multi-blog engine)
│   ├── lib/
│   └── mix.exs
├── Dockerfile              # Deploy build; ARG pins must match mise.toml
├── mise.toml               # Runtime versions (Elixir, Erlang, Node)
└── scripts/check-pins.sh   # Verifies the pin sync; runs in `make check`

Two blogs are configured:

Blog Route Description
Engineering /blog/engineering Main blog
Release Notes /blog/releases What's new in an app

Prerequisites

  • Elixir 1.15+ and Erlang/OTP 26+ (managed by mise)
  • Node.js (managed by mise, for Tailwind + esbuild assets)
  • PostgreSQL 16+ running on localhost:5432

Setup

1. Install runtimes

mise install

This installs Elixir, Erlang, and Node at the versions specified in mise.toml.

Toolchain pins

mise.toml (local dev toolchain) and the Dockerfile ARG pins (deploy builder image) must agree — local builds and deployed images should run the same Elixir/OTP. make check runs scripts/check-pins.sh to verify this; when it reports a MISMATCH, fix the file it names. Upstream drift of these pins (newer Elixir/OTP/Debian releases) is reported separately by dokku-updates firehose check from the dokku-maintenance repo.

2. Start PostgreSQL

You need PostgreSQL running locally with a postgres user (password: postgres).

Option A — Docker (quick):

docker run -d --name firehose-pg -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:16

Option B — Dev container:

cd .devcontainer && docker compose up -d db

Option C — System install:

Install PostgreSQL normally and ensure a postgres user with password postgres exists.

3. Install dependencies and set up the database

cd app
mix setup

This runs:

  • mix deps.get — fetch Hex and git dependencies
  • mix ecto.create — create the firehose_dev database
  • mix ecto.migrate — run database migrations
  • mix run priv/repo/seeds.exs — seed initial data
  • mix assets.setup — install Tailwind and esbuild
  • mix assets.build — compile CSS and JS assets

4. Start the server

mix phx.server

Visit localhost:8056 in your browser.

Note: The dev server runs on port 8056 by default. Override with PORT=4000 mix phx.server.

Development commands

All commands below are run from the app/ directory.

Command Description
mix phx.server Start the dev server with live reload
iex -S mix Start an IEx session with the app
mix test Run the test suite
mix precommit Run all quality gates (compile, credo, format, test)
mix format Format Elixir/HEEx code
mix credo --strict Run Credo static analysis

Writing blog posts

Posts are markdown files with Elixir frontmatter. Place them under app/priv/blog/<blog-name>/:

app/priv/blog/
├── engineering/
│   └── 2026/
│       └── 05-01-hello-world.md
└── release-notes/
    └── 2026/
        └── 05-01-v0-1.md

Frontmatter format:

%{
  title: "Hello World",
  author: "Willem van den Ende",
  tags: ~w(elixir phoenix blogex),
  description: "My first blog post"
}
---


This is my first post. It compiles into the BEAM at build time.

In dev mode, edited .md files trigger live reload automatically.

Blogex

The Blogex library is a multi-blog engine for Phoenix apps, powered by NimblePublisher. See blogex/README.md for full documentation.

License

MIT — see LICENSE

S
Description
personal blogging platform.
Readme MIT
14 MiB
Languages
HTML 84.2%
Elixir 12.9%
Shell 1%
Makefile 0.6%
CSS 0.5%
Other 0.8%