ClubLedger/docs/deployment.md
Claude 6aa4c45616
docs: update all four guides to reflect new features
Covers timezone settings, business address/branding/logo upload,
transfer types, transaction reference prefix, receipt label
localisation, separate charge/cashier footers, three-role system
(POS Staff / Cashier / Admin), five-option overdraft policy,
per-member overdraft override, withdrawal transaction type,
and manage.py CLI (reset-admin, reset-db).

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 17:12:09 +00:00

14 KiB
Raw Permalink Blame History

ClubLedger Deployment Guide

Overview

ClubLedger runs as a small web server on one computer (the server). Every other device on the same Wi-Fi network — tablets at the bar, a laptop at the cashier desk, a phone — opens the app in a normal web browser. No software is installed on the client devices.

                     Wi-Fi Router
                         │
        ┌────────────────┼────────────────┐
        │                │                │
   Server PC        Cashier Tablet    Bar Tablet
   runs ClubLedger  opens browser     opens browser
   :8000            http://192.168.1.x:8000

Part 1 Initial Setup

Prerequisites by Operating System

Linux (Debian / Ubuntu)

sudo apt update
sudo apt install git python3 python3-venv python3-full

macOS

Install Homebrew if you haven't already, then:

brew install git python3

Alternatively, download Python from python.org and install Git from git-scm.com.

Windows

  1. Download and install Python 3.11+ from python.org.
    • On the first installer screen, tick "Add Python to PATH" before clicking Install.
  2. Download and install Git from git-scm.com. Accept all defaults.

Get the Code

Open a terminal (Linux/Mac) or Git Bash / Command Prompt (Windows):

# Clone the repository
git clone https://github.com/kbenestad/clubledger.git
cd ClubLedger

# Or, if you downloaded a ZIP instead:
# Unzip it, then open a terminal in that folder

Start the Server

Linux / macOS:

chmod +x run.sh
./run.sh

Windows — open Command Prompt or PowerShell in the project folder:

python -m venv .venv
.venv\Scripts\pip install -r requirements.txt
.venv\Scripts\python main.py

On Windows you can also create a run.bat file with those three lines so double-clicking it starts the server in future.

The first time it runs, the server prints the default admin credentials to the terminal:

============================================================
  Default admin created  →  username: admin  password: admin
  Change this immediately in the Admin → Staff Accounts area.
============================================================

It then starts listening:

INFO:     Uvicorn running on http://0.0.0.0:8000

Open http://localhost:8000 in a browser on the same machine to confirm it works.


Part 2 Finding the Server's IP Address

For other devices to connect, you need the local IP address of the server machine — the address your router has assigned to it on the Wi-Fi network. This is usually something like 192.168.1.42 or 10.0.0.15.

Linux

hostname -I

The first address listed is normally the right one. Example output: 192.168.1.42

Or with more detail:

ip addr show | grep "inet " | grep -v "127.0.0.1"

macOS

Open System Settings → Network → Wi-Fi → Details. The IP address is listed there.

Or in a terminal:

ipconfig getifaddr en0   # Wi-Fi
ipconfig getifaddr en1   # Try this if the above gives nothing

Windows

Open Command Prompt and run:

ipconfig

Look for the section labelled Wi-Fi (or Wireless LAN adapter Wi-Fi). The value next to IPv4 Address is the server's address — for example 192.168.1.42.


Part 3 Connecting Other Devices

Once you have the server's IP address, any device on the same Wi-Fi network can open ClubLedger by entering this address in any browser:

http://192.168.1.42:8000

Replace 192.168.1.42 with your actual IP address.

This works on:

  • Other laptops and desktops (any browser)
  • iPads and Android tablets
  • Smartphones

No app installation needed. The browser is the client.


Part 4 Keeping It Running

By default, ClubLedger stops when you close the terminal. To keep it running continuously, set it up as a background service.

Linux systemd Service

Create a service file. Replace /home/youruser/ClubLedger with the actual path.

sudo nano /etc/systemd/system/clubledger.service

