Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/soker90/finper/llms.txt

Use this file to discover all available pages before exploring further.

Finper is a self-hosted application designed for personal use. There is no admin panel or user directory UI — user accounts are managed via a CLI seed script or the REST API. By default, open registration is disabled; only the seed script can create users unless you explicitly enable the registration endpoint.

Creating the first user

The simplest way to bootstrap a user from the project root:
make seed-user USERNAME=admin PASSWORD=mypassword
The Makefile translates USERNAME and PASSWORD into the INIT_USERNAME and INIT_PASSWORD environment variables expected by the seed script (packages/api/src/scripts/seed-user.ts). The script:
  1. Validates the credentials against the length rules.
  2. Applies any pending Drizzle migrations to the SQLite database.
  3. Creates the user if the username does not already exist.
  4. Exits silently without error if the user already exists (idempotent).

Validation rules

Both the seed script and the registration endpoint enforce the same constraints:
FieldRule
username3–15 characters. Automatically lowercased and trimmed.
passwordMinimum 5 characters.
If validation fails, the seed script prints an error message and exits with code 1. The REST API returns a 422 Unprocessable Entity (Boom badData) with the Joi error message.

Registration control

The ALLOW_REGISTRATION environment variable controls whether POST /api/auth/register is reachable:
ValueBehaviour
false (default)The endpoint returns 403 Forbidden. No new users can be created via the API.
trueThe endpoint is open. Any caller with network access can register a new account.
Set this in .env:
ALLOW_REGISTRATION=true
Enable ALLOW_REGISTRATION only temporarily when you need to add an account, then set it back to false. Leaving it enabled exposes the registration endpoint to anyone who can reach your API.

Multi-tenancy

Every data record in Finper carries a user field containing the username string of its owner. All service-layer queries automatically filter by the authenticated user, so:
  • A user can only read and modify their own accounts, transactions, debts, goals, and every other record.
  • There is no cross-user data access and no admin role.
  • There is no shared data between users.
The user field in the database is the username (a plain string), not a foreign-key ID — this is the req.user value set by auth.middleware.ts after JWT verification.

Changing passwords

There is no dedicated password-change API endpoint. To update a user’s password, re-run the seed script with the same username and a new password:
make seed-user USERNAME=admin PASSWORD=newstrongpassword
Because the seed script checks for the UNIQUE constraint on username, running it with an existing username does not create a duplicate — it will report that the user already exists. To actually change the password you need to use a direct database approach or implement a custom migration.
The seed script is idempotent for the creation path — it detects the UNIQUE constraint failed SQLite error and exits cleanly with an info message rather than crashing or creating a duplicate record.

Logging in

Once a user exists, obtain a JWT token by calling the login endpoint:
curl -X POST http://localhost:3008/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "admin",
    "password": "mypassword"
  }'
Response:
{ "token": "<jwt>" }
Pass the token in the token header for all subsequent authenticated requests:
curl http://localhost:3008/api/accounts \
  -H 'token: <your-jwt>'
JWT tokens expire after 1 hour. Re-authenticate to obtain a new token.

Build docs developers (and LLMs) love