Installation
local, then production.
A local install to try it, a production install on Ubuntu, and the first run: claiming the site and the Admin console.
01Local install
The commands on this page are written for bash or zsh on macOS or Linux. On Windows, run them in WSL (Ubuntu) or Git Bash, not in PowerShell or cmd. The production server itself is Ubuntu.
corepack enable # once; or: npm install -g pnpm
pnpm install
cp .env.example .env # set PUBLIC_URL=http://localhost:8787
pnpm build # compiles the TypeScript source to dist/
pnpm start # runs dist/src/index.js with plain node
pnpm start runs the compiled JavaScript in dist/ with plain node. Run pnpm build again after you change or update the source. pnpm dev is for development: it runs the TypeScript source with tsx and restarts when files change. Stop with Ctrl+C. Always start from the project folder: the app reads .env, public/ and the default database path data/receipts.db relative to it.
02Production install on Ubuntu
- Node.js 22 and tools
sudo apt update sudo apt install -y curl ca-certificates build-essential python3 sqlite3 curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt install -y nodejs node -v - A system userThe app should not run as root.
sudo useradd --system --create-home --home-dir /opt/receipts --shell /usr/sbin/nologin receipts - Upload and installCopy the unzipped release to
/opt/receipts(scp or rsync), then:
Install with development dependencies: the build needs TypeScript. The running app needs only the production dependencies, so after the buildsudo chown -R receipts:receipts /opt/receipts cd /opt/receipts sudo -u receipts npm install # or: sudo corepack enable && sudo -u receipts pnpm install --frozen-lockfile sudo -u receipts npm run build # or: sudo -u receipts pnpm buildnpm prune --omit=devmay remove the rest (runnpm installagain before the next build). - Configure
Setsudo -u receipts cp .env.example .env sudo -u receipts nano .env sudo chmod 600 .envPUBLIC_URL=https://signals.example.com,ADMIN_SETUP_TOKEN(openssl rand -hex 24), optionallyADMIN_EMAIL,ADMIN_SECRET_KEY(openssl rand -hex 32), keepPORT=8787andDEMO_MODE=0(all variables). - Test once by hand
Then stop it with Ctrl+C and continue with systemd and Caddy.sudo -u receipts npm start curl http://127.0.0.1:8787/healthz # from a second SSH session: {"ok":true,...}
03First run: claim the site
- Have the setup secret readyOn a server, use the
ADMIN_SETUP_TOKENyou put in.env(recommended; the log then saysNo operator yet. Use ADMIN_SETUP_TOKEN twice…). Without a token, the server prints a one-time setup code instead: read it withjournalctl -u receipts, in the block after[admin] No owner account yet…. A new code is printed on every restart until both accounts exist. - Sign upOpen your site and click Create account. The page says the site has no operator yet and asks for the setup code. Enter the token (or the code), a name, a password of at least 10 characters and, if you set
ADMIN_EMAIL, that email. You are now the operator; the Admin console asks for the same secret to create its owner (next section). - Nobody else firstUntil the operator exists, nobody can create an account without the setup secret.
ADMIN_EMAILalone is not enough. - Remove the tokenOnce the operator and the console owner exist, delete
ADMIN_SETUP_TOKENfrom.envand restart.
04First run: the Admin console
Settings, fees, revenue, status, team and the audit log live in the Admin console at /admin (the MIKODES Admin Kit). It has its own accounts.
- Set the console key
ADMIN_SECRET_KEYin.env: 32 random bytes that encrypt the keys you save in the console. Without it a key is generated intodata/.admin-secret-keyon first start, with a warning. Move it into.envand back it up withdata/admin.sqlite: without it the stored secrets cannot be read. - Create the console ownerOpen
/admin, enter the same setup token (or printed code), your email and a password of at least 12 characters, then press Create owner and sign in. Without a token, a restart issues a new code. - Walk through Get startedThe console's setup wizard refuses to finish over a blocker (Admin console).
Only when you upgrade an install that saved settings before the console existed: the app writes those settings to data/admin-import.json and changes nothing until you import them (Admin → Export / Import, preview first). A fresh install writes no such file.
05Publishing yourself
To publish signals yourself, open Studio (sidebar: Become a provider), enter a provider name and confirm that published signals are permanent. Any member can become a provider this way (unless you turn on provider approval).