Paste the following (adjust User, WorkingDirectory, and the path to Python):

[Unit]
Description=ClubLedger Store Credit App
After=network.target

[Service]
Type=simple
User=youruser
WorkingDirectory=/home/youruser/ClubLedger
ExecStart=/home/youruser/ClubLedger/.venv/bin/python main.py
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable clubledger
sudo systemctl start clubledger

Check it is running:

sudo systemctl status clubledger

View logs:

journalctl -u clubledger -f

ClubLedger now starts automatically when the computer boots.


macOS launchd

Create a file at ~/Library/LaunchAgents/com.clubledger.plist.

Adjust the paths below to match your actual username and ClubLedger folder:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.clubledger</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/youruser/ClubLedger/.venv/bin/python</string>
        <string>/Users/youruser/ClubLedger/main.py</string>
    </array>
    <key>WorkingDirectory</key>
    <string>/Users/youruser/ClubLedger</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/Users/youruser/ClubLedger/clubledger.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/youruser/ClubLedger/clubledger.log</string>
</dict>
</plist>

Load it:

launchctl load ~/Library/LaunchAgents/com.clubledger.plist

To stop it:

launchctl unload ~/Library/LaunchAgents/com.clubledger.plist

Logs are written to clubledger.log in the project folder.


Windows Task Scheduler

  1. Create a file called start_clubledger.bat in the ClubLedger folder:
@echo off
cd /d "C:\Users\YourName\ClubLedger"
.venv\Scripts\python main.py
  1. Open Task Scheduler (search for it in the Start menu).
  2. Click Create Basic Task…
  3. Name it ClubLedger. Click Next.
  4. Trigger: When the computer starts. Click Next.
  5. Action: Start a program. Click Next.
  6. Browse to start_clubledger.bat. Click Next, then Finish.
  7. Right-click the new task → PropertiesGeneral tab → tick Run whether user is logged on or not.

ClubLedger will now start automatically at boot.

To check it manually, right-click the task and choose Run.


Part 5 Using a Fixed IP Address

