繁體中文 ← Back to KakePhooa

Manual

Installation

This chapter is for people running KakePhooa themselves (the Self-hosted Edition): from download to your first login, plus upgrades and recovery. Settings are covered in the "Configuration" chapter.

The Self-hosted Edition is free for a single person to use, with no time limit.

Support scope

It's maintained by one person, so replies may be slow. Not covered: your own Docker/Podman environment, reverse proxy, TLS certificates, domain setup and database tuning. Those are part of your hosting environment.

Problems with the software itself (it won't start after following these steps, or a feature doesn't match this Manual) are welcome. Please include the version number (see "Version" below).

Requirements

Keep it running in the system tray

If you don't want to keep a terminal or console window open, start it with kakephooa-tray (kakephooa-tray.exe on Windows), included in the tarball and the zip. It starts the app in the background, puts an icon in the system tray, and opens your default browser. Closing the browser doesn't stop the app.

It works on Windows and on the GNOME and XFCE desktops on Linux.

  1. Download, verify and unpack the release as in step 1 of "Install from the Linux tarball" or "Install on Windows".
  2. Double-click kakephooa-tray (kakephooa-tray.exe on Windows) in the unpacked directory. If there's no config.toml there yet, it first copies config.toml.example to config.toml: the embedded database, accepting connections from this computer only. To change settings, edit that file as described in the "Configuration" chapter, then press "Restart" in the menu.
  3. The browser opens a "Starting KakePhooa" page, which moves on to the login page once the app is ready. The first start sets up the database, so the page says "Setting up the database" and it takes longer than usual, depending on the computer. If it takes too long, the page suggests choosing "View log" in the tray menu to see why.

Left-click the icon to open the app. The right-click menu:

ItemWhat it does
StatusStarting, Running, Stopped or Failed to start
VersionThe version that's running
Copy URLA submenu of addresses to copy to the clipboard, for pasting on your phone or another device: "This computer" (public_url; shown as "Public URL" when it's a domain or Tailscale address), "Local network" (appears once "Allow phone connections" is ticked) and "Tailscale" (appears when Tailscale is installed and signed in on this computer and your phone can reach the app)
OpenOpens the app in your default browser
Connect a phoneOpens a page with the address for your phone and its QR code; see "Connecting a phone" below
View logOpens logs/server.log, where all of the app's messages go
Allow phone connectionsWhen ticked, other devices on the same network can reach the app too; see "Connecting a phone" below
Start at sign-inWhen ticked, it starts when you sign in to the computer. When started this way it stays in the tray without opening the browser
RestartStops and starts the app again, for example after editing config.toml
QuitStops the app and removes the icon

Connecting a phone

By default the app only accepts connections from this computer, so your phone can't reach it.

  1. Tick "Allow phone connections" in the menu. It sets [server] ip in config.toml to 0.0.0.0 and restarts the app, then opens the "Connect a phone" page once it's ready. Unticking sets it back to 127.0.0.1.
  2. Put your phone on the same network as this computer (for example your home Wi-Fi) and scan the QR code on the page. The page follows your browser's language; switch between "中文" and "English" in the top-right corner.
  3. When Windows Firewall asks whether server.exe may use the network, allow it.

Sign up for your account on this computer before ticking it: while the system has no accounts, the first person on the network to sign up becomes its User.

This connection uses http, so you can log in, record Expenses and check your budget, but you can't add it to your home screen, record offline or use Passkeys. For those, switch to an HTTPS address as described in the "Connecting from your phone" chapter; once public_url is an HTTPS address, the "Connect a phone" page shows that address instead.

If Tailscale is installed and signed in on this computer, the page also shows a Tailscale address: with Tailscale on, your phone can connect even when it's not on the same network. Once you've set up tailscale serve as described in the "Connecting from your phone" chapter, the page shows only its HTTPS address, and you don't need to tick "Allow phone connections". Not having Tailscale doesn't affect starting the app.

When IP is set as an environment variable (including in .env), it takes precedence and "Allow phone connections" can't be clicked.

Install with Docker or Podman

The ready-made image is ghcr.io/single9/kakephooa, for linux/amd64 and linux/arm64; it contains the binary from the Linux tarball. NAS apps such as Container Manager (Synology) and Container Station (QNAP) can pull it directly. The compose file and settings below are starting points, not a setup guaranteed to work everywhere; adapting them to your machine is part of hosting it yourself.

  1. Create a directory (for example kakephooa) and create config.toml in it, with public_url set to the address this machine will actually be reached at:

    [database]
    mode = "embedded"
    
    [server]
    public_url = "http://192.168.1.10:8080"
    

    Create config.toml before the first start: if the file doesn't exist, Docker creates a directory of that name and the container fails with Is a directory. To recover, delete that directory, create the file, and run docker compose down before starting again. Every setting is listed in config.toml.example in the Linux tarball and in the Configuration chapter.

  2. In the same directory, create docker-compose.yml (see "Embedded database" under "Docker Compose examples" below), replacing 1.1.1 with the version to install (see the releases page).

  3. Start it: docker compose up -d (or podman-compose up -d).

  4. Wait a few seconds for the database migrations to finish, then open http://<this machine's address>:8080.

  5. Sign up as usual to create your User (see "Your User" below).

Permissions on config.toml

The container runs as a non-root user, so config.toml has to be readable by other users. When it isn't, the container keeps restarting and its log shows config.toml 讀取失敗:Permission denied (os error 13). On this machine, run:

chmod 644 config.toml

Synology and some other NAS devices give files created or copied into a shared folder the folder's ACL, which only allows specific accounts, so the user inside the container can't read them. Sign in to the NAS over SSH and run the command above in the directory that holds config.toml to switch it back to ordinary file permissions.

Building the image yourself

If you'd rather not use the ready-made image, the Linux tarball includes a Dockerfile and a docker-compose.yml that wrap the tarball's binary in an image on your own machine, with nothing to compile. In the unpacked tarball's directory, copy config.toml.example to config.toml and run docker compose up -d --build. Without compose, run docker build -t kakephooa:<version> . to build the image. The Dockerfile does only this: start from debian:bookworm-slim, install ca-certificates, tzdata and wget, copy server and public/ to /app, and run as a non-root user listening on 0.0.0.0:8080. Change it as you like, for example to use another base image. Building the image downloads packages, so where containers can't reach the network (as on some NAS devices), use the ready-made image instead.

On macOS, install Docker Desktop and follow the same steps; Docker pulls the image for your Mac's architecture.

A reverse proxy and TLS aren't part of this compose file. You don't need TLS when you only use the system from a computer. To add it to a phone's home screen, record Expenses offline or use Passkeys, the address has to be an HTTPS domain; see the Connecting from your phone chapter.

Docker Compose examples

Both examples below work as a starting point; they differ only in where the database lives. Put the two files in the same directory, replace 1.1.1 with the version to install, set public_url to the address this machine will actually be reached at, and run docker compose up -d.

name: kakephooa fixes the compose project name. When the directory is renamed or moved and you start it again, it keeps using the same volume (for example kakephooa_data) instead of a new, empty one.

Embedded database (default)

The database runs inside the app's container, and its data lives in the data volume. This is the setup of the docker-compose.yml in the tarball.

docker-compose.yml:

name: kakephooa

services:
  app:
    image: ghcr.io/single9/kakephooa:1.1.1
    restart: unless-stopped
    volumes:
      - ./config.toml:/app/config.toml:ro
      - data:/app/data
    ports:
      - "8080:8080"

volumes:
  data:

config.toml:

[database]
mode = "embedded"

[server]
public_url = "http://192.168.1.10:8080"

To back up, back up the data volume (see "Backing up the whole database" in the "Configuration" chapter).

External database

The database runs in a separate PostgreSQL container. This suits you if you'd rather manage PostgreSQL yourself, for example to back it up or tune it with tools you already use.

docker-compose.yml:

name: kakephooa

services:
  app:
    image: ghcr.io/single9/kakephooa:1.1.1
    restart: unless-stopped
    volumes:
      - ./config.toml:/app/config.toml:ro
    ports:
      - "8080:8080"
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: kakephooa
      POSTGRES_PASSWORD: change-me
      POSTGRES_DB: kakephooa
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U kakephooa -d kakephooa"]
      interval: 5s
      timeout: 5s
      retries: 10

volumes:
  pgdata:

config.toml:

[database]
url = "postgres://kakephooa:change-me@db:5432/kakephooa"

[server]
public_url = "http://192.168.1.10:8080"

Install from the Linux tarball

  1. Download the tarball for your machine's architecture from the releases page (kakephooa-<version>-linux-<arch>.tar.gz, where <arch> is x86_64 or aarch64; run uname -m if unsure), along with SHA256SUMS and SHA256SUMS.asc, and verify them (see "Verify the release" below). Then extract it:

    tar xzf kakephooa-<version>-linux-<arch>.tar.gz
    cd kakephooa-<version>-linux-<arch>
    

    The layout is fixed: server, kakephooa-tray, public/, config.toml.example, LICENSE, README-install.md, README-install.en.md, THIRD-PARTY-NOTICES.md. server looks for public/ in the directory you run it from, so keep the layout as it is.

  2. Copy config.toml.example to config.toml and set public_url (or use environment variables instead; see the "Configuration" chapter).

  3. Start it:

    ./server
    

    The database migrations run on first start. The embedded database lives in a data/ directory next to where you run server; it must be on a local Linux filesystem, not a Windows drive or a network share (see "The embedded database's data directory" in the "Configuration" chapter).

Install on Windows

  1. Download kakephooa-<version>-windows-x86_64.zip from the releases page, along with SHA256SUMS and SHA256SUMS.asc, verify them, and extract the zip. The layout is the same as the Linux tarball, with server.exe instead of server.

  2. Copy config.toml.example to config.toml and set public_url.

  3. Double-click start.bat in the extracted folder. The window stays open after server.exe exits, so the error message remains readable. To start it from PowerShell instead:

    .\server.exe
    

    The first time, Windows Firewall may ask whether server.exe may use the network; allow it if other devices need to reach the app. The embedded database lives in data\ in the folder you run it from. Wherever this chapter says ./server, use .\server.exe.

Verify the release

Every tarball and zip comes with SHA256SUMS (checksums) and SHA256SUMS.asc (the maintainer's GPG signature), so you can confirm the download isn't corrupted and really comes from this project.

The release-signing key:

KakePhooa Releases <[email protected]>
Fingerprint: ACC6 7AF4 2A28 A134 C82C  9A1A E27C A53B 662A 823F

The public key is published alongside the releases as kakephooa-release-signing-pubkey.asc. Import it, then verify:

gpg --import kakephooa-release-signing-pubkey.asc
gpg --verify SHA256SUMS.asc SHA256SUMS
sha256sum -c --ignore-missing SHA256SUMS

On Windows without sha256sum, run Get-FileHash kakephooa-<version>-windows-x86_64.zip in PowerShell and compare the hash with the matching line in SHA256SUMS (gpg --verify works the same with Gpg4win).

The first time, gpg: WARNING: This key is not certified with a trusted signature is normal: it only means you imported the key without certifying it. What matters is Good signature from "KakePhooa Releases <[email protected]>" in the output, with a fingerprint matching the one above.

Your User

Sign up through the normal sign-up page. While there are no Users yet, the first sign-up is always accepted, whatever signup_policy says. That User is yours, and it's the only one a Self-hosted Edition deployment supports: further sign-ups are refused.

Running the software as a service that people outside your household or organization can sign up for isn't permitted by its license (clause 4 of LICENSE). If that's what you want, email [email protected].

Version

The current version (v<x.y.z>) is shown at the bottom of the account Settings page. Include it when reporting a problem.

Forgot password (Mail-less Mode)

By default the Self-hosted Edition doesn't send email (Mail-less Mode), so "Forgot password" can't send you a link. Set a new password with the built-in command instead:

# Docker/Podman:
docker compose exec app ./server set-password [email protected]

# Tarball:
./server set-password [email protected]

The password is read from standard input and isn't shown as you type. Once it's set, every existing session of that User is logged out; log in again with the new password.

Turning off Two-Step Verification for a User

If a User has Two-Step Verification on and has lost their authenticator app, their Passkeys and their Recovery Codes, turn it off for them with the built-in command:

# Docker/Podman:
docker compose exec app ./server reset-mfa [email protected]

# Tarball:
./server reset-mfa [email protected]

This removes the User's authenticator app and every Passkey, cancels all Recovery Codes and logs out every session of that User. They can then log in with their password; ask them to turn Two-Step Verification on again. If no User has that email, the command exits with status 1.

Upgrading

When a newer version is out, a notice appears at the top of the page after you log in (see "Checking for new versions" in the "Configuration" chapter).

  1. Back up the whole database first (see "Backing up the whole database" in the "Configuration" chapter).
  2. Download the new tarball or zip from the releases page and restart. With Docker/Podman, change the image version in docker-compose.yml to the new version and run docker compose up -d. If you build the image yourself, copy config.toml into the new version's unpacked directory and run docker compose up -d --build there. Database migrations run automatically on start. If you start it with kakephooa-tray, press "Quit" in its menu first; after starting from the new version's directory, tick "Start at sign-in" again.
  3. Downgrading isn't supported. Data written by a newer version isn't guaranteed to be readable by an older one. The backup you took before upgrading is your way back.

Health check

GET /healthz reports whether the database is reachable. The healthcheck in docker-compose.yml uses it, and your own monitoring can call it too.

Usage statistics

"Usage Statistics" on the account settings page shows how many times this deployment is used each day:

You can switch between the last 7, 30 and 90 days, and "Daily breakdown" lists the numbers for each day. Dates are in UTC, so a new day starts at 8 a.m. in Taiwan.

These numbers stay in your own database. Only counts are kept, not who did what, and nothing is sent anywhere.