# Picsou: a self-hosted net worth dashboard for a French household

> Picsou is a self-hosted personal finance dashboard that aggregates bank accounts, brokerage, crypto, loans and French property into one net worth view, built as a React front end over a Spring Boot backend on PostgreSQL. It reaches French institutions that PSD2 cannot, through local read-only login sidecars, and its own documentation tells you not to put it on the public internet.

**Cloeille/picsou-finance** — Self-hosted personal finance dashboard  Track bank accounts, brokerage, crypto, and net worth — all in one place.

- Repository: https://github.com/Cloeille/picsou-finance
- Website: picsou.zoeille.fr
- Stars: 567 · Forks: 61
- Language: Java
- License: NOASSERTION
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/cloeille-picsou-finance

## The problem: one household, many banks, no consolidated view

Picsou is described as a self-hosted personal finance dashboard that tracks bank accounts, brokerage, crypto and net worth in one place. That is a narrower ambition than a wealth management platform, and the narrowness is the point.

The concrete target is a French household with several accounts spread across institutions. A current account and a Livret at one bank, a PEA or CTO at a broker, an Amundi Épargne Salariale plan with its FCPE lines, a few crypto wallets, and a property. Each institution has its own portal, its own export format and its own login, and reconciling them by hand is the recurring chore the project exists to remove.

The technical choices follow the target market. Enable Banking handles bank sync over PSD2 and OAuth across more than 2000 EU banks, which covers most ordinary accounts. For the rest, the project uses local read-only sidecars: a separate container that performs the login and two factor step for BoursoBank, Bourse Direct, Amundi and Trade Republic, and hands the resulting session to the main application. The README is explicit about why this exists for BoursoBank: it reaches the securities account that PSD2 cannot.

So the project is not pretending that a PSD2 consent covers everything. Where the open banking standard does not reach, it drives the bank's own web login in a side container, which is more work and more risk, and which is the honest way to reach a PEA position.

Beyond the accounts, there are the market prices, with CoinGecko for crypto and Yahoo Finance for stocks and ETFs, and a property valuation that is unusual for a dashboard: automatic monthly valuation of French property from open data, using DGFiP transactions via Cerema, IGN geocoding and the INSEE price index, with no API key and no subscription. Ownership shares between family members, mortgages linked to the property they finance, and gross and net property equity are all first-class.

The features that surround the data are the ordinary ones of a household tool: historical net worth snapshots with stacked area charts, savings goals with deadlines, a multi-member family model where one admin manages children's or a spouse's profile, Finary import by CSV or API sync, English and French interfaces, and dark mode with flash-free theme switching.

## Ports and adapters, and a two-tier identity model

The architecture diagram shows a React front end built with Vite and Bun talking to a Spring Boot backend on Tomcat at port 8080, which talks to PostgreSQL on 5432, with four outbound integrations hanging off the backend: Enable Banking over PSD2 and OAuth, CoinGecko, Yahoo Finance, and Trade Republic over a WebSocket.

Two design decisions in the backend are worth more than the diagram.

The first is Ports and Adapters. The README names the ports explicitly, BankConnectorPort, PriceProviderPort, TradeRepublicPort, BoursoPort among them, and states the intent: swap providers without touching business logic. That is a large amount of work for a personal finance app whose value is in the breadth of connectors, because it means a new institution is a new adapter rather than a change to the domain model. It also means the connectors can be tested against a fake port, which matters when the alternative is a test that needs a real bank login.

The second is the two-tier identity. There is an AppUser, which is the login identity, and a FamilyMember, which is the domain identity. Every entity is scoped by member_id, and an admin can act on behalf of a managed profile by passing a memberId query parameter. So the data model has no concept of a shared family account: it has people, and each record belongs to one of them, and sharing between people is expressed per resource with a value of NONE, ALL or MANUAL.

The security layer is described with similar precision. Authentication is JWT in HttpOnly cookies with SameSite=Lax, chosen for Safari on iOS compatibility, with refresh token rotation. Two factor is opt-in TOTP per user with hashed recovery codes, ten single-use codes, a 90-day Remember Me cookie with rotating tokens, a Trust this device option, and per-session revocation from the settings. API secrets at rest are mandatory AES-256-GCM encrypted, and the list includes Binance credentials, TOTP secrets and bank session tokens, which is the right set to treat as sensitive. Rate limiting uses Bucket4j on the login, the MFA challenge, the sync endpoints and the data export.

## Getting started: pull the images and let the entrypoint generate secrets

The prerequisites are Docker and Docker Compose v2, with an Enable Banking account optional if you want bank sync.

The first step is to clone the repository:

```bash
git clone https://github.com/Cloeille/picsou-finance.git
cd picsou-finance
```

The project publishes pre-built multi-arch images for amd64 and arm64 to the GitHub Container Registry, so there is nothing to compile. The image list is more interesting than a single image would be, because each of the login sidecars is its own package:

