Skip to content

Instantly share code, notes, and snippets.

@callebtc
Last active May 23, 2024 20:01
Show Gist options
  • Save callebtc/ec4d41582e356b63989f7a9a57bba831 to your computer and use it in GitHub Desktop.
Save callebtc/ec4d41582e356b63989f7a9a57bba831 to your computer and use it in GitHub Desktop.
Running a Cashu mint - best practices

Running a Cashu mint - best practices

Running a Cashu mint is fun but requires a good understanding of what you're dealing with, as is the case with other server software. Since we're dealing with an irreversible and privacy-preserving Chaumian Ecash system, some steps are required to run a safe, stable, and performant service that you can offer to your community.

In this document, I will outline some of the lessons I have gathered and the necessary precautions that will make running a mint a good experience for you. I will focus the Nutshell mint but the same should apply to other Cashu mint implementations. You can find almost all of the options below described in the example configuration file of Nutshell, please have a look at the different options that it provides you.

Choose your Lightning backend

Cashu works with Bitcoin, and particularly with Lightning. In order to be able to mint (create) and melt (destroy) ecash, you need to plug your mint into a Lightning backend. The best practice is to run your own dedicated Lightning node, such as LND or CLN. Running a Lightning node comes with its own responsibilities. You need to make sure that you properly back up your seed phrase, regularly export your static channel backups, and, ideally, have a copy of your node's database in case of a catastrophic incident. These are all things that apply to Lightning nodes, with or without ecash. You should be aware of these things whenever you run a Lightning node, so I will assume that your setup is safe and you have enough experience keeping your node happy and alive.

Nutshell also works with custodial backends. This is great for getting started quickly, to experiment or to run a mint for a temporary event, such as a conference. Currently, you can use your Strike or Blink account, or use an LNbits wallet of your choice to hook it up to a Nutshell mint.

Whichever method you choose, you absolutely need to make sure that the balance on your Lightning backend always exceeds the amount of ecash that you have issued. Do not, under any circumstance, repeat the same mistakes of the banking system by running a fractional reserve mint. You will run into terrible issues once your users start making Lightning payments and withdrawing their ecash. It's best if you can provide up-to-date proof of reserves reports to your users combined with a proof of liabilities for the ecash you're issuing (see here for more on this).

Limit amounts

Although you can't know what the balance of each individual user is (and therefore can't limit how much ecash a single user can accumulate), you can set limits for each transaction size in Nutshell. This makes it a lot harder to abuse a mint and do nasty things with it. It is strongly advised to set strict limits on transaction sizes so that it's clear that a mint should only be used for small payments for example, if that is your use case. You have three options in nutshell.

Maximum balance of your mint

This limits the overall risk of your mint by setting an upper limit to the total ecash that can be minted with it. Once your mint has issued so ecash that it reaches this limit, new ecash issuance won't be possible anymore until enough ecash has been destroyed again. This is the best way to make sure your operation does not grow out of control.

Maximum mint and melt amount

You can individually set the maximum amount for any operation that creates (mints) or destroys (melts) ecash on your mint. That way, you can make sure that only small transactions are possible.

Back up your data

Similar to a Lightning node, your ecash mint has a database in which all the spent ecash is stored (and other data as well). You must make sure that this data is safe and is regularly backed up. My preferred approach is to have database replication using Postgres or to mirror the SQLite database across different disks. It's also useful to have a fault-tolerant RAID storage system. This is a common feature for servers that you can rent today, so make sure that you always have a copy of your database. I additionally use custom cron jobs that push the mint's database to another server regularly so that I always keep a copy of it off-premise.

One interesting quirk about ecash services is that in the case of loss of your database, it won't affect the balances of your users as would be the case for many other services. The ecash is not stored on your server, it is stored on the users's device. What the server stores is all spent ecash. That means that if you lose your database for good, your users would be able to double-spend their ecash. Just to drive this point home: in the case of a fatal loss of data, while for normal services the user's balances would be affected, for ecash, the service provider has a much bigger problem than the users. I think that's a good thing. It's your responsibility to keep everything safe.

Every ecash mint also has a private key from which it derives its keysets which it uses to blind-sign the ecash. Obviously, you also need to back up this key, the same way you should keep your Bitcoin seed phrase secure in a safe place.

Make sure your database doesn't grow too large

Every ecash transaction your users make will leave entries in your database so that your mint can remember which ecash has been spent before. That means that, in the most basic case, your database's size will grow indefinitely, which is obviously not a sustainable state of affairs. I should note that this growth is fairly small and I have seen a Nutshell database grow to a mere size of a few dozen MB over a course of more than a year of usage. However, we would like to be able to use ecash until the heat death of the universe, and that's why there is a nice strategy to keep the size of your database constant over a long time which is keyset rotations. As mentioned above, every mint has a private key from which it derives its keysets. A keyset is the set of private keys used to sign the ecash. Inside a Cashu token, you can see the fingerprint (or ID) of the keyset with which it was signed.