Router DHCP can change the server's IP over time, which would break the URL bookmarked on client devices. Prevent this by assigning a static IP to the server machine.

  1. Log in to your router admin page (usually http://192.168.1.1 or http://192.168.0.1).
  2. Find DHCP Reservations or Static DHCP (varies by router brand).
  3. Find the server's MAC address and assign it a fixed IP (e.g. 192.168.1.10).

The router always gives the same IP to that machine. No changes needed on the machine itself.

Option B Static IP on the Machine

See your operating system's network settings to assign a static IP manually. Use an address outside the router's DHCP range to avoid conflicts. Set the gateway to your router's IP and DNS to 8.8.8.8 or your router's IP.


Part 6 Changing the Port

If port 8000 is in use by something else, edit the last line of main.py:

uvicorn.run("main:app", host="0.0.0.0", port=8080, reload=True)

Or pass it as an argument:

./run.sh --port 8080     # Linux / macOS
.venv\Scripts\python main.py --port 8080   # Windows

Devices would then connect to http://192.168.1.42:8080.


Part 7 Keeping It Off the Internet

ClubLedger has no HTTPS, no brute-force protection, and no rate limiting. It is designed exclusively for use on a private local network. This section explains why it is safe by default, and how to make that guarantee explicit.

Why it is already protected by default

Every home and business router uses NAT (Network Address Translation). NAT means:

  • Your router has one public IP address (assigned by your internet provider).
  • All devices on your network share that one address.
  • Inbound connections from the internet are dropped by the router unless you explicitly tell it to forward them.

ClubLedger listens on port 8000. Unless you specifically create a port-forwarding rule for port 8000 in your router settings, no one on the internet can reach it. This is the default state of every standard router.

What never to do

  • Do not create a port-forwarding rule for port 8000 (or whichever port ClubLedger uses) in your router's admin panel. This is the only action that would expose it.
  • Do not use a reverse proxy (nginx, Caddy, Apache) to publish it under a public domain unless you have added authentication, HTTPS, and rate limiting first.
  • Do not run it on a cloud server (VPS, AWS, etc.) without those same protections.

Verify it is not reachable from outside

To confirm ClubLedger is not publicly accessible, check from a device that is not on your Wi-Fi (e.g. a phone on mobile data, or ask someone outside the building):

  1. Find your public IP address — visit https://ifconfig.me from a computer on your network.
  2. From the external device, try to open http://<your-public-ip>:8000.
  3. It should time out or refuse the connection. If it loads ClubLedger, you have an unintended port-forwarding rule — remove it from your router immediately.

Restrict the server to the local network interface (belt-and-suspenders)

By default ClubLedger binds to 0.0.0.0, meaning it accepts connections on all network interfaces, including the local one. For extra certainty you can bind it only to your specific Wi-Fi interface IP so it cannot be reached even if a forwarding rule is accidentally added.

Find the server's local IP (e.g. 192.168.1.42), then edit the last line of main.py:

uvicorn.run("main:app", host="192.168.1.42", port=8000, reload=True)

With this change, the app only accepts connections from devices on your local network. Nothing outside the router can reach it regardless of router configuration.

Note: if the server's local IP changes (e.g. after a reboot), the app will fail to start. Use this option only after setting a fixed/reserved IP (see Part 5).

Add a firewall rule (optional, Linux only)

On Linux you can use ufw (Uncomplicated Firewall) to explicitly allow port 8000 only from your local network range and block everything else:

sudo ufw enable
# Allow from local network (adjust 192.168.1.0/24 to match your network)
sudo ufw allow from 192.168.1.0/24 to any port 8000
# Block port 8000 from everywhere else
sudo ufw deny 8000
sudo ufw status

This means even if your router accidentally forwarded port 8000, the firewall on the server itself would drop those packets.

To find your local network range: if your server's IP is 192.168.1.42, your range is almost certainly 192.168.1.0/24.


Part 8 Backing Up Data

All club data is stored in a single SQLite file: clubledger.db. To back up:

  1. If the app is stopped: copy clubledger.db to a safe location.

  2. If the app is running: SQLite may be in WAL (Write-Ahead Log) mode. Copy all three files if they exist:

    • clubledger.db
    • clubledger.db-wal
    • clubledger.db-shm

    Copy all three together in one operation so the backup is consistent.

  3. Logo file: if an admin has uploaded a club logo, also copy static/logo.* (e.g. static/logo.png). The exact extension depends on the file that was uploaded.

To restore from a backup:

  1. Stop the server.
  2. Replace clubledger.db (and clubledger.db-wal / clubledger.db-shm if present) with the backed-up copies.
  3. Copy any backed-up logo file back to the static/ folder.
  4. Restart the server.

Part 9 Security Notes

Risk Mitigation
Internet exposure NAT protects by default — do not add port-forwarding rules (see Part 7)
Unauthorised Wi-Fi access Use WPA2/WPA3 with a strong passphrase; consider a guest network for members separate from the staff network
Staff accessing admin settings Use separate staff and admin accounts; do not share admin credentials
Weak member PINs Encourage members to set at least 6-digit PINs
Data loss Back up clubledger.db regularly — just copy the file
Accidental deletion Delete only appears on zero-balance accounts; there is no bulk delete

Quick Reference

Task Command
Start the server (Linux/Mac) ./run.sh
Start the server (Windows) .venv\Scripts\python main.py
Find server IP (Linux) hostname -I
Find server IP (Mac) ipconfig getifaddr en0
Find server IP (Windows) ipconfig → IPv4 Address under Wi-Fi
Access from another device http://<server-ip>:8000
Stop the server Press Ctrl+C in the terminal
View systemd logs (Linux) journalctl -u clubledger -f
Backup data Copy clubledger.db to a safe location
Verify not internet-accessible From mobile data: http://<public-ip>:8000 should time out
Reset admin password python manage.py reset-admin (app can be running)
Wipe database (start fresh) Stop app first, then python manage.py reset-db