- ghcr.io/cloeille/picsou-finance for the application itself, frontend and backend together
- ghcr.io/cloeille/picsou-finance/tr-auth for the Trade Republic auth sidecar
- ghcr.io/cloeille/picsou-finance/bourse-direct-auth for the Bourse Direct login and 2FA sidecar
- ghcr.io/cloeille/picsou-finance/amundi-auth for the Amundi Épargne Salariale login and 2FA sidecar
- ghcr.io/cloeille/picsou-finance/bourso-auth for the BoursoBank login and 2FA sidecar

Only the ones you need are worth running, since each sidecar is a container that holds credentials.

Starting the stack is two commands:

```bash
docker compose -f docker/docker-compose.yml pull    # fetch the published images from GHCR
docker compose -f docker/docker-compose.yml up -d
```

The compose file pins the :latest tag, and the README explains the alternatives: :nightly is built on every push to main if you want the bleeding edge, and a release tag such as :1.0.0 if you want to pin. Building from source instead of pulling is the same command with --build, since the build sections are kept for contributors.

On first launch the entrypoint generates JWT_SECRET, CRYPTO_ENCRYPTION_KEY and POSTGRES_PASSWORD, and persists them to the picsou_data volume under /data/.secrets/. That is the zero-config claim in practice: you do not have to invent three secrets before the first boot, and the container does it for you.

The database service in the compose file is a plain postgres:16-alpine with a health check, and the backend waits for it:

```yaml
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    env_file: .env
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
```

Then you open http://localhost:8080 and the setup wizard walks you through the admin credentials, CORS and, optionally, Enable Banking.

## Set up HTTPS before the first launch, not after

There is one instruction in the README that is marked IMPORTANT, and it is the most practically useful thing in the document.

Planning to sync bank accounts means setting up HTTPS before opening the wizard, and the reason is a pair of related traps. Enable Banking refuses plain-HTTP callback URLs, so the redirect URI for the PSD2 consent has to be served over TLS. And the wizard permanently stores values derived from the address you open it on. Run the wizard over HTTP and three settings end up derived from the wrong origin, and they then have to be corrected by hand afterwards. Run it over HTTPS and they are all derived correctly with nothing to type.

This is a good example of a failure mode that is invisible until it is expensive. A tool that derives and persists configuration from the URL it was first opened at is convenient, and it means the first launch is irreversible in a small way that the documentation would otherwise let you discover by trial and error.

The bare-metal path has the same shape. The .env.example explains that all values are optional and are only there to override what the setup wizard would otherwise ask, and it gives the init script for a non-Docker run:

```bash
./backend/scripts/picsou-init.sh > .env.local
```

The same file shows the two secret variables with the commands that generate them, openssl rand -base64 48 for JWT_SECRET and openssl rand -base64 32 for CRYPTO_ENCRYPTION_KEY, and the database block with POSTGRES_DB and POSTGRES_USER defaulting to picsou. The comment explains that the password is left blank on purpose so that the init script generates it, and that on bare metal the backend falls back to jdbc:postgresql://localhost:5432/picsou with the picsou credentials if no direct JDBC overrides are set.

Those are exactly the details you need and rarely have: the init script path, the exact secret lengths, and the fallback connection string.

## The sidecars, and why they exist

The sidecar pattern deserves its own look, because it is the most interesting engineering decision in the project and the least documented part of it.

Where PSD2 works, the connection is ordinary: Enable Banking handles the consent, the accounts appear, and no credential is ever handled by Picsou. Where PSD2 does not work, the situation is different. A PEA or CTO brokerage position, an employee savings plan with its FCPE lines, a BoursoBank securities account, all of them are behind a web login with a second factor, and there is no open banking consent for them.

The sidecar pattern is the answer: a small container, with its own image in the registry list, whose only job is to perform the login and the two factor challenge and hand a session to the main application. It is described as local and read-only, which is the property that makes it tolerable, because the credential exists only to read positions.

The cost is real and should be named. A sidecar holds a bank credential, so it is the most sensitive part of the deployment, and there are up to four of them. The compose file shows the backend depending on bourso-auth, bourse-direct-auth, degiro-auth and amundi-auth with a service_started condition, so the sidecars start with the stack whether or not you use them. Their URLs are wired into the backend as BOURSO_AUTH_URL, BOURSE_DIRECT_AUTH_URL and AMUNDI_AUTH_URL, each pointing at port 8001 on the sidecar service name, and they all share the picsou-net network with the backend and the database.

One more detail from the compose file is worth understanding, because it explains an otherwise confusing comment. The backend's environment block only sets the variables that need Docker Compose interpolation, namely the JDBC URL construction, and everything else comes from env_file as-is. The comment says this is because other variables may contain a dollar sign or a newline, which Compose would otherwise try to interpolate.