As mentioned in the PoL scheme here, we can keep the size of the database constant by rotating to a new keyset epoch once every few months or a year. A keyset rotation will create a new keyset and new ecash signed with this new keyset will have a different keyset ID. You can imagine it like opening a new ecash counter from which users are served from now on. Ecash from an older keysets will still be accepted but only be exchanged for ecash from the new keyset. That way, ecash from the old epoch will slowly be taken out of circulation (once it is spent) and after a while, there will be only ecash from the new epoch. Once all (or almost all, you can't force dead users to spend their ecash) of the old ecash has been spent and replaced by ecash from the new epoch, you can delete that part of the database that remembered the ecash from the old epoch (to prevent its double spending). That way, a mint operator can regularly reduce the size of the mint's database which allows you to run the mint forever.

To rotate to a new keyset in Nutshell (version 0.15.3 as of this writing), you increment the derivation path in the config of the mint and restart it. For example, you would change the derivation from m/0'/0'/0' to m/0'/0'/1' to rotate to the new keyset. To prevent minting of ecash from an old keyset, you also need to set that keyset inactive. Currently, this is only possible manually by finding the keyset in the mint's database, and marking the column active as false. Now, only ecash from the new epoch (m/0'/0'/1') will be allowed to be minted and all ecash from the old epoch (m/0'/0'/0') will be slowly taken out of circulation. There is currently also no automated way to crop your database and get rid of the old spent table in Nutshell so this step also needs to be done manually as of this writing. However, since this is a vital part of running a sustainable mint, all this will be automated soon such that manual intervention with the database won't be necessary anymore and all of this will simply be possible by changing the configuration of the mint. More updates on this soon. For now, I'll leave you with an outlook and a clear path for an automated solution, and the option to do it manually in case it will be necessary in the short-term.

Limit access to your mint

An ecash mint works without accounts. Accounts are terrible for privacy but they allow you to do one good thing which is to rate-limit the requests of your users. As we've seen, a mint's database can only grow if we don't regularly rotate keys and crop the database. This poses an attack vector in which users can spend ecash to themselves forever and slowly force the mint's database to grow relatively quickly. This is not nice but it can happen. There are several mitigation strategies for this.

Solution 1: Rate limiting

This is the simplest solution to make sure that your mint won't be subject to a denial of service attack. Nutshell has a very basic IP-based rate limiter built in for reason which you can activate using the configuration. The built-in rate limiter allows you to limit the number of requests that can come from an IP for requests that do not grow the database (like a check for a token state, or a mint info request) and for transactions that do grow the database separately. You can use the default values in the config for this or choose values you prefer. Note: In order to be able to rate limit access per IP address, Nutshell needs to be able to see the IP address of the requests. Some reverse proxies strip away the IP from the requests, so make sure that your reverse proxy does not hide the IP from Nutshell. You can verify this by looking at the activity logs of Nutshell and checking whether you see the public IP of the incoming requests.

An alternative rate limiter that works great is fail2ban which you can use if you run Nutshell as a systemd service or as a standalone application. I have included a fail2ban config in the supplementary material at the bottom of this document that works nicely with Nutshell when run as a systemd service.

Solution 2: Fees (outlook)

We are currently working on an alternative and more sustainable solution to this problem which is to require users to pay a small fee for every ecash transaction they make. As we know from how Bitcoin was designed, this is the only solution that works for systems with privacy. At the time of this writing (May 2024), the Cashu protocol does not support fees yet. Once we do support fees, users won't be able to spend their ecash to themselves indefinitely and this denial of service attack won't be possible anymore.

Solution 3: Accounts (outlook)

As I've said before, accounts are terrible for privacy but they allow you to rate limit your users. The Cashu protocol will also support accounts at some point and we're currently working on the protocol specifications for that. This is a requirement many mint operators have expressed since they require to authenticate their users by law or due to other application-specific requirements. Accounts will make it very easy to rate limit requests per user and also ban users that misbehave. This is not ideal from a privacy perspective but it is something that people want, so we're going to build it soon.

Add contact information

The Cashu protocol allows a mint operator to leave contact information with which users can reach out to the operator of a mint. It also allows the operator to send warnings or other notices to the wallets using that mint (assuming that the wallet application is able to display this information). Make sure to add an email address or nostr public key or Twitter handle to your contacts so that your users can reach you if anything goes wrong. You can set your contact information in the Nutshell config file.

Final thoughts

Ecash is a fascinating technology and we're still at the very beginning of working out its kinks and issues. We have a very clear path with Cashu on how to solve the issues mentioned above to make sure that we can offer safe and useful services to our communities and at the same time protect the users and the operators of a mint. Cashu mints are not meant to obfuscate the origin of illegal funds or to allow bad actors to abuse public infrastructure. On the contrary, ecash is a technology that can be very useful for enabling payments for local or online communities, serve users of digital services such as games, social media platforms, or for content monetization. These are the use cases that we want to enable in the safest possible way.

Supplementary

Fail2ban config

This is a fail2ban config that works with Nutshell running as a systemd service called nutshell.service.

This is the jail file /etc/fail2ban/jail.d/nutshell.conf

[nutshell]
enabled = true
port = http,https
filter = nutshell
backend = systemd[journalflags=1]
journalmatch = _SYSTEMD_UNIT=nutshell.service
bantime = 1d
findtime = 120
maxretry = 60
banaction = ufw

This is the filter file /etc/fail2ban/filter.d/nutshell.conf

[Definition]
failregex = ^.*\| <HOST>:\d* -.*
ignoreregex =

Here is an example systemd service file /etc/systemd/system/nutshell.service for nutshell. Please adjust your directories and user names accordingly. In the example below, nutshell is running in the directory /home/cashu/cashuand is run by the user called cashu.

# Systemd unit for Nutshell
# /etc/systemd/system/nutshell.service

[Unit]
Description=nutshell
Wants=nutshell.service

[Service]
WorkingDirectory=/home/cashu/cashu
ExecStart=/home/cashu/.local/bin/poetry run python -m cashu.mint --port 2338 --host 127.0.0.1
User=cashu
Restart=always
TimeoutSec=120
RestartSec=30
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target
@Liongrass
Copy link

Very interesting, thank you for the write-up! As somebody running an experimental mint I've often wondered:
How do I responsibly shut down a mint?
As I don't know who my users are and have no way of communicating with them, what are my options to limit the inevitable rug pull? Above you hint that ecash tokens could have an expiration date? Or might there a way to communicate with my users?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment