How to contribute
I'm really glad you're reading this, because we need volunteer developers to help this project come to fruition.
If you haven't already, you can write to us (#ivory on postgres slack). We want you working on things you're excited about.
Here are some important resources:
- Enhancements
- Good for newcomers
- Supported versions
- Setup Local Environment
- Build Frontend
- Build Backend
Coding conventions
Ivory has a strict coding convention, to prevent disputes in PRs and keep the code readable. The complete, up-to-date rulebook is AGENTS.md — architecture, naming, file layout, model and service rules, plugin contracts, backup compatibility and testing. It is the single source of truth: this file only summarises it, and where the two disagree, AGENTS.md wins.
It is long on purpose, and it is written to be read by AI coding assistants as much as by people. Claude Code
(through CLAUDE.md, a link to it), Codex, Cursor and most other agents load it automatically. If you use
one, ask it to follow AGENTS.md and to check your change against it before you open a PR — that is the
fastest way to get the conventions right. Reviewers will hold the change to it either way.
The rules that come up most often:
- Structure — Go backend in
server/(features/business logic,clients/integrations,plugins/keeper/database/platform adapters,storage/persistence,core/wiring); React frontend inapp/(features/domain logic,core/pages and widgets,shared/reusable components, hooks and providers). - Go file names are prefixed with their package:
node/node_service_keeper.go,cluster_router_http.go. Domain types and mappers live in<feature>_model.go; split large services by concern as<feature>_service_<domain>.go. - Frontend file names are PascalCase,
.tsand.tsxalike; a feature's types live infeatures/<name>/api/<Name>Type.ts, owned by the feature they belong to. - Components are functions with a fixed member order:
returnJSX, thenrender***,handle***,handleEffect***/handleMemo***,get***. Styles go in one constant namedSXabove the component — never inlinesx={{...}}, never shared between components.Propsis atypeabove the component. - TypeScript uses
undefined, nevernull. - Comments only where the why is not obvious; no multi-line blocks.
- Backups are sacred: a released backup version's shape never changes, a change means a new version.
Testing
Every change should come with tests and pass the same checks CI runs:
- Backend —
make -C server test. Table-driven Go tests witht.Run()subtests, in a_test.gofile that mirrors the file it tests. Services and repositories are tested; routers are not. - Frontend —
cd app && npm run lint && npm run build && npm test. Rstest + Testing Library, colocated as[filename].test.[ext]. Test helpers, hooks and providers; skip presentational components.
Never delete an existing test during a refactor — update it to the new shape. For anything touching deployment or a plugin, also try it by hand against the local environment.
Submitting changes
Please send a GitHub Pull Request to Ivory with a clear list of what you've done (read more about pull requests). Describe behaviour changes, link the related issue, and add screenshots or GIFs for UI work.
Always write a clear log message for your commits. Short imperative subjects (add tooltips for refresh buttons) are fine for small changes, but bigger changes should look like this:
All new proposals about changing the coding convention should be discussed among contributors, and land as an edit to AGENTS.md.
Thanks, Andrei Sergeev
