Thank you for your interest in contributing. This guide covers everything you need to go from zero to a merged pull request.
- Code of Conduct
- Reporting Bugs
- Requesting Features
- Getting Started
- Branching Strategy
- Commit Conventions
- Code Style and Linting
- Running Tests
- Pull Request Process
- Branch Protection Rules
- Docker Base Image Pinning
We are committed to a welcoming and inclusive community. All contributors are expected to:
- Use respectful and inclusive language
- Accept constructive criticism gracefully
- Focus on what is best for the project and its users
- Show empathy toward other community members
Unacceptable behavior includes harassment, discrimination, intimidation, offensive comments related to personal characteristics, and unwelcome sexual attention. Report violations to the maintainers by opening a private issue or emailing the project contact. All reports will be reviewed and investigated promptly.
Use the Bug Report issue template on GitHub:
- Go to Issues → New Issue
- Select Bug Report
- Fill in the description, steps to reproduce, expected vs. actual behavior, and your environment (OS, Node.js version, Docker version, etc.)
- Attach relevant logs or screenshots
Before opening a new bug report, search existing issues to avoid duplicates. If you find an open issue that matches, add a comment with any additional context rather than opening a new one.
Use the Feature Request issue template on GitHub:
- Go to Issues → New Issue
- Select Feature Request
- Describe the problem you are trying to solve, your proposed solution, and any alternatives you considered
- Fill in the acceptance criteria so reviewers know when the feature is complete
| Tool | Minimum version |
|---|---|
| Node.js | 18+ |
| Python | 3.11+ |
| Rust | stable (latest) |
wasm32-unknown-unknown target |
— |
| Soroban CLI | latest |
| Docker + Docker Compose | — |
| Freighter Wallet | — (for manual testing) |
External contributors must work from a fork. Do not push branches directly to the upstream repository.
# 1. Fork the repository on GitHub (click "Fork" in the top-right corner)
# 2. Clone your fork
git clone https://github.com/<your-username>/VacciChain.git
cd VacciChain
# 3. Add the upstream remote so you can pull future changes
git remote add upstream https://github.com/dev-fatima-24/VacciChain.git
# 4. Verify your remotes
git remote -v
# origin https://github.com/<your-username>/VacciChain.git (fetch)
# origin https://github.com/<your-username>/VacciChain.git (push)
# upstream https://github.com/dev-fatima-24/VacciChain.git (fetch)
# upstream https://github.com/dev-fatima-24/VacciChain.git (push)To keep your fork up to date before starting new work:
git fetch upstream
git checkout main
git merge upstream/main
git push origin main# Copy and fill in environment variables
cp .env.example .env
# Edit .env — at minimum set STELLAR_NETWORK, SOROBAN_RPC_URL,
# VACCINATIONS_CONTRACT_ID, ADMIN_SECRET_KEY, SEP10_SERVER_KEY,
# ISSUER_SECRET_KEY, and JWT_SECRET
# Install backend dependencies
cd backend && npm install && cd ..
# Install frontend dependencies
cd frontend && npm install && cd ..
# Install Python service dependencies
cd python-service && pip install -r requirements.txt && cd ..
# (Optional) Deploy the smart contract to testnet
cd contracts && make build && make deploy && cd ..Run the full stack with Docker Compose:
docker compose up --build
# frontend → http://localhost:3000
# backend → http://localhost:4000
# analytics → http://localhost:8001Or run each service individually:
# Terminal 1 — backend
cd backend && npm run dev
# Terminal 2 — frontend
cd frontend && npm run dev
# Terminal 3 — Python analytics
cd python-service && uvicorn main:app --port 8001VacciChain uses Gitleaks to prevent accidental commits of Stellar secret keys, JWT secrets, and other credentials. Install the pre-commit hook before making any commits:
# Linux / macOS
./scripts/setup-git-hooks.sh
# Windows
.\scripts\setup-git-hooks.ps1The hook runs automatically on every git commit. If it fires, remove the detected secret, rotate it if it was ever pushed, and commit again. See docs/secret-scanning-setup.md for full details.
Never commit real Stellar secret keys, JWT secrets, or API keys. Use
.env(which is gitignored) for all secrets. The.env.examplefile must only contain placeholder values.
All work happens on feature branches. The main branch is protected — direct pushes are blocked.
| Pattern | When to use |
|---|---|
feature/<short-description> |
New features |
fix/<short-description> |
Bug fixes |
docs/<short-description> |
Documentation-only changes |
chore/<short-description> |
Tooling, dependencies, CI |
issues/<issue-numbers> |
When addressing one or more GitHub issues |
Examples: feature/batch-verify-endpoint, fix/sep10-nonce-expiry, issues/77-79-82
Always branch from an up-to-date main:
git fetch upstream # or: git fetch origin (if you are a maintainer)
git checkout main
git merge upstream/main
git checkout -b feature/my-featureWe follow Conventional Commits. Every commit message must match this format:
<type>(<scope>): <subject>
[optional body]
[optional footer — e.g. Closes #42]
| Type | Use for |
|---|---|
feat |
A new feature |
fix |
A bug fix |
docs |
Documentation changes only |
style |
Formatting, whitespace — no logic change |
refactor |
Code restructuring without behavior change |
perf |
Performance improvements |
test |
Adding or updating tests |
chore |
Build, dependency, or tooling changes |
Use the affected area: backend, frontend, contracts, python-service, auth, vaccination, verify, analytics, or an issue number like #77.
git commit -m "feat(backend): add OpenAPI spec generation"
git commit -m "fix(#77): resolve swagger endpoint routing"
git commit -m "docs: add contributor onboarding guide"
git commit -m "test(contracts): add mint_vaccination edge-case tests"
git commit -m "chore: pin Node.js base image digest"Keep the subject line under 72 characters. Use the body to explain why, not what — the diff shows what changed.
- Follow the existing code style in each file — indentation, quote style, and semicolons must be consistent with the surrounding code.
- Use
constandlet; nevervar. - Prefer
async/awaitover raw Promise chains. - All route handlers must use the shared
validatemiddleware (Zod schemas) for input validation. - Do not introduce new dependencies without discussion in an issue first.
If a linter is configured in the project (npm run lint), run it before committing:
cd backend && npm run lint
cd frontend && npm run lintFix all errors and warnings before opening a PR. Do not disable lint rules inline without a comment explaining why.
- Run
cargo fmtbefore committing:cd contracts && cargo fmt
- Run
cargo clippyand resolve all warnings:cd contracts && cargo clippy -- -D warnings
- Follow PEP 8.
- Run
blackandflake8if they are available in the project:cd python-service black . flake8 .
- Do not commit commented-out code.
- Remove debug
console.log/printstatements before opening a PR. - Keep functions small and focused. If a function is hard to test, it is probably doing too much.
- Add JSDoc / docstring comments to public functions and non-obvious logic.
Run the full test suite for every service you touched before opening a PR. All checks must pass.
cd backend
npm test # run Jest suite (exits after one pass)
npm run test:coverage # run with coverage — must meet 70% lines/functions/branchesThe coverage gate is enforced in CI. If your changes drop coverage below 70% in any category, the build will fail. Add tests for any new code you introduce.
cd contracts
cargo testcd python-service
pytestdocker compose -f docker-compose.e2e.yml up --build --abort-on-container-exitE2E tests run against the full Docker stack. They are also run automatically in CI on every PR.
- Sync your branch with the latest
mainto avoid merge conflicts:git fetch upstream git rebase upstream/main
- Run all tests for the services you changed (see Running Tests).
- Run linters and formatters (see Code Style and Linting).
- Update documentation if your changes affect user-facing behavior, environment variables, or API contracts.
- Ensure no secrets or
.envvalues are included in your commits.
- Push your branch to your fork:
git push -u origin feature/my-feature
- Open a pull request against
mainon the upstream repository. - The PR template will be pre-populated — fill in every section:
- Description — what changed and why
- Type of change — bug fix, feature, breaking change, docs
- Changes made — bullet list of the key changes
- Testing — how you tested the changes and what the results were
- Checklist — check every box that applies
- Link related issues in the description:
Closes #42orRelates to #77, #79.
- At least one maintainer approval is required before merging.
- All CI checks (tests, linting, security scans) must pass.
- If a reviewer requests changes, push updates to the same branch — do not open a new PR.
- Respond to all review comments, either by making the change or explaining why you disagree.
- Once approved, a maintainer will merge using:
- Squash and merge — for single-purpose changes where one clean commit is appropriate
- Create a merge commit — for multi-commit PRs with logically distinct commits
The main branch is protected with the following rules:
| Rule | Setting |
|---|---|
Direct pushes to main |
Blocked — all changes must go through a PR |
| Required approving reviews | 1 — at least one maintainer must approve |
| Required status checks | All CI jobs must pass (contract, backend, frontend, python, all-tests) |
| Stale review dismissal | Enabled — new commits dismiss existing approvals |
| Branch deletable | No — main cannot be deleted or force-pushed |
All FROM statements use pinned SHA256 digests to ensure reproducible, auditable builds:
FROM node:18.18.2-alpine@sha256:18a70ffe45b8a3db9e3e8dd85a92d7beab70d395e0d529ada0d9de0319c8b4d7Rules:
- Never use untagged images (
FROM pythonis not allowed). - Always include both a version tag and a digest.
- Use specific version tags (
node:18.18.2-alpine) rather than floating tags (node:18-alpine).
Updating a base image:
docker pull node:20-alpine
docker inspect node:20-alpine --format '{{index .RepoDigests 0}}'
# Copy the sha256 digest and update the Dockerfile FROM lineRenovate runs daily and opens PRs for digest updates automatically. Digest-only updates are auto-merged; version bumps require manual review.
If something in this guide is unclear, open an issue with the question label or start a discussion in the repository. We are happy to help.