---
title: Contributing
section: Install and operate
order: 45
summary: What kinds of help are most useful, how to set up for development, and what to run before opening a pull request.
---

Balsa is early, and help is welcome, especially anything that makes the
numbers more correct, easier to trace, more secure, or more useful. The code,
issues, and pull requests are at
[github.com/tdavidson/balsa](https://github.com/tdavidson/balsa).
If this page and `CONTRIBUTING.md` in the repo ever disagree, go with
`CONTRIBUTING.md`.

## Before you start

- Check existing issues before opening a new one.
- For big changes to the product, data model, or accounting, open an issue first
  so we can talk it through.
- Don't put real company data, credentials, personal information, or unredacted
  financial documents in issues, fixtures, tests, or pull requests.
- Report security problems privately, as described in `SECURITY.md`, not in a
  public issue.

## Local development

You'll need Node.js 22+, pnpm 10+, Docker, and an OpenAI or OpenRouter API key.

```bash
pnpm run setup
pnpm run doctor
pnpm dev
```

`AUTH_MODE=disabled` is fine for local development. Just don't expose that
instance to a network.

## Making a change

1. Fork the repo and work on a focused branch.
2. Add or update tests for what you changed, including regressions.
3. If you're fixing a real accounting error, add a synthetic or fully redacted
   fixture that would have caught it.
4. Update the docs if behavior or configuration changes.

## Before opening a pull request

```bash
pnpm run verify
pnpm lint
```

Also run `pnpm acceptance` if you touched ingestion, financial calculations,
database behavior, or the Company Zero workflow.

`pnpm test` includes database tests. They run when `DATABASE_URL` or
`TEST_DATABASE_URL` points at a migrated PostgreSQL and skip otherwise. Run
`pnpm db:migrate` first to include the company repository and row level
security tests.

Run `pnpm isolation` if you changed company switching, company-scoped routes,
provisioning or roles, the audit log, or row level security. It tests a running
app from the outside rather than unit-testing code, so start the app first, set
`BASE_URL` to it, and choose a mode with `ISOLATION_AUTH_MODE=disabled` or
`ISOLATION_AUTH_MODE=better-auth`. Both modes need to pass. Use an isolated synthetic database and application;
the script creates and deletes companies and is not safe as a customer-data fixture. In Better Auth mode
it also checks that registering without the sign-up code fails, and that a
second user sees the existing companies instead of getting a new one. For that,
run the app with `AUTH_ALLOW_SIGN_UP=true` and pass its `AUTH_SIGN_UP_CODE` as
`ISOLATION_SIGN_UP_CODE`.

## In the pull request

Describe the problem, what happened before and after your change, how you
tested it, and anything it affects in the schema, security, migrations,
deployment, or accounting. Also say whether the model suggests the result or
code decides and calculates it. That line matters a lot in this project.

## Licensing

Balsa is licensed under the [Apache License 2.0](/license/). Under Section
5, anything you submit for inclusion is covered by the same license unless you
say otherwise. There's no separate contributor agreement to sign.

For private questions, email Taylor Davidson at hello@hemrock.com.


## Documentation contributions

The website lives in `balsa-site`, separately from the application. Public
guides are Markdown in `content/docs`; private plans in `internal/` are not
published. Keep claims tied to implemented behavior and distinguish accounting
foundation features from narrower planning-workflow support.

In the site repository, run `pnpm check:docs`, `pnpm typecheck`, `pnpm lint`,
`pnpm build`, and `pnpm check:export`. The build regenerates raw Markdown and
`llms.txt`; include those generated public files with source documentation changes.
