Welcome. This guide takes you from a fresh clone to a passing CI run and an open PR. Follow it top-to-bottom on your first setup; after that, jump to whichever section you need.
- Prerequisites
- Clone and bootstrap
- Contract development (Rust / Soroban)
- Backend development (NestJS)
- Frontend development (Next.js)
- CI quality gates
- PR review process
- Good first issues
- Security rules
- Dependency update policy
Install these before anything else.
| Tool | Version | Install |
|---|---|---|
| Node.js | >=22 (see .nvmrc) |
nvm or fnm |
| npm | >=10 |
bundled with Node |
| Rust | stable | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh |
| wasm32 target | — | rustup target add wasm32-unknown-unknown |
| Soroban CLI | latest | cargo install --locked stellar-cli --features opt |
| Docker | — | Docker Desktop |
| PostgreSQL | 16 | via Docker (see below) or local install |
| Redis | 7 | via Docker (see below) or local install |
Node version: run
nvm usein the repo root to switch to the pinned version automatically.
git clone https://github.com/InsurNiffy/niff-Stellar-shurance.git
cd niff-Stellar-shuranceStart Postgres and Redis with Docker:
cd backend
docker compose up -dValidate your local env files before starting anything:
make check-envThis checks backend/.env and frontend/.env.local for all required variables and prints a clear list of anything missing.
The Soroban smart contract lives in contracts/niffyinsure/.
# Rust stable + wasm target (if not done in step 1)
rustup update stable
rustup target add wasm32-unknown-unknown
# Soroban CLI
cargo install --locked stellar-cli --features optcargo test --workspace --features testutilsmake wasm-release
# Output: artifacts/niffyinsure-<version>-<git-tag>.wasm
# artifacts/niffyinsure-<version>-<git-tag>.wasm.sha256make fmt # cargo fmt --check
make lint # cargo clippy -D warningsbackend/src/soroban/golden-vectors.json records the exact ScVal encoding for every critical contract call. CI fails if the vectors drift.
Refresh after changing any contract function signature, argument builder, or enum variant:
cd backend
npm run refresh-vectors
git diff backend/src/soroban/golden-vectors.json # review carefullyIf the contract ABI changed, bump _meta.contractSemver in the JSON to match the new contract version. Commit the updated file — a second engineer must review any vector changes before merge.
| Failure | Fix |
|---|---|
cargo test fails |
Run cargo test --workspace --features testutils locally and fix the failing test |
cargo audit fails |
Run cargo audit and update or patch the flagged dependency |
| WASM build fails | Run make build and resolve the compiler error |
When you add a new #[contractevent] struct to events.rs:
- Add the new event's
EventKeytobackend/src/events/events.schema.ts(EventKeyunion +EVENT_PARSERSentry + typed interface). - Add a decode test in
backend/src/events/events.test.tsthat constructs the raw payload and assertsparseEventreturns the correct typed result. - Update
docs/EVENT_DICTIONARY.mdwith the new event's topic layout and payload schema. - If any field is removed or its type changes, bump
SCHEMA_VERSIONinevents.schema.tsand add a new versioned parser entry — backward-compatible additions do not require a bump.
The backend lives in backend/. It is a NestJS API backed by PostgreSQL (Prisma) and Redis.
cd backend
# Copy and fill in env vars
cp .env.example .env
# Edit .env — at minimum set DATABASE_URL, REDIS_URL, JWT_SECRET, ADMIN_TOKEN,
# FRONTEND_ORIGINS, CAPTCHA_SECRET_KEY, IP_HASH_SALT.
# See backend/docs/environment-variables.md for the full reference.
npm ci
npx prisma generate
npx prisma migrate dev # applies all migrations to your local DBnpm run start:dev
# API available at http://localhost:3000
# Swagger UI at http://localhost:3000/apinpm testThe E2E suite spins up real Postgres and Redis containers via Testcontainers — no .env needed for this.
npm run test:e2eIf a test hangs, check Docker is running:
docker info.
npm run env:example:generate # regenerate .env.example from env.definitions.ts
npm run env:example:check # verify .env.example is not drifted
npm run export-spec # regenerate backend/openapi.json from DTOs
npm run error-catalog:check # verify error codes are consistentIf you modify a backend DTO (request/response body), the OpenAPI spec must be regenerated:
cd backend
npm run export-spec
git add openapi.jsonCI will fail if the spec drifts from the committed version. Always regenerate and commit the updated file when DTOs change.
| Failure | Fix |
|---|---|
npm test fails |
Run npm test locally and fix the failing test |
.env.example drift |
Run npm run env:example:generate and commit the updated file |
| OpenAPI spec drift | Run npm run export-spec and commit the updated backend/openapi.json |
npm audit high/critical |
Update or patch the flagged dependency |
| Failure | Fix |
|---|---|
| Denormalized addresses detected | Run cd backend && npx ts-node -r tsconfig-paths/register src/scripts/normalize-addresses.ts to normalize all addresses in the database, then verify the fix with --dry-run |
| Failure | Fix |
|---|---|
| Pending migrations detected | Run npx prisma migrate dev locally, commit the new migration file |
| Migration lock file invalid | Do not edit migration_lock.toml manually — it is managed by Prisma |
The frontend lives in frontend/. It is a Next.js 16 app using the App Router.
cd frontend
# Copy and fill in env vars
cp .env.example .env.local
# At minimum set NEXT_PUBLIC_API_URL.
# See frontend/.env.example for all variables and their owners.
npm cinpm run dev
# App available at http://localhost:3001npm testnpx playwright install --with-deps # first time only
npm run test:e2enpm run storybook
# Component explorer at http://localhost:6006npm run build
npx playwright test tests/accessibility.spec.tsRun this after the backend openapi.json changes:
make generate-client
# Commits: frontend/src/lib/api/generated/openapi.d.ts| Failure | Fix |
|---|---|
npm run lint fails |
Run npm run lint -- --max-warnings=0 locally and fix all warnings |
npm run typecheck fails |
Run npm run typecheck locally and fix type errors |
npm run build fails |
Run npm run build locally — often a missing env var or type error |
npm test fails |
Run npm test locally and fix the failing test |
| Generated types stale | Run make generate-client and commit the updated .d.ts file |
| Failure | Fix |
|---|---|
| axe violations | Run npx playwright test tests/accessibility.spec.ts locally, open the HTML report, and fix the flagged violations before opening a PR |
The E2E job is continue-on-error: true — failures are reported but do not block merge. Check the uploaded Playwright report artifact for details.
Every PR to main runs these jobs. All must pass (except e2e-tests which is advisory).
| Job | What it checks | Key commands to run locally |
|---|---|---|
frontend |
lint, typecheck, build, unit tests, generated types | cd frontend && npm run lint -- --max-warnings=0 && npm run typecheck && npm run build && npm test |
contract |
Rust tests, cargo audit, WASM build | cargo test --workspace --features testutils && cargo audit && make build |
unit-tests |
Backend unit tests, .env.example drift, OpenAPI spec drift |
cd backend && npm test && npm run env:example:check && npm run export-spec |
address-normalization |
Tracked address data must be canonical (no denormalized M-addresses) | cd backend && npx ts-node -r tsconfig-paths/register src/scripts/normalize-addresses.ts --dry-run |
golden-vectors |
Soroban ABI encoding (runs on contract/backend changes) | cd backend && npm run refresh-vectors |
migrations |
Prisma migration history and schema validity | cd backend && npx prisma migrate deploy |
accessibility |
axe/Playwright — no critical violations | cd frontend && npx playwright test tests/accessibility.spec.ts |
e2e-tests |
Playwright E2E (advisory, does not block merge) | cd frontend && npm run test:e2e |
Run this checklist locally to avoid a CI round-trip:
# Contract
cargo fmt --all -- --check
cargo clippy --target wasm32-unknown-unknown --release -- -D warnings
cargo test --workspace --features testutils
# Backend
cd backend
npm run env:example:check
npm run export-spec
npx ts-node -r tsconfig-paths/register src/scripts/normalize-addresses.ts --dry-run
npm test
# Frontend
cd frontend
npm run lint -- --max-warnings=0
npm run typecheck
npm run build
npm test- Branch off
main:git checkout -b feat/<short-description>orfix/<short-description>. - Keep PRs focused — one logical change per PR.
- Fill in the PR description: what changed, why, and how to test it.
- Link the related issue if one exists.
- All CI jobs must be green before requesting review (except the advisory
e2e-testsjob).
- At least one approving review from a team member before merge.
- Two approving reviews required for any change to:
backend/src/soroban/golden-vectors.jsoncontracts/(any contract source or ABI)backend/prisma/schema.prismaor migration files.github/workflows/
- The author merges after approval — do not merge someone else's PR without their acknowledgement.
Squash-merge into main. Write a clear squash commit message following Conventional Commits:
feat(claims): add idempotency key support to claim submission
fix(auth): prevent nonce reuse after wallet disconnect
chore(deps): bump stellar-sdk to 14.6.1
docs(contributing): add contract setup section
- Delete the feature branch.
- If
openapi.jsonorgolden-vectors.jsonchanged, notify the relevant team so downstream consumers can update. - If a contract was deployed, update
contracts/deployment-registry.jsonand open a follow-up PR.
Look for issues labelled good first issue on GitHub. These are scoped to be completable without deep knowledge of the full system.
- Self-contained change in one area (frontend, backend, or contract docs)
- Clear acceptance criteria
- No dependency on unreleased contract changes
- Estimated at under half a day
| Area | Examples |
|---|---|
| Frontend | Fix a lint warning, add a missing aria-label, improve an error message |
| Backend | Add a missing field to an API response DTO, improve a log message, fix a typo in an error code |
| Docs | Clarify a setup step, add a missing env var description, fix a broken link |
| Contract | Add a #[doc] comment to a public function, fix a clippy warning |
- Comment on the issue to claim it — avoids duplicate work.
- Ask questions in the issue thread before writing code.
- Open a draft PR early so reviewers can give early feedback.
If your issue is one part of a feature that spans contract, backend, and/or
frontend, follow the
cross-stack issue linking convention
to keep the related issues discoverable.
4. Reference the issue in your PR description: Closes #<issue-number>.
- Never commit real private keys. Stellar secret keys start with
S. CI scans for this pattern and will fail. - Use only placeholder G-addresses and C-addresses in test fixtures and golden vectors.
- Rotate all secrets (
JWT_SECRET,ADMIN_TOKEN,IP_HASH_SALT,CAPTCHA_SECRET_KEY) before any production deploy — the backend will refuse to start with the example placeholder values. - Store production secrets in a secrets manager (HashiCorp Vault, AWS SSM, Kubernetes Secrets) — never in
.envfiles committed to the repo. Cargo.lockandpackage-lock.jsonare committed. Do not modify them without a deliberate dependency update PR.
Every PR that touches UI must pass the checks in the accessibility CI job. See the Accessibility Testing section below for the full checklist.
Accessibility is a first-class requirement. Every PR that touches UI must pass the checks below before merge.
The accessibility CI job runs @axe-core/playwright against the quote, policy, claims, and vote routes. No critical violations are permitted. The job uploads a Playwright report as an artifact on failure.
Run locally:
cd frontend
npm run build
npx playwright test tests/accessibility.spec.ts- Install the axe DevTools browser extension.
- Open each targeted route:
/quote,/policy,/claims,/claims/<id>. - Run the full-page scan. Resolve any critical or serious violations before opening a PR.
Verify these flows using only the keyboard (no mouse):
| Flow | Steps |
|---|---|
| Get a quote | Tab through all form fields → submit → confirm quote preview updates |
| Purchase policy | Complete all 4 wizard steps using Tab / Shift+Tab / Enter / Space |
| File a claim | Complete all 4 wizard steps; confirm focus moves to new step heading on advance |
| Cast a vote | Tab to Approve / Reject buttons → Enter to open confirm modal → Tab within modal → confirm or cancel |
| Connect wallet | Tab to "Connect Wallet" button → Enter → confirm status announced |
Focus must always be visible. After a modal opens, focus must move inside it. After a modal closes, focus must return to the trigger.
Test at minimum one major flow per release with a screen reader:
- macOS / iOS: VoiceOver (
Cmd+F5to toggle) - Windows: NVDA (free) or Narrator
- Android: TalkBack
Checklist:
- Transaction status updates are announced (aria-live regions on wizard and policy pages)
- Step changes in wizards are announced (focus moves to hidden
<h2>with step name) - Quote preview updates are announced on the quote page
- Vote tally countdown is announced via
aria-live="polite" - Modal title is read when dialog opens
- Icon-only buttons have accessible names (aria-label or sr-only text)
- Claim status badges convey outcome via text/shape, not color alone
Verify that setting prefers-reduced-motion: reduce stops all non-essential animations. Loading spinners should become static; slide/fade transitions should be instant.
Each page must have exactly one <h1>. Use the browser Accessibility Tree panel or the HeadingsMap extension to verify a logical heading order with no skipped levels.
Every page must have at minimum: <main>, <nav> (if navigation present), and <footer> (if present).
All text must meet WCAG AA contrast ratios (4.5:1 normal text, 3:1 large text). Claim outcomes (Approved / Rejected / Pending) must not rely on color alone — shape indicators and text labels are required.
When adding new interactive components:
- Icon-only controls must have
aria-labelor a visually hidden label. - Async state changes (transactions, loading) must update an
aria-liveregion. - Multi-step wizards must move focus to a step heading on step change.
- Modals must trap focus and return it to the trigger on close (Radix Dialog handles this automatically).
- Animations must respect
prefers-reduced-motionvia the global CSS rule inglobals.css.
See section 3 for the refresh workflow. Additional rules:
- A second engineer must review and approve any vector changes before merge.
- Before tagging a release: run
npm run refresh-vectorsand confirm the diff is empty (or intentional), confirm_meta.contractSemvermatchesCargo.toml, and updatecontracts/deployment-registry.jsonwith the new wasm hash.
Dependabot is configured to open automated PRs on a weekly schedule (every Monday). It targets the following ecosystems:
| Ecosystem | Config location | Update scope |
|---|---|---|
| npm (backend) | backend/package.json |
patch and minor |
| npm (frontend) | frontend/package.json |
patch and minor |
| GitHub Actions | .github/workflows/ |
patch and minor |
| Cargo (contracts / backend Rust) | Cargo.toml |
patch and minor |
Dependabot PRs that touch only patch or minor versions of non-contract dependencies are reviewed and merged by any team member without a formal review gate, provided all CI jobs pass.
Major version bumps (X.0.0) require:
- A dedicated PR titled
chore(deps): bump <package> from vN to vN+1. - Manual review of the package's migration guide and changelog.
- At least one approving review from a team member with ownership of the affected area (backend, frontend, or contracts).
- Confirmation that all CI jobs pass — especially the
golden-vectorsjob if the bump touches any Soroban SDK.
Do not batch multiple major bumps into a single PR; each major version upgrade must be independently reviewable and revertable.
The Soroban contract SDK and CLI are pinned to exact versions to prevent mid-sprint breakage from upstream changes to the WASM ABI or XDR encoding:
stellar-sdk(npm): pinned to an exact version infrontend/package.jsonandbackend/package.json(no^or~prefix). Update only via a deliberate PR that also refreshes the golden vectors (npm run refresh-vectors) and updates_meta.contractSemverinbackend/src/soroban/golden-vectors.json.stellar-cli(Cargo / CI): pinned viacargo install --locked stellar-cli --features opt. The locked version is recorded inCargo.lock; do not runcargo updateon this crate without a corresponding golden-vector refresh.- Rust toolchain:
rust-toolchain.toml(orrustup override) pins the channel tostableat a specific date. Update together with any contract SDK upgrade.
Any PR that changes a pinned SDK version must include a golden-vector diff review (two approvals required — see PR review process).
npm audit and cargo audit run in CI. If a high or critical vulnerability is reported:
- Open a fix PR immediately, even outside the weekly window.
- If a non-breaking patch is available, merge it the same day.
- If the fix requires a major bump or a workaround, open a tracking issue and document the interim mitigation in
audit.toml(Cargo) or vianpm audit fix --forcewith a justification comment in the PR description.
Never merge an npm audit or cargo audit suppression without a documented reason and an expiry date in the suppression entry.