Reference

Run it yourself

Run the whole stack on your own machine, with no outside network: a local database, a local chain and the app.

Start a local environment

You need Docker, Foundry, Node.js 24 with pnpm, and OpenSSL.

Shell
infra/local/up.sh

The script:

  • starts the project's Postgres container and creates a database called linvesther_local, separate from any other database on that server;
  • starts a local chain (Anvil) and deploys the registry contracts to it. The chain's state is saved in infra/local/.state, so it survives a restart;
  • writes .env.local with the addresses, fresh secrets and a signing key for your instance. It is never committed.

Then start the API and the web app in two terminals, and open the address it prints.

Shell
set -a; source .env.local; set +a; pnpm --filter @linvestherzk/api start
pnpm --filter @linvestherzk/web dev

Build the worker once with cargo build -p binance-worker --manifest-path services/Cargo.toml. Add --no-default-features if you do not need zero-knowledge proofs, which require the RISC Zero toolchain.

infra/local/down.sh stops the chain, and infra/local/up.sh --reset forgets the local chain and database and starts over.

What is kept

StateWhereNote
Sign-insPostgres, for 30 daysSigned out when the session expires or you sign out.
Identities and passkeysPostgresWithout them, nobody could sign in again after a restart.
Published claimsPostgresA link someone shared keeps working until the claim expires.
Profile settingsPostgresPrivacy mode never switches itself off.
Connected accounts and historyThe worker's Postgres tablesCredentials are stored encrypted.
Ownership and registrationThe chainPublic, and rebuilt from events if the database is lost.

All of this needs DATABASE_URL. Without it the API starts anyway, warns, and keeps sign-ins, identities and claims in memory, so they are lost when it restarts. Sign-in challenges are always in memory, so run a single API instance.

Your instance and trust

Your instance signs the figures it reads with its own key, so anything it proves is self-attested for everyone else: nobody has a reason to trust a key they have not chosen to trust. That is by design. It makes your own numbers fully usable to you, and it keeps a self-run instance from passing for anyone else's.

Your instance reports its collector at /public/collector. Someone who trusts you can add that fingerprint to their own list. See Verify a proof.

Beyond your machine

  • Passkeys need HTTPS on a real domain. Set WEBAUTHN_RP_ID and WEBAUTHN_ORIGIN to it; the session cookie is then marked Secure.
  • Set CORS_ORIGINS to the origins of your web app. A state-changing request from any other origin is refused.
  • Point CHAIN_RPC_URL and the contract addresses at the network you deploy to, and set CHAIN_DEPLOY_BLOCK so the indexer does not scan from the start of the chain.
  • Behind a reverse proxy or tunnel, set TRUST_PROXY_HOPS to the number of proxies in front of the API (usually 1). Rate limits are per client address, and without this every client looks like the proxy and shares one allowance. Leave it unset when the API is reachable directly.
  • Keep the database on a private network. The default password in the repository is public; the local environment binds Postgres to this machine only, and yours should be reachable only by the API and worker.
  • Serve the web app with next build and next start, not the development server.
  • Set your own POSTGRES_PASSWORD. Secrets can also be mounted as files (BINANCE_WORKER_ENCRYPTION_KEY_FILE, BINANCE_WORKER_A0_SIGNING_KEY_FILE, mode 600), which keeps them out of the process environment. RELAY_MAX_PER_HOUR caps the gas you will fund.
  • Keep BINANCE_WORKER_ENCRYPTION_KEY and BINANCE_WORKER_A0_SIGNING_KEY safe. The first protects stored credentials; losing the second changes your collector identity.
Run it yourself