# Partage: an encrypted, local-first bill splitting app made with Elm

**URL:** <https://discourse.elm-lang.org/t/partage-an-encrypted-local-first-bill-splitting-app-made-with-elm/10893>\
**Category:** Show and Tell\
**Created:** [August 28, 2026, 8:41am UTC](https://discourse.elm-lang.org/t/partage-an-encrypted-local-first-bill-splitting-app-made-with-elm/10893 "2026-08-28T08:41:11Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [August 28, 2026, 8:41am UTC](https://discourse.elm-lang.org/t/partage-an-encrypted-local-first-bill-splitting-app-made-with-elm/10893/1 "2026-08-28T08:41:11Z")

</div>

Hi everyone,

I just deployed Partage on its final domain: [https://onpartage.eu](https://onpartage.eu)

 ![partage-home-screenshots](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/2X/2/2ddd72be1158539088e640feefc3878810b47504.webp)

## What it is

Partage helps a group of people keep track of who paid for what. Think of a trip with friends, a shared flat, or a family. You write down each expense, who paid, and who it was for. Partage does the math and tells you who owes who, and the simplest set of payments to settle everything.

If you already use an app like Tricount or Splitwise, Partage is that, with two main differences. Everything is encrypted in your browser, so the server only ever stores ciphertext and cannot read your group. And there are no accounts. No email, no password, no sign up. Your identity is a keypair created on your device the first time you open the app, and you invite people by sharing a link.

It is free and open source: [GitHub - mpizenberg/partage-elm · GitHub](https://github.com/mpizenberg/partage-elm)

## Features

You can record expenses, direct transfers between two people, and income. An expense can have several payers, and you can split it by shares or by exact amounts. Groups can mix currencies, with one default currency for the balances. The settlement plan is optimized to minimize the reimbursements transfer needed. You can add people who have not joined a group yet as virtual members to easily keep track of expenses. Nothing is ever really erased: editing an entry creates a new version and keeps the old one, so there is a full activity feed and a per member audit log. There is also search and filtering, CSV export, push notifications, and English and French translations (for now). It installs as a Progressive Web App on Android, iOS, macOS and desktop, and it works offline.

## A bit of architecture

The frontend is Elm 0.19.1. It is around 38k lines of Elm, or about 65k if you count the vendored dependencies the compiler builds along with it, plus 31 test modules. A few things may be interesting to people here.

**elm-ui 2.0 all the way.** The whole interface is built with @mdgriffith elm-ui 2.0. I love working with it, and I would make that choice again without hesitating. It is not published yet, so it is vendored in the repo, and that is part of why the line count above jumps when you include vendored code.

**The domain is a fold.** A group is an append only log of signed events. All the state comes from replaying that log:

```elm
applyEvents : List Envelope -> GroupState -> GroupState

```

Balances, the settlement plan, the activity feed, the audit log, the member graph, all of it is derived from that one pure function. Two devices that hold the same set of events derive exactly the same state, which is what makes offline editing converge without any server side merge. It is also very pleasant to test, because the whole domain is a function from a list to a record.

**Almost no ports.** All the async browser work goes through @andrewMacmurray `elm-concurrent-task`: Web Crypto, IndexedDB, HTTP, compression. So a flow like “sign this event, encrypt it, store it, push it to the relay, update the cursor” is a single typed Task chain in Elm, instead of a port ping-pong spread across the update function. I wrote three small packages for that, currently vendored in the repo: `elm-webcrypto`, `elm-indexeddb` ( [GitHub - mpizenberg/elm-indexeddb: IndexedDB support for Elm via elm-concurrent-task · GitHub](https://github.com/mpizenberg/elm-indexeddb) ) and `elm-pwa` ( [GitHub - mpizenberg/elm-pwa: PWA integration for Elm apps · GitHub](https://github.com/mpizenberg/elm-pwa) ), not published yet. In the end `public/index.js`, the entire JavaScript side of the frontend app, is about 650 lines.

**The server is deliberately dumb.** The backend is a small Hono and SQLite relay. It stores encrypted blobs and signed metadata, it authenticates with a bearer token derived from the group key, and it can decrypt nothing. Clients are the source of truth, so if the relay loses history a client can push it back.

Elm’s “no runtime exceptions” promise matters more than usual in this design. The user’s data lives in their browser, and if the app crashed in the middle of a write there would be no server copy to fall back on.

Translations are Fluent files compiled to Elm at build time with @andreasmolitor `travelm-agency`, and the build is @lydell `elm-watch` plus `esbuild`.

One last Elm note: the feedback button you will find on every screen opens a form hosted by @kantuni [Feedback.one](https://feedback.one), which is itself a product built in Elm. Nice to be able to stay in the family for that part too.

## How it was built

Partage started in January 2026 as a JavaScript app that I mostly vibecoded. I was struggling to keep the bugs in check as it became a Whack-A-Mole game for something that is important to be bug-free, so I restarted from scratch in Elm. I architected the app myself: the core data structures, the design decisions, the properties that have to hold, and LLMs were the help. Elm makes that help safer to accept. The compiler rejects whatever a model gets wrong about types and missing cases, and @jfmengels `elm-review` keeps them in check on the rest, especially by refusing dead code before it piles up.

Happy to answer any questions, and feedback is very welcome.

---

<div class="post-metadata">

**Author:** ![dwayne](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dwayne/32/5478_2.png) [@dwayne](https://discourse.elm-lang.org/u/dwayne)\
**Post date:** [August 28, 2026, 9:51am UTC](https://discourse.elm-lang.org/t/partage-an-encrypted-local-first-bill-splitting-app-made-with-elm/10893/2 "2026-08-28T09:51:42Z")

</div>

This is really cool and there’s a lot to learn from in the application. Congrats on getting it completed and deployed. Please consider submitting it to [Built with Elm](https://www.builtwithelm.co/).

---

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [August 28, 2026, 10:54am UTC](https://discourse.elm-lang.org/t/partage-an-encrypted-local-first-bill-splitting-app-made-with-elm/10893/3 "2026-08-28T10:54:50Z")

</div>

Yep PR in preparation.

BTW, the app is using elm-field, elm-form, and elm-validation 🙂 which are great!

---

<div class="post-metadata">

**Author:** ![paulh](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/paulh/32/5022_2.png) [@paulh](https://discourse.elm-lang.org/u/paulh)\
**Post date:** [August 28, 2026, 1:12pm UTC](https://discourse.elm-lang.org/t/partage-an-encrypted-local-first-bill-splitting-app-made-with-elm/10893/4 "2026-08-28T13:12:23Z")

</div>

> [@mattpiz](#):
>
> `elm-indexeddb` ( [GitHub - mpizenberg/elm-indexeddb: IndexedDB support for Elm via elm-concurrent-task · GitHub](https://github.com/mpizenberg/elm-indexeddb) ) and `elm-pwa` ( [GitHub - mpizenberg/elm-pwa: PWA integration for Elm apps · GitHub](https://github.com/mpizenberg/elm-pwa) ), not published yet.

I played around with Elm and indexeddb some time ago but never pushed it forward, will look at your implementation soon, been watching your posts re elm-pwa, **really** interested in that, hope you’re able to publish at some point 👍

---

<div class="post-metadata">

**Author:** ![john\_s](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/john_s/32/4425_2.png) [@john\_s](https://discourse.elm-lang.org/u/john_s)\
**Post date:** [August 28, 2026, 6:53pm UTC](https://discourse.elm-lang.org/t/partage-an-encrypted-local-first-bill-splitting-app-made-with-elm/10893/5 "2026-08-28T18:53:41Z")

</div>

This is awesome!

As a side note, does it include any implementation of [Bistromathics](https://hitchhikers.fandom.com/wiki/Bistromathics)? I know that has long been considered an open problem and I was just wondering how you might have solved it.

In all seriousness, this is really nicely engineered along several different dimensions! Wow. Thank you for sharing.

---

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [August 28, 2026, 8:56pm UTC](https://discourse.elm-lang.org/t/partage-an-encrypted-local-first-bill-splitting-app-made-with-elm/10893/6 "2026-08-28T20:56:30Z")

</div>

Never heard of that ahah ^^

---

<div class="post-metadata">

**Author:** ![kantuni](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/kantuni/32/5397_2.png) [@kantuni](https://discourse.elm-lang.org/u/kantuni)\
**Post date:** [August 31, 2026, 11:56pm UTC](https://discourse.elm-lang.org/t/partage-an-encrypted-local-first-bill-splitting-app-made-with-elm/10893/7 "2026-08-31T23:56:40Z")

</div>

Thank you for trusting us with your feedback! ❤

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [September 10, 2026, 11:57pm UTC](https://discourse.elm-lang.org/t/partage-an-encrypted-local-first-bill-splitting-app-made-with-elm/10893/8 "2026-09-10T23:57:11Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
