receipt-upload

Self-hosted receipt collection

Receipt uploads without user accounts or heavyweight infrastructure.

A small Go application for collecting phone-camera receipts through a private link, combining images into compact PDFs, and giving one administrator a clean review queue.

Receipt with an upload arrowSingle binary · SQLite · Local storage

Quick start

Run it locally in a few commands

Build the binary, initialize its config and data directory, replace the default password, then start the server.

Terminalbash
make install
receipt-upload init
# Edit ADMIN_PASSWORD in ./config/.env
receipt-upload config check
receipt-upload serve
Default addressDocumentation is available at http://localhost:8725/. Administration lives at /admin/login.

How it works

One administrator, simple links for everyone else

01

Configure

The admin creates cardholders and stores, then chooses the public hostname and secret upload code.

02

Share

Send the generated private upload URL to cardholders. They do not create accounts or sign in.

03

Collect

Cardholders select their name, add expense details, and attach one or more receipt photos.

04

Review

The app resizes images, builds one PDF per expense, and makes it available in the admin portal.

Configuration

Environment-style config file

The application reads the file passed with --config (default: ./config/.env). Runtime environment variables are not merged in.

ADMIN_USERNAMEAdministrator login name
ADMIN_PASSWORDAdministrator password
SECRET_KEYSigns admin session cookies
UPLOAD_TOKENInitial private upload-link token
APP_BASE_URLPublic origin used when building links
DATA_DIRSQLite database and receipt PDFs
MAX_UPLOAD_MBMaximum size of one submission

Deployment

Docker or a standalone binary

Standalone

receipt-upload serve \
  --config ./runtime/app.env \
  --host 0.0.0.0 \
  --port 8725

Docker

docker build -t receipt-upload .
docker run --rm -p 8725:8725 \
  -v "$PWD/config/.env:/app/config/.env:ro" \
  -v receipt-upload-data:/app/data \
  receipt-upload

Persist DATA_DIR. It contains both app.sqlite3 and generated receipt PDFs.

Operational notes

Designed to stay small

Image processing included

Phone photos are resized to a maximum 1600px dimension and JPEG-compressed before PDF generation. No external converter is required.

Local, portable data

Metadata lives in one SQLite database and receipt PDFs live beside it under the configured data directory.

Protected administration

Three failed login attempts ban an IP for 24 hours. The CLI can list and remove ban records.