homepage of the hrones net
  • Python 33.9%
  • CSS 33.2%
  • HTML 24.8%
  • Dockerfile 8.1%
Find a file
matt e8f75dad5b
All checks were successful
Deploy / deploy (push) Successful in 5s
Add ShowTimeFinder public link, move Git to private
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 11:00:20 -04:00
.forgejo/workflows Point deploy DEPLOY_DIR at ~/docker/hrones-net on the Pi 2026-06-10 03:23:14 -04:00
static Add Flask homepage app with login-gated private services 2026-06-10 03:11:28 -04:00
templates Add Flask homepage app with login-gated private services 2026-06-10 03:11:28 -04:00
.dockerignore Add Forgejo Actions deploy pipeline 2026-06-10 03:15:48 -04:00
.gitignore Add Flask homepage app with login-gated private services 2026-06-10 03:11:28 -04:00
app.py Add Flask homepage app with login-gated private services 2026-06-10 03:11:28 -04:00
Caddyfile.example Add Flask homepage app with login-gated private services 2026-06-10 03:11:28 -04:00
compose.yaml Add Forgejo Actions deploy pipeline 2026-06-10 03:15:48 -04:00
Dockerfile Add Flask homepage app with login-gated private services 2026-06-10 03:11:28 -04:00
hashpw.py Add Flask homepage app with login-gated private services 2026-06-10 03:11:28 -04:00
LICENSE Initial commit 2026-06-09 21:09:21 -04:00
README.md Fix README deploy-dir paths to ~/docker/hrones-net 2026-06-10 03:30:13 -04:00
requirements.txt Add Flask homepage app with login-gated private services 2026-06-10 03:11:28 -04:00
services.yaml Add ShowTimeFinder public link, move Git to private 2026-06-12 11:00:20 -04:00

hrones-net

Homepage of the hrones net — a small Flask app that lists links to services. Public services are always visible; private ones (Jellyfin, etc.) appear only after logging in.

The app listens on 127.0.0.1:8010; put Caddy in front of it for TLS (see Caddyfile.example). If PASSWORD_HASH is empty, login is disabled and only public services show.

Continuous deployment

.forgejo/workflows/deploy.yml re-deploys on every push to main: a self-hosted Forgejo runner on the Pi fetches the new commit and runs docker compose up -d --build. One-time setup on the Pi:

  1. Clone to the deploy directory CI keeps in sync. The runner must be able to git fetch it non-interactively, so embed a read-scoped Forgejo access token (User Settings → Applications → Generate Token) in the remote — or use the plain URL if the repo is public:

    mkdir -p ~/docker && git clone \
      https://matt:<TOKEN>@git.hrones.net/matt/hrones-net.git ~/docker/hrones-net
    
  2. Let the runner deploy. Its user must be in the docker group and able to write ~/docker/hrones-net; Docker + the compose plugin must be installed.

  3. Add the app secrets in Forgejo → repo → Settings → Actions → Secrets:

    • SECRET_KEYopenssl rand -hex 32
    • PASSWORD_HASH — build once and run the helper, then paste the output:
      cd ~/docker/hrones-net && docker compose build
      docker compose run --rm web python hashpw.py
      
  4. Point the workflow at your runner. runs-on is already host and DEPLOY_DIR is /home/matt/docker/hrones-net in deploy.yml — adjust if yours differ. Push to main to trigger a deploy.

  5. Front it with Caddy (Caddyfile.example) for TLS.

The push that first adds this workflow runs it immediately — it will fail until steps 13 are done. That's expected.

Run manually

Without CI, on the Pi (secrets via the shell — never an .env file, since the $ in a pbkdf2 hash gets mangled by env-file parsing):

export SECRET_KEY="$(openssl rand -hex 32)"
export PASSWORD_HASH='pbkdf2:sha256:...'   # from `python hashpw.py`
docker compose up -d

Develop (local)

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

python hashpw.py                 # prints a PASSWORD_HASH value
export PASSWORD_HASH='pbkdf2:sha256:...'

python app.py                    # http://localhost:5050

Adding services

Edit services.yaml. Entries under public: are always visible; entries under private: require login. Each entry takes name, url, and optional description and icon (emoji). Changes are picked up on the next page load — no restart needed.