So the sidecars are a pragmatic bridge: they get you coverage that a pure PSD2 integration cannot, at the price of holding credentials in containers on your network.

## Licence, releases and the security disclaimer

The licence needs a note, because the metadata and the badge disagree in a way that matters. The README badge reads Apache 2.0 with a Commons Clause, while the repository is classified as a custom licence that the hosting platform cannot categorise, with the LICENSE file at the root as the text. The Commons Clause is what makes it not plain Apache 2.0, and it is the clause to read before you use this at work, because it is a source-available licence rather than an OSI-approved one. That is a question for your legal team, not something this article can answer.

The release history is a patch line moving steadily. The recent tags are 1.0.18 on 2026-09-17, 1.0.17 on 2026-09-03 and 1.0.16 on 2026-09-02, and the last push to the repository was on 2026-10-21, with the project homepage at picsou.zoeille.fr. The high patch number on a 1.0 version is the pattern of an application that ships fixes often rather than a library with a wide surface, and the CHANGELOG.md at the root is the upgrade record.

The security disclaimer is the most important paragraph in the README, and it is unusually direct. Picsou is designed for personal, local use. It stores sensitive financial data, including balances, transactions and bank session tokens. It supports multi-member families, optional TOTP two factor and audit logging of setup and administrative actions, but it has not undergone a professional security audit. And the instruction is not to expose it on the public internet, but to use it on a local machine or home network behind a firewall, with the explicit note that exposing it is at your own risk.

That is a real constraint rather than boilerplate, and it interacts with the features rather than sitting beside them. The GDPR export gives a self-service ZIP of JSON plus per-entity CSV, gated by re-authentication and rate limited to five per hour, and the multi-member sharing model with per-resource NONE, ALL and MANUAL access means a compromise reaches more than one person's balances. The audit logging of setup and admin actions is the right mitigation for the setup wizard, and the SECURITY.md file in the repository root is where a disclosure would be handled.

The repository also carries AGENTS.md, CLAUDE.md, CONTRIBUTING.md, a TODO.txt, a.claude directory and a.scratch directory, along with the backend, frontend, services, docker and docs directories, which is a working application repository rather than a packaged library.

## Conclusion

Adopt Picsou if you want a single net worth view across French bank accounts, PEA and CTO brokerage, employee savings plans, crypto and property, and you are prepared to run sidecar services that hold your bank credentials. Do not adopt it for a shared or public deployment, because the project states plainly that it has not undergone a professional security audit and that it should stay on a local machine or home network behind a firewall. Verify first that the Enable Banking callback will be served over HTTPS, because the platform refuses plain HTTP callbacks and the wizard permanently stores values derived from the address you open it on, that the licence file matches what you expect given the badge says Apache 2.0 with a Commons Clause, and that the sidecar you need is published for your institution in the ghcr.io/cloeille/picsou-finance image list.

## FAQ

### What is Picsou?

Picsou is a self-hosted personal finance dashboard that tracks bank accounts, brokerage accounts, crypto wallets, debts and property in one place, with historical net worth snapshots, savings goals and a multi-member family model. It is a Spring Boot and React application on PostgreSQL.

### How do I install Picsou with Docker?

Clone the repository, then run docker compose -f docker/docker-compose.yml pull followed by docker compose -f docker/docker-compose.yml up -d, which fetches the published multi-arch images from the GitHub Container Registry. Add --build to build from source instead. The entrypoint generates JWT_SECRET, CRYPTO_ENCRYPTION_KEY and POSTGRES_PASSWORD on first launch.

### Why does Picsou need sidecar containers for some banks?

Enable Banking covers most EU accounts over PSD2 and OAuth, but PSD2 cannot reach a securities account such as the BoursoBank PEA or CTO, and positions at brokers or employee savings plans. Picsou reaches those through local read-only login sidecars, published as separate images such as ghcr.io/cloeille/picsou-finance/bourso-auth and /tr-auth.

### Do I need HTTPS before using Picsou?

Yes, if you plan to sync bank accounts. Enable Banking refuses plain-HTTP callback URLs, and the setup wizard permanently stores values derived from the address you open it on, so running it over HTTP leaves three settings that have to be corrected by hand afterwards.

### What licence does Picsou use?

The README badge states Apache 2.0 with a Commons Clause, while the repository is classified as a custom licence that the hosting platform cannot categorise, with the LICENSE file at the root as the authoritative text. The Commons Clause is why the badge is not plain Apache 2.0.

## Sources

- [Cloeille/picsou-finance on GitHub](https://github.com/Cloeille/picsou-finance)
- [Issues](https://github.com/Cloeille/picsou-finance/issues)
- [README](https://github.com/Cloeille/picsou-finance/blob/main/README.md)
- [Releases](https://github.com/Cloeille/picsou-finance/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cloeille-picsou-finance
