Family Tree app
We document the installation of the self-hosted family tree app running at familytree.piribauer.ch. The source is available at git.piribauer.ch/familytree.
The app is built with FastAPI and stores its data in a SQLite
database. Authentication is handled entirely by an Authentik outpost
via nginx forward-auth; the app reads the
X-Authentik-Username and X-Authentik-Email
headers injected by nginx and never touches the Authentik API
directly.
We assume that nginx and an Authentik outpost are already
running, and that the repo has been cloned to
/home/julian/familytree.
Python environment
The system Python on Arch Linux is PEP 668-managed and rejects
direct pip install calls. We therefore use a virtual
environment:
$ python -m venv /home/julian/familytree/venv
$ /home/julian/familytree/venv/bin/pip install -r /home/julian/familytree/requirements.txt
Database directory
The app stores its data in a SQLite database under
/var/lib/familytree/. We create the directory and
assign ownership to the service user:
# mkdir -p /var/lib/familytree
# chown julian:julian /var/lib/familytree
Environment file
The app reads its configuration from
/home/julian/familytree/.env. We create it with the
database URL:
DATABASE_URL=sqlite:////var/lib/familytree/familytree.db
Systemd service
We install the unit file included in the repo:
# cp /home/julian/familytree/familytree.service /etc/systemd/system/familytree.service
# systemctl daemon-reload
# systemctl enable --now familytree
The service runs as user julian, listening on
127.0.0.1:8766. We can verify it started cleanly
with
$ systemctl status familytree
Nginx
We copy the nginx config from the repo and enable it:
# cp /home/julian/familytree/nginx/family /etc/nginx/sites-available/family
# ln -s /etc/nginx/sites-available/family /etc/nginx/sites-enabled/family
# nginx -t && systemctl reload nginx
The config forwards all requests through the Authentik outpost
running on 127.0.0.1:9000. On success, nginx extracts
the username and email from the outpost response and passes them to
the app:
auth_request_set $authentik_username $upstream_http_x_authentik_username;
auth_request_set $authentik_email $upstream_http_x_authentik_email;
proxy_set_header X-Authentik-Username $authentik_username;
proxy_set_header X-Authentik-Email $authentik_email;
Unauthenticated requests are redirected to the Authentik login page.
Schema migrations
SQLAlchemy creates missing tables on startup with
create_all, but does not modify tables that already
exist. If a column is added to the models, it must be applied to the
existing database manually. For example, adding a notes
column to the persons table:
$ sqlite3 /var/lib/familytree/familytree.db "ALTER TABLE persons ADD COLUMN notes TEXT;"
Invitation flow
Users are invited by email address from within the app. The
invitation is stored as a pending record linked to the tree. When
the invited user logs in for the first time, their Authentik email
is matched against all pending invitations and the corresponding
tree memberships are created automatically. Trees have three roles:
owner, editor, and viewer.
Only owners can invite others or change roles.