Deployment
service, HTTPS, backups.
Run Receipts as a service behind an HTTPS proxy, block countries if you need to, and back up the database. Plus your responsibilities as an operator.
01Run it as a service (systemd) Not tested here
Create /etc/systemd/system/receipts.service:
[Unit]
Description=Receipts signal marketplace
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=receipts
Group=receipts
WorkingDirectory=/opt/receipts
ExecStart=/usr/bin/npm start
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now receipts
sudo systemctl status receipts # should say "active (running)"
journalctl -u receipts -f # live logs; Ctrl+C to leave
The app stops cleanly on systemctl stop: it stops the background jobs, the bot and the console, then closes the database. WorkingDirectory matters: the app reads .env from there.
02HTTPS with Caddy Not tested here
The app listens on plain HTTP port 8787. Caddy sits in front, gets a free Let's Encrypt certificate and renews it. Install Caddy following caddyserver.com/docs/install, open ports 80 and 443 (sudo ufw allow 80,443/tcp), then replace /etc/caddy/Caddyfile with:
signals.example.com {
encode gzip
reverse_proxy 127.0.0.1:8787
}
sudo systemctl reload caddy
Set TRUST_PROXY=1 so the sign-in limits use the visitor's address, and keep port 8787 closed to the internet. nginx works as well: proxy to 127.0.0.1:8787, set X-Forwarded-For, and manage the certificate yourself (for example with certbot).
03Country blocking
Admin → Access → Blocked countries blocks visitors by two-letter code (for example US GB). Blocked visitors get HTTP 451. The app does not look up countries itself: it reads a request header your proxy or CDN adds.
- Cloudflare (proxied): adds
CF-IPCountrywhen IP Geolocation is on. That is the default header; nothing else is needed. - Caddy or nginx without Cloudflare: add a GeoIP module (for example a MaxMind GeoLite2 database) that sets a header such as
X-Country, and enter that name in Admin → Access → Country header.
A request without the header is let through, and if your server is reachable without Cloudflare a visitor can send any value. Treat blocking as a reasonable measure you document, not a promise. /static/ and /webhooks/ are never blocked, so Stripe can always reach you.
04Backups
Everything (accounts, signals, seals, batches, payments, settings) is in data/receipts.db; the console's settings, team and audit are in data/admin.sqlite. Back up .env (with ADMIN_SECRET_KEY) separately and privately.
The databases run in WAL mode. Do not just copy the files while the app runs; use SQLite's online backup, which is safe while running Tested:
cd /opt/receipts
sqlite3 data/receipts.db ".backup backup.db"
sqlite3 data/admin.sqlite ".backup admin-backup.sqlite"
A nightly backup with cron (sudo crontab -u receipts -e):
15 3 * * * cd /opt/receipts && mkdir -p backups && sqlite3 data/receipts.db ".backup backups/receipts-$(date +\%F).db" && find backups -name 'receipts-*.db' -mtime +14 -delete
Copy backups off the server too. To restore: stop the app, replace the database file, delete any -wal and -shm files next to it, start the app. The owner can also download a snapshot from Operations → Data backup.
Losing the database loses the sealed records. The Bitcoin timestamps prove a batch root existed at a time, but the event bodies needed to show that proof live only in your database.
05Your responsibilities
This is information, not legal advice. Talk to a qualified lawyer where you and your users are before you charge anyone.
- Signal services may be regulated. In some countries publishing or selling trading signals, or taking a share of them, may count as investment advice or require a licence. Use Blocked countries where needed and keep the risk notice on.
- No auto-trading. Receipts only publishes and measures signals. It never executes trades, never connects to anyone's exchange account and never holds user funds.
- No profit claims. Measured results describe the past under a stated method. Do not advertise them as expected returns.
- Payments and taxes. Paying providers, invoicing, VAT/sales tax and your terms of service are your responsibility.
- Market data. Prices come from Binance and Bybit public endpoints; their terms apply, including whether you may display their data publicly and commercially.
- Privacy. You store members' emails, names, optional Telegram IDs and payment records. Publish a privacy policy that fits your jurisdiction.
- Licence. Receipts is sold under the Envato Market licence (Regular or Extended). If your members pay (subscriptions, fees), read the difference between the two on the CodeCanyon item page before you